10

前の質問で、サブクラス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"配列が私のタイプにビューキャストされるFooようですが、この時点でドキュメントは100%明確/正確ではないようです。いずれにせよ、ビューのキャスト後、出力はデータを所有しなくなり、その場で形状を変更することができなくなります(私が知る限り)。

なんらかの厄介なブードゥー(__array_prepare____array__)などを介して、データの所有権をサブクラスのインスタンスに譲渡する方法はありますか?

4

3 に答える 3

6

満足のいく答えとは言えませんが、コメントにも当てはまりません... 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

中間メモリを節約できる可能性があり、データを所有できるようになります。それは「読みやすさのカウント」のマントラを窓の外に投げ出しますが...

于 2013-03-15T20:45:52.497 に答える
1

しかし、途中のある時点で、「出力」配列が私のFooタイプにビューキャストされるようです。

はい、をndarray.__array_prepare__呼び出しますoutput.view。これは、データを所有していない配列を返します。

私は少し実験しましたが、それを回避する簡単な方法を見つけることができませんでした。

この動作は理想的ではないことに同意しますが、少なくともあなたのユースケースでは、dそのデータを所有しないことは許容できると思います。Numpyはビューを広範囲に使用しており、numpy配列での作業でビューを作成しないように主張すると、人生が非常に困難になります。

また、私の経験に基づいて、resize一般的には避けるべきだと主張します。resizeingを回避すれば、作成されたビューで問題なく動作するはずです。それにはハッキーな感じがあり、操作するのは難しいです(あなたが理解し始めるかもしれませんが、それを使用するときに2つの古典的なエラーの1つに遭遇しました:it does not own its data。もう1つはですcannot resize an array that has been referenced)。(別の問題はこの質問で説明されています。)

使用するというあなたの決定はあなたの他の質問への答えから来るので、私はそこresizeに私の答えの残りを投稿します。

于 2013-03-15T09:01:24.340 に答える
0

どうですか:

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
于 2013-03-15T12:44:18.763 に答える