Копии и представления
При работе с массивами NumPy можно напрямую получить доступ к внутреннему буферу данных, используя представление, не копируя данные. Это обеспечивает высокую производительность, но также может привести к нежелательным проблемам, если пользователь не понимает, как это работает. Поэтому важно знать разницу между этими двумя терминами и какие операции возвращают копии, а какие — представления.
Массив NumPy — это структура данных, состоящая из двух частей: смежного буфера данных с фактическими элементами данных и метаданных, содержащих информацию о буфере данных. Метаданные включают тип данных, шаги и другую важную информацию, которая помогает легко управлять ndarray. Подробный обзор см. в разделе Внутренняя организация массивов NumPy.
Представление
Можно получить доступ к массиву по-другому, просто изменив некоторые метаданные, такие как шаг и тип данных, не изменяя буфер данных. Это создает новый способ просмотра данных, и эти новые массивы называются представлениями. Буфер данных остается неизменным, поэтому любые изменения, внесенные в представление, отражаются в исходной копии. Представление можно принудительно создать с помощью метода ndarray.view.
Копия
Когда новый массив создаётся путём дублирования буфера данных и метаданных, он называется копией. Изменения, внесенные в копию, не отражаются в исходном массиве. Создание копии медленнее и потребляет больше памяти, но иногда это необходимо. Копию можно создать, используя ndarray.copy.
Операции индексирования
См. также
Представления создаются, когда элементы можно адресовать с помощью смещений и шагов в исходном массиве. Поэтому базовые операции индексирования всегда создают представления. Например:
>>> 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, потому что это представление.
Расширенное индексирование, с другой стороны, всегда создаёт копии. Например:
>>> 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]] представление или копия не создаются, так как присвоение происходит непосредственно.
Другие операции
Функция numpy.reshape создаёт представление, если это возможно, или копию в противном случае. В большинстве случаев шаги можно изменить для изменения формы массива с помощью представления. Однако в некоторых случаях, когда массив становится несмежным (возможно, после операции ndarray.transpose), изменение формы с помощью изменения шагов невозможно и требуется копия. В этих случаях можно вызвать ошибку, присвоив новую форму атрибуту формы массива. Например:
>>> 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.
Рассмотрим пример другой операции, ravel возвращает сплошное уплощенное представление массива, где это возможно. С другой стороны, ndarray.flatten всегда возвращает уплощенную копию массива. Однако для гарантии представления в большинстве случаев, x.reshape(-1) может быть предпочтительнее.
Как определить, является ли массив представлением или копией
Атрибут base ndarray позволяет легко определить, является ли массив представлением или копией. Атрибут base представления возвращает исходный массив, а для копии он возвращает None.
>>> 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.
© 2005–2024 NumPy Developers
Licensed under the 3-clause BSD License.
https://numpy.org/doc/2.0/user/basics.copies.html