2015-12-08 4 views
1

Как скопировать массив numpy таким образом, чтобы он имел идентичный макет памяти в исходном массиве, включая шаги и любую связанную с ними несогласованность? В принципе, новый __array_interface__ должен быть идентичным, за исключением указателя.Как сделать точную копию массива numpy (включая шаги)

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

Пример (обратите внимание, что копирование изменяет шаги):

>>> import numpy as np 
>>> a = np.empty((10, 6)) 
>>> b = a[::2] 
>>> b.strides 
(96, 8) 
>>> b.copy(order='K').strides 
(48, 8) 

Edit:

Вот полная функция Я использую на основе ответа senderle в:

def exact_copy(data): 
    if data.base is None: 
     return data.copy()   
    base = data.base.copy() 
    offset = (data.__array_interface__['data'][0] - 
       data.base.__array_interface__['data'][0]) 
    return np.ndarray(buffer=base.data, shape=data.shape, strides=data.strides, 
         offset=offset, dtype=data.dtype) 
+0

Работает ли '.copy()'? Если нет, когда это неправильно? – hpaulj

+0

'copy' переустанавливает данные как смежный, c-упорядоченный массив (другими словами, он не сохраняет существующий макет памяти). – Luke

+0

Как насчет 'order = 'K''? – hpaulj

ответ

0

В основном , вы можете это сделать, но вы, вероятно, не должны в большинстве случаев - выгоды не такие огромные, а много вещей в numpy сложнее с несмежными данными.

Тем не менее, это не сложно сделать, что вы хотите - вы можете использовать аргумент strides, выставленный ndarray. Скажем, мы начинаем с массива, в котором есть некоторые вкусные яблоки, но много других крутых. Как мы получаем яблоки?

>>> xapplesx = numpy.array(list('xxxaxpxpxlxexsxxx')) 
>>> xapplesx 
array(['x', 'x', 'x', 'a', 'x', 'p', 'x', 'p', 'x', 'l', 'x', 'e', 'x', 
     's', 'x', 'x', 'x'], 
     dtype='|S1') 

Мы хотим начать с индексом 3, и мы хотим, чтобы все другие ценности и положить его в одномерном зрения, это означает, что мы хотим, чтобы принимать все значения 2 й.

Так мы проходим offset=3 и strides=(2,):

>>> apples = numpy.ndarray(shape=(6,), dtype='|S1', 
          buffer=xapplesx, offset=3, strides=(2,)) 
>>> apples 
array(['a', 'p', 'p', 'l', 'e', 's'], 
     dtype='|S1') 

Привыкание к использованию strides непосредственно этот путь занимает немного тщательного мышления. Предположим, мы хотим получить наши яблоки и положить их на x s. Теперь нам нужен двумерный массив, и мы должны указать шаги для обоих измерений. Но мы хотим, чтобы вторая строка данных содержала значения, которые поступают сразу после яблок. Таким образом, мы задаем большой шаг только 1 для этого измерения:

>>> applesoverx = numpy.ndarray(shape=(2, 6), dtype='|S1', 
           buffer=xapplesx, offset=3, strides=(1, 2)) 
>>> applesoverx 
array([['a', 'p', 'p', 'l', 'e', 's'], 
     ['x', 'x', 'x', 'x', 'x', 'x']], 
     dtype='|S1') 

Если у вас возникли проблемы при получении правильные данные buffer, вероятно, можно сделать что-то вроде этого:

>>> a = np.arange(12).reshape(4,3)[::-1,:] 
>>> x = np.ndarray(shape=a.shape, buffer=a.base.data, 
        strides=a.strides, offset=72, dtype=a.dtype) 

Единственный трюк что вам нужно выработать правильное смещение. В результате (я уверен) идеальный клон:

>>> x 
array([[ 9, 10, 11], 
     [ 6, 7, 8], 
     [ 3, 4, 5], 
     [ 0, 1, 2]]) 
>>> a 
array([[ 9, 10, 11], 
     [ 6, 7, 8], 
     [ 3, 4, 5], 
     [ 0, 1, 2]]) 
>>> x.shape 
(4, 3) 
>>> a.shape 
(4, 3) 
>>> x.strides 
(-24, 8) 
>>> a.strides 
(-24, 8) 
>>> id(x.base) 
140602344736320 
>>> id(a.base) 
140602344736320 

Если вы хотите, чтобы сделать копию исходных данных, вы можете использовать a.base.copy().data в качестве аргумента buffer. И (как Лука указал), вы можете определить смещение (см выше) с этим:

a.__array_interface__['data'][0] - a.base.__array_interface__['data'][0] 

Это дает вам ссылку на указатель, на котором буфер начинается для данного массива, как описано here.

+0

Случай, который может быть невозможно клонировать, - это 'np.arange (12) .reshape (4,3) [:: - 1,:]'. Strides - '(-4,16)', и буфер не является непрерывным. – hpaulj

+0

@hpaulj, правда, но я думаю, это просто потому, что 'numpy' не даст вам данных буфера для фрагментов. Если вы получаете исходный буфер, используемый для создания среза (через 'a.base'), вы можете дублировать срез с явными шагами, и результат будет клонирован. – senderle

+0

Это приближает нас, но память на самом деле не была скопирована (новый массив указывает на то же место в памяти). – Luke

Смежные вопросы