Основы манипулирования массивами NumPy

NumPyBeginner
Практиковаться сейчас

Введение

В этой лабораторной работе вы изучите фундаментальные концепции манипулирования массивами NumPy, уделяя особое внимание различию между копиями (copies) и представлениями (views). Понимание этой разницы имеет решающее значение для написания эффективного и безошибочного числового кода на Python. Вы будете писать и выполнять скрипты Python, чтобы увидеть эти концепции в действии.

Разница между копией и представлением

В NumPy копия (copy) — это совершенно новый массив с собственными данными, тогда как представление (view) — это новый способ просмотра тех же данных. Изменение представления повлияет на исходный массив, а изменение копии — нет.

Давайте посмотрим на это на практике. Мы создадим массив, затем сделаем его представление и копию. После этого мы изменим оба и понаблюдаем за эффектом на исходном массиве.

Сначала откройте файл main.py в файловом менеджере слева. Затем замените его содержимое следующим кодом:

import numpy as np

## --- Часть 1: Изменение представления ---
print("--- Modifying a View ---")
## Создаем исходный массив
original_array_view = np.array([1, 2, 3, 4, 5])
print(f"Original array: {original_array_view}")

## Создаем представление массива
view_array = original_array_view.view()
## Изменяем первый элемент представления
view_array[0] = 99
print(f"View after modification: {view_array}")
print(f"Original array after modifying the view: {original_array_view}\n")


## --- Часть 2: Изменение копии ---
print("--- Modifying a Copy ---")
## Создаем другой исходный массив
original_array_copy = np.array([10, 20, 30, 40, 50])
print(f"Original array: {original_array_copy}")

## Создаем копию массива
copy_array = original_array_copy.copy()
## Изменяем первый элемент копии
copy_array[0] = 999
print(f"Copy after modification: {copy_array}")
print(f"Original array after modifying the copy: {original_array_copy}")

Теперь сохраните файл и запустите его из терминала, чтобы увидеть вывод.

python main.py

Вы должны увидеть следующий вывод. Обратите внимание, как исходный массив изменился при изменении его представления, но остался неизменным при изменении его копии.

--- Modifying a View ---
Original array: [1 2 3 4 5]
View after modification: [99  2  3  4  5]
Original array after modifying the view: [99  2  3  4  5]

--- Modifying a Copy ---
Original array: [10 20 30 40 50]
Copy after modification: [999  20  30  40  50]
Original array after modifying the copy: [10 20 30 40 50]

Это демонстрирует основное различие: представление связано с исходными данными, в то время как копия полностью независима.

Срезы массива — создание представления

Очень распространенной операцией в NumPy является нарезка (slicing), которая используется для выбора диапазона элементов из массива. Базовая нарезка всегда создает представление (view) исходного массива. Это ключевая особенность для эффективности использования памяти, но вы должны знать, что изменение среза приведет к изменению исходных данных.

Проверим это. Очистите содержимое файла main.py и добавьте следующий код:

import numpy as np

## Создаем массив от 0 до 9
original_array = np.arange(10)
print(f"Original array: {original_array}")

## Создаем срез массива (элементы с индекса 2 по 4)
array_slice = original_array[2:5]
print(f"Slice of the array: {array_slice}")

## Изменяем первый элемент среза
print("Modifying the first element of the slice to 100...")
array_slice[0] = 100

## Снова выводим исходный массив, чтобы увидеть изменение
print(f"Original array after modification: {original_array}")

Сохраните файл и выполните его в терминале.

python main.py

Ваш вывод будет следующим:

Original array: [0 1 2 3 4 5 6 7 8 9]
Slice of the array: [2 3 4]
Modifying the first element of the slice to 100...
Original array after modification: [  0   1 100   3   4   5   6   7   8   9]

Как вы можете видеть, изменение array_slice[0] также изменило original_array[2]. Это подтверждает, что срез был представлением, а не копией.

Расширенное индексирование — создание копии

В то время как базовая нарезка создает представления, расширенная индексация (advanced indexing) всегда создает копию. Расширенная индексация включает передачу списка, кортежа или другого массива индексов для выбора элементов. Поскольку выбранные элементы могут не находиться в непрерывном блоке памяти, NumPy создает новый массив (копию) для их хранения.

Сравним это с предыдущим шагом. Очистите файл main.py и вставьте этот код:

import numpy as np

## Создаем массив от 0 до 9
original_array = np.arange(10)
print(f"Original array: {original_array}")

## Используем расширенную индексацию для выбора элементов с индексами 1, 3 и 5
indexed_array = original_array[[1, 3, 5]]
print(f"Indexed array: {indexed_array}")

## Изменяем первый элемент нового массива
print("Modifying the first element of the indexed array to 100...")
indexed_array[0] = 100

## Снова выводим исходный массив, чтобы увидеть, изменился ли он
print(f"Original array after modification: {original_array}")

Сохраните файл и запустите скрипт.

python main.py

Вывод будет следующим:

Original array: [0 1 2 3 4 5 6 7 8 9]
Indexed array: [1 3 5]
Modifying the first element of the indexed array to 100...
Original array after modification: [0 1 2 3 4 5 6 7 8 9]

На этот раз исходный массив остался неизменным. indexed_array был копией, поэтому изменения в нем не повлияли на original_array.

Определение копий и представлений с помощью .base

Иногда неочевидно, вернула ли операция представление или копию. NumPy предоставляет надежный способ проверки: атрибут .base массива.

  • Если массив является представлением (view), его атрибут .base будет указывать на исходный объект массива, к которому он принадлежит.
  • Если массив является копией (copy), его атрибут .base будет равен None.

Используем это для подтверждения наших выводов из предыдущих шагов. Очистите файл main.py и добавьте следующий код:

import numpy as np

## Создаем исходный массив
original_array = np.arange(10)
print(f"Original array: {original_array}\n")

## Создаем представление с помощью нарезки
view_slice = original_array[2:5]
print(f"Slice (view): {view_slice}")
## Проверяем, является ли срез представлением исходного массива
print(f"Is the slice a view? {view_slice.base is original_array}\n")

## Создаем копию с помощью расширенной индексации
copy_indexed = original_array[[1, 3, 5]]
print(f"Indexed (copy): {copy_indexed}")
## Проверяем, является ли индексированный массив копией
print(f"Is the indexed array a copy? {copy_indexed.base is None}")

Сохраните файл и запустите его из терминала.

python main.py

Вы получите следующий вывод, который программно подтверждает, что срез является представлением, а индексированный массив — копией.

Original array: [0 1 2 3 4 5 6 7 8 9]

Slice (view): [2 3 4]
Is the slice a view? True

Indexed (copy): [1 3 5]
Is the indexed array a copy? True

Атрибут .base является бесценным инструментом для отладки и обеспечения того, чтобы ваш код работал так, как ожидается.

Резюме

В этой лабораторной работе вы изучили критическое различие между копией и представлением массива NumPy. Вы практиковались в создании обоих и наблюдали, как изменения влияют на исходные данные.

  • Вы узнали, что такие методы, как .view() и базовая нарезка (basic slicing), создают представления (views), которые эффективны с точки зрения использования памяти, но могут привести к непреднамеренным побочным эффектам при изменении.
  • Вы узнали, что такие методы, как .copy() и расширенная индексация (advanced indexing), создают копии (copies), которые независимы от исходного массива.
  • Наконец, вы узнали, как использовать атрибут .base для окончательной проверки того, является ли массив представлением или копией.

Овладение этими концепциями является ключевым шагом к написанию надежных и эффективных численных приложений с использованием NumPy.