Передача права собственности на данные о количестве
В предыдущем вопросе , я научился изменять размер подкласса ndarray
на месте. Ухоженная. К сожалению, это больше не работает, когда массив, который я пытаюсь изменить, является результатом вычисления:
import numpy as np
class Foo(np.ndarray):
def __new__(cls,shape,dtype=np.float32,buffer=None,offset=0,
strides=None,order=None):
return np.ndarray.__new__(cls,shape,dtype,buffer,offset,strides,order)
def __array_prepare__(self,output,context):
print output.flags['OWNDATA'],"PREPARE",type(output)
return np.ndarray.__array_prepare__(self,output,context)
def __array_wrap__(self,output,context=None):
print output.flags['OWNDATA'],"WRAP",type(output)
return np.ndarray.__array_wrap__(self,output,context)
a = Foo((32,))
#resizing a is no problem
a.resize((24,),refcheck=False)
b = Foo((32,))
c = Foo((32,))
d = b+c
#Cannot resize `d`
d.resize((24,),refcheck=False)
Точный вывод (включая трассировку):
True PREPARE <type 'numpy.ndarray'>
False WRAP <class '__main__.Foo'>
Traceback (most recent call last):
File "test.py", line 26, in <module>
d.resize((24,),refcheck=False)
ValueError: cannot resize this array: it does not own its data
Я думаю, это потому, что numpy
создает новый ndarray
и передает его на __array_prepare__
. В какой-то момент по пути, кажется, что "output
"
array получает view-casted в мой Foo
тип, хотя документы не кажутся на 100% четкими/точными в этой точке. В любом случае, после каста представления, выход больше не владеет данными, что делает невозможным изменение на месте (насколько я могу судить).
Есть ли какой-либо способ с помощью какого-то numood voodoo (__array_prepare__
, __array__
) и т.д., чтобы передать право собственности на данные в экземпляр моего подкласса?
Ответы
Ответ 1
Это вряд ли удовлетворительный ответ, но он не вписывается в комментарий... Вы можете обойти владение данными с помощью параметра ufunc out
. Глупый пример:
>>> a = Foo((5,))
>>> b = Foo((5,))
>>> c = a + b # BAD
True PREPARE <type 'numpy.ndarray'>
False WRAP <class '__main__.Foo'>
>>> c.flags.owndata
False
>>> c = Foo((5,))
>>> c[:] = a + b # BETTER
True PREPARE <type 'numpy.ndarray'>
False WRAP <class '__main__.Foo'>
>>> c.flags.owndata
True
>>> np.add(a, b, out=c) # BEST
True PREPARE <class '__main__.Foo'>
True WRAP <class '__main__.Foo'>
Foo([ 1.37754085e-38, 1.68450356e-20, 6.91042737e-37,
1.74735556e-04, 1.48018885e+29], dtype=float32)
>>> c.flags.owndata
True
Я думаю, что вывод выше согласуется с тем, что c[:] = a + b
получает собственные данные за счет копирования его в c
из временного массива. Но это не должно происходить, если вы используете параметр out
.
Так как вы уже беспокоились о промежуточном хранении в своих математических выражениях, возможно, не так уж плохо, если вы управляете процессом управления. То есть, заменяя
g = a + b + np.sqrt(d*d + e*e + f*f)
с
g = foo_like(d) # you'll need to write this function!
np.multiply(d, d, out=g)
g += e * e
g += f * f
np.sqrt(g, out=g)
g += b
g += a
может сэкономить вам некоторую промежуточную память, и она позволит вам владеть вашими данными. Он бросает мантру "удобочитаемость" из окна, но...
Ответ 2
В какой-то момент, по ходу пути, кажется, что массив "output" получает вид-casted к моему типу Foo
Да, ndarray.__array_prepare__
вызывает output.view
, который возвращает массив, который не имеет своих данных.
Я немного экспериментировал и не мог найти легкий путь.
Хотя я согласен, что это поведение не является идеальным, по крайней мере, в вашем случае использования, я бы заявил, что для d
допустимо не владеть его данными. Numpy широко использует представления, и если вы настаиваете на том, чтобы не создавать какие-либо представления при работе с массивами numpy, вы делаете свою жизнь очень тяжело.
Я также утверждаю, что, основываясь на моем опыте, resize
обычно следует избегать. У вас не должно возникнуть проблем с созданием представления, если вы избежите resize
ing. Там хакерское чувство, и с ним трудно работать (как вы могли бы начать понимать, столкнувшись с одной из двух классических ошибок при его использовании: it does not own its data
, а другой - cannot resize an array that has been referenced
). (Еще одна проблема описана в этом вопросе.)
Поскольку ваше решение использовать resize
вытекает из ответа на ваш другой вопрос, я отправлю оставшуюся часть своего ответа там.
Ответ 3
Как насчет:
def resize(arr, shape):
np.require(arr, requirements=['OWNDATA'])
arr.resize(shape, refcheck=False)
Кажется, удастся изменить размер (и уменьшить потребление памяти):
import array
import numpy as np
import time
class Foo(np.ndarray):
def __new__(cls, shape, dtype=np.float32, buffer=None, offset=0,
strides=None, order=None):
return np.ndarray.__new__(cls, shape, dtype, buffer, offset, strides, order)
def __array_prepare__(self, output, context):
print(output.flags['OWNDATA'], "PREPARE", type(output))
return np.ndarray.__array_prepare__(self, output, context)
def __array_wrap__(self, output, context=None):
print(output.flags['OWNDATA'], "WRAP", type(output))
output = np.ndarray.__array_wrap__(self, output, context)
return output
def free_memory():
"""
Return free memory available, including buffer and cached memory
"""
total = 0
with open('/proc/meminfo', 'r') as f:
for line in f:
line = line.strip()
if any(line.startswith(field) for field in ('MemFree', 'Buffers', 'Cached')):
field, amount, unit = line.split()
amount = int(amount)
if unit != 'kB':
raise ValueError(
'Unknown unit {u!r} in /proc/meminfo'.format(u=unit))
total += amount
return total
def gen_change_in_memory():
"""
http://stackoverflow.com/a/14446011/190597 (unutbu)
"""
f = free_memory()
diff = 0
while True:
yield diff
f2 = free_memory()
diff = f - f2
f = f2
change_in_memory = gen_change_in_memory().next
def resize(arr, shape):
print(change_in_memory())
# 0
np.require(arr, requirements=['OWNDATA'])
time.sleep(1)
print(change_in_memory())
# 200
arr.resize(shape, refcheck=False)
N = 10000000
b = Foo((N,), buffer = array.array('f',range(N)))
c = Foo((N,), buffer = array.array('f',range(N)))
дает
print(change_in_memory())
# 0
d = b+c
d = np.require(d, requirements=['OWNDATA'])
print(change_in_memory())
# 39136
resize(d, (24,)) # Increases memory by 200 KiB
time.sleep(1)
print(change_in_memory())
# -39116