Документация о том, когда python автоматически создает копии массивов - PullRequest
0 голосов
/ 30 июня 2019

Я пытаюсь найти документацию о том, когда python автоматически создает копии массивов numpy. Это связано с представлениями массива.

Если используется простая индексация, присваивание создает представление массива. Если используется расширенная индексация, Python делает копию. Я нашел другие случаи, когда python делает копии массивов, но не смог найти документацию. Вот пример:

a = np.array([1.,2.,3.,4.,5.])
av = a.view()
print(a)
print(av)
a[0] = 100.0
print(av)
a = 0
print(av)

Переменная av была представлением для a до тех пор, пока a не изменилось настолько, чтобы больше не могло быть представлением. Где это задокументировано?

Ответы [ 2 ]

1 голос
/ 01 июля 2019
In [118]: a = np.array([1.,2.,3.,4.,5.]) 
     ...: av = a.view()                                                                                         
In [119]: a                                                                                                     
Out[119]: array([1., 2., 3., 4., 5.])
In [120]: av                                                                                                    
Out[120]: array([1., 2., 3., 4., 5.])

Массив - это объект с атрибутами, такими как dtype и shape, плюс указатель на буфер данных (base).Мы можем получить сводку этих атрибутов с помощью:

In [121]: a.__array_interface__                                                                                 
Out[121]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

av - это новый объект массива, но он имеет те же атрибуты, включая data:

In [122]: av.__array_interface__                                                                                
Out[122]: 
{'data': (69293648, False),       # same value as for a
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

сравните это с copy:

In [123]: bv = a.copy()                                                                                         
In [124]: bv.__array_interface__                                                                                
Out[124]: 
{'data': (77843920, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

Другие виды могут иметь разные значения - срезы имеют различную форму и шаги и т. д. Но буфер данных будет одинаковым (фактическое число, показанное в __array_interface__['data'], может бытьнемного по-другому, указывая на другой элемент в буфере.

Измените элемент на a, и мы увидим это изменение также в av - из-за общего буфера данных:

In [125]: a[0] =12                                                                                              
In [126]: av[0]                                                                                                 
Out[126]: 12.0
In [127]: bv[0]                                                                                                 
Out[127]: 1.0

Когда вы назначаете что-то еще для a, av не изменяется.

In [128]: a = 10                                                                                                
In [129]: a.__array_interface__                                                                                 
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-129-3b348559b028> in <module>
----> 1 a.__array_interface__

AttributeError: 'int' object has no attribute '__array_interface__'
In [130]: av.__array_interface__                                                                                
Out[130]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

a - это целое число, а не массив. Буфер данных, который был изначально создан дляa все еще существует, служа базой для av. В противном случае исходный объект a исчезнет.

Мне нужно определить этот последний оператор - из-за буферизации вывода ipython, чтоОбъект массива все еще существует:

In [131]: Out[119].__array_interface__                                                                          
Out[131]: 
{'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3}

In [132]: Out[119][0]                                                                                           
Out[132]: 12.0

Я делаюybe должен был отобразить id(a) и т. д., чтобы уточнить

In [133]: id(av)                                                                                                
Out[133]: 139992523130800
In [134]: id(Out[119])                                                                                          
Out[134]: 139992142353312

изменения в av появляются в Out[119], но не в a - потому что a - это совершенно другой объект.

In [144]: av[1] = 100                                                                                           
In [145]: Out[119]                                                                                              
Out[145]: array([ 12., 100.,   3.,   4.,   5.])
In [146]: a                                                                                                     
Out[146]: 10

Итак, на каком-то уровне вам необходимо понять природу переменных и объектов Python.Например, b=a просто создает другое имя или ссылку на тот же объект, а не копию.b = a.copy() делает копию, но детали этой копии зависят от класса объекта.numpy добавляет вариант copy, view, что позволяет экономить время и память.Но вы должны знать кое-что о его механизме хранения данных, чтобы понять это.

0 голосов
/ 30 июня 2019

Из документов NumPy на Расширенное индексирование :

Расширенное индексирование запускается, когда объект выбора obj является объектом последовательности, не состоящим из кортежей, ndarray (типа данных integer или bool) или кортежем, по крайней мере, с одним объектом последовательности или ndarray (типа данных integer или BOOL). Существует два типа расширенной индексации: целочисленная и логическая.

Расширенное индексирование всегда возвращает копию данных (в отличие от базового среза, который возвращает представление).


Вы можете использовать атрибут base , чтобы проверить, является ли ndarray копией или представлением другого.

a = np.arange(0, 10)

b = a.view()
print(b.base is a) # True

c, d = a[2:], a[::2]
print(c.base is a and d.base is a) # True

b = a.copy()
print(b.base is a) # False

b = a[a >= 5]
print(b.base is a) # False



РЕДАКТИРОВАТЬ: Мне показалось интересным, что вы можете создать представление «b» для массива «a», а затем изменить исходный адрес памяти массива (используя метод resize). Атрибут «base» для представления по-прежнему указывает на b, но изменения в b не отражаются на a:

a = np.arange(0, 10)
b = a.view()
print(np.shares_memory(a, b)) # True
print(b.base is a) # True

a.resize(50, refcheck=False)
print(np.shares_memory(a, b)) # False
print(b.base is a) # True

b[0] = 20
print(a[0] == b[0]) # False

Еще один трюк, который вы можете сделать, - это вручную изменить атрибут «данные» представления. Если вы сделаете это, атрибут base будет изменен:

a, b = np.arange(0, 10), np.arange(10, 20)
c = a.view()
print(c.base is a) # True

c.data = b.data
print(c.base is a) # False
print(c.base is b) # False
print(c[0]) # 10
print(c.base) # 'Memory at ....'
print(c.base == c.data == b.data) # True
...