2012-07-02 2 views
30

Мое впечатление, что в NumPy два массива могут использовать одну и ту же память. Рассмотрим следующий пример:Есть ли способ проверить, имеют ли массивы NumPy одни и те же данные?

import numpy as np 
a=np.arange(27) 
b=a.reshape((3,3,3)) 
a[0]=5000 
print (b[0,0,0]) #5000 

#Some tests: 
a.data is b.data #False 
a.data == b.data #True 

c=np.arange(27) 
c[0]=5000 
a.data == c.data #True (Same data, not same memory storage), False positive 

Так ясно b не сделать копию a; он просто создал несколько новых метаданных и привязал их к тому же буферу памяти, который использует a. Есть ли способ проверить, ссылаются ли на два массива один и тот же буфер памяти?

Мое первое впечатление заключалось в использовании a.data is b.data, но это возвращает false. Я могу сделать a.data == b.data, который возвращает True, но я не думаю, что проверяет, чтобы убедиться, что a и b используют один и тот же буфер памяти, только тот блок памяти, на который ссылается a, и тот, на который ссылаются b, имеет одинаковые байты.

+2

Вот наиболее актуальный ранее задаваемый вопрос: http://stackoverflow.com/questions/10747748/how-do-i-check-that-two-slices-of-numpy-arrays-are-the-same- или перекрытие –

+1

@RobertKern - Спасибо. Я действительно видел эту запись, но поскольку я не мог найти документацию для 'numpy.may_share_memory' (кроме встроенной' help'), я думал, что может быть что-то еще - например. 'Numpy.uses_same_memory_exactly'. (мой вариант использования немного менее общий, чем другой, поэтому я подумал, что может быть более окончательный ответ). В любом случае, увидев ваше имя в нескольких списках рассылки, я предполагаю, что ответ «нет такой функции». – mgilson

+1

'numpy.may_share_memory()' не отображается в справочном руководстве только из-за аварии организации справочного руководства. Это правильная вещь. К сожалению, в данный момент функция 'uses_same_memory_exactly()' не существует. Для реализации такой функции требуется решение ограниченного линейного диофантова уравнения, NP-жесткая задача. Размер проблемы обычно не слишком велик, но просто записывать алгоритм раздражает, поэтому он еще не выполнен. Если мы это сделаем, он будет включен в 'numpy.may_share_memory() ', поэтому я рекомендую использовать. –

ответ

8

Я думаю, что ответ jterrace является, вероятно, лучший способ пойти, но вот еще одна возможность.

def byte_offset(a): 
    """Returns a 1-d array of the byte offset of every element in `a`. 
    Note that these will not in general be in order.""" 
    stride_offset = np.ix_(*map(range,a.shape)) 
    element_offset = sum(i*s for i, s in zip(stride_offset,a.strides)) 
    element_offset = np.asarray(element_offset).ravel() 
    return np.concatenate([element_offset + x for x in range(a.itemsize)]) 

def share_memory(a, b): 
    """Returns the number of shared bytes between arrays `a` and `b`.""" 
    a_low, a_high = np.byte_bounds(a) 
    b_low, b_high = np.byte_bounds(b) 

    beg, end = max(a_low,b_low), min(a_high,b_high) 

    if end - beg > 0: 
     # memory overlaps 
     amem = a_low + byte_offset(a) 
     bmem = b_low + byte_offset(b) 

     return np.intersect1d(amem,bmem).size 
    else: 
     return 0 

Пример:

>>> a = np.arange(10) 
>>> b = a.reshape((5,2)) 
>>> c = a[::2] 
>>> d = a[1::2] 
>>> e = a[0:1] 
>>> f = a[0:1] 
>>> f = f.reshape(()) 
>>> share_memory(a,b) 
80 
>>> share_memory(a,c) 
40 
>>> share_memory(a,d) 
40 
>>> share_memory(c,d) 
0 
>>> share_memory(a,e) 
8 
>>> share_memory(a,f) 
8 

Вот график, показывающий время для каждого share_memory(a,a[::2]) вызова в зависимости от числа элементов в a на моем компьютере.

share_memory function

+4

. Можно иметь представления, которые делят память даже с разными элементами. Например, я могу получить массив как 'float32' с чередующимися реальными и мнимыми компонентами и рассматривать его как массив' complex64'. Более надежная реализация выполняется в 'numpy.may_share_memory()'. –

+0

@RobertKern: Хорошая точка. Я обновил свой ответ. Вы видите какие-либо потенциальные проблемы с этим решением? – user545424

+0

Я думаю, что, наконец, понял. 'share_memory()' требует памяти порядка сумм размеров каждого массива, но это довольно быстро. – user545424

26

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

>>> import numpy as np 
>>> a = np.arange(27) 
>>> b = a.reshape((3,3,3)) 
>>> b.base is a 
True 
>>> a.base is b 
False 

Не уверен, если это решает проблему. Базовый атрибут будет None, если массив владеет собственной памятью. Обратите внимание, что база на массив будет другой массив, даже если она является подмножеством:

>>> c = a[2:] 
>>> c.base is a 
True 
+0

Это, вероятно, достаточно хорошо для моих целей. К сожалению, это не улица с 2-мя движениями. Я подожду и посмотрю, если что-нибудь еще лучше всплывет. Тем временем, спасибо. (+1) – mgilson

+0

Вы можете сделать «a.base is b или b.base is a». – user545424

+4

Это ненадежно. Каждый массив может иметь цепочки атрибутов «.base», например. 'a.base.base is b' может быть правдой. Массивы также могут быть сконструированы так, чтобы указывать на одну и ту же память, не разделяя одни и те же объекты .base. –

4

Вобще:

a = np.arange(27) 
a.__array_interface__['data'] 

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

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