Копии и представления#
При работе с массивами NumPy возможно получить прямой доступ к внутреннему буферу данных, используя представление без копирования данных. Это обеспечивает хорошую производительность, но также может вызвать нежелательные проблемы, если пользователь не знает, как это работает. Поэтому важно понимать разницу между этими двумя терминами и знать, какие операции возвращают копии, а какие — представления.
Массив NumPy — это структура данных, состоящая из двух частей: непрерывный буфер данных с фактическими элементами данных и метаданными, содержащими информацию о буфере данных. Метаданные включают тип данных, шаги и другую важную информацию, которая помогает манипулировать ndarray легко. См. Внутренняя организация массивов NumPy
раздел для подробного рассмотрения.
Вид#
Можно получить доступ к массиву по-другому, просто изменив определённые
метаданные, такие как шаг и dtype без изменения буфера данных. Это создает новый способ просмотра данных, и эти новые массивы называются представлениями. Буфер данных остается неизменным, поэтому любые изменения, внесенные в представление, отражаются в исходной копии. Представление может быть принудительно создано через
ndarray.view метод.
Копировать#
Когда новый массив создается путем дублирования буфера данных вместе с метаданными, это называется копией. Изменения, внесенные в копию, не отражаются на исходном массиве. Создание копии медленнее и требует больше памяти, но иногда необходимо. Копию можно принудительно создать с помощью
ndarray.copy.
Операции индексирования#
Смотрите также
Представления создаются, когда элементы могут быть адресованы с помощью смещений и шагов в исходном массиве. Следовательно, базовое индексирование всегда создает представления. Например:
>>> import numpy as np
>>> x = np.arange(10)
>>> x
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> y = x[1:3] # creates a view
>>> y
array([1, 2])
>>> x[1:3] = [10, 11]
>>> x
array([ 0, 10, 11, 3, 4, 5, 6, 7, 8, 9])
>>> y
array([10, 11])
Здесь, y изменяется, когда x изменяется, потому что это представление.
Расширенная индексация, с другой стороны, всегда создаёт копии. Например:
>>> import numpy as np
>>> x = np.arange(9).reshape(3, 3)
>>> x
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> y = x[[1, 2]]
>>> y
array([[3, 4, 5],
[6, 7, 8]])
>>> y.base is None
True
Здесь, y является копией, как обозначено base
атрибут. Мы также можем подтвердить это, присваивая новые значения x[[1, 2]]
что, в свою очередь, не повлияет на y вообще:
>>> x[[1, 2]] = [[10, 11, 12], [13, 14, 15]]
>>> x
array([[ 0, 1, 2],
[10, 11, 12],
[13, 14, 15]])
>>> y
array([[3, 4, 5],
[6, 7, 8]])
Следует отметить, что во время присваивания x[[1, 2]] не создается ни представление,
ни копия, так как присваивание происходит на месте.
Другие операции#
The numpy.reshape функция создаёт представление, где это возможно, или копию в противном случае. В большинстве случаев шаги (strides) могут быть изменены для изменения формы массива с помощью представления. Однако в некоторых случаях, когда массив становится несмежным (возможно, после ndarray.transpose операции),
изменение формы не может быть выполнено путем изменения шагов и требует копирования.
В таких случаях мы можем вызвать ошибку, присвоив новую форму
атрибуту shape массива. Например:
>>> import numpy as np
>>> x = np.ones((2, 3))
>>> y = x.T # makes the array non-contiguous
>>> y
array([[1., 1.],
[1., 1.],
[1., 1.]])
>>> z = y.view()
>>> z.shape = 6
Traceback (most recent call last):
...
AttributeError: Incompatible shape for in-place modification. Use
`.reshape()` to make a copy with the desired shape.
Рассмотрим пример другой операции, numpy.ravel возвращает
непрерывное сглаженное представление массива, где это возможно. С другой стороны,
ndarray.flatten всегда возвращает сглаженную копию массива. Однако, чтобы гарантировать представление в большинстве случаев, x.reshape(-1) может быть предпочтительнее.
Как определить, является ли массив представлением или копией#
The base атрибут ndarray позволяет легко
определить, является ли массив представлением или копией. Атрибут base представления возвращает
исходный массив, тогда как он возвращает None для копии.
>>> import numpy as np
>>> x = np.arange(9)
>>> x
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> y = x.reshape(3, 3)
>>> y
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
>>> y.base # .reshape() creates a view
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> z = y[[2, 1]]
>>> z
array([[6, 7, 8],
[3, 4, 5]])
>>> z.base is None # advanced indexing creates a copy
True
Обратите внимание, что base атрибут не должен использоваться для определения,
является ли объект ndarray новый; только если это представление или копия другого ndarray.