2

他の多くの人と同じように、私の状況では、大量のデータを収集し、データをnumpy配列として返すメソッドを提供するクラスがあります。(配列を返した後でも、追加のデータが流入し続ける可能性があります)。配列の作成はコストのかかる操作であるため、必要な場合にのみ作成し、可能な限り効率的に(具体的には、可能な場合はデータをインプレースで追加するために)作成したいと思います。

そのために、私はndarray.resize()メソッドとrefcheck引数について読んでいます。「この配列のメモリを別のPythonオブジェクトと共有していないことが確実な場合」にのみ、refcheckをFalseに設定する必要があることを理解しています。

よくわかりません。持っていることもあれば、持っていないこともあります。refcehckが失敗した場合にエラーが発生しても問題ありませんが(キャッチして新しいコピーを作成できます)、安全であることがわかっているものを無視して、「実際の」外部参照がある場合にのみ失敗させたいと思います。

簡略化した図を次に示します。

import numpy as np

def array_append(arr, values, refcheck = True):
    added_len = len(values)
    if added_len == 0:
        return arr
    old_len = len(arr)
    new_len = old_len + added_len
    arr.resize(new_len, refcheck = refcheck)
    arr[old_len:] = values
    return arr

class DataCollector(object):

    def __init__(self):
        self._new_data = []
        self._arr = np.array([])

    def add_data(self, data):
        self._new_data.append(data)

    def get_data_as_array(self):
        self._flush()
        return self._arr

    def _flush(self):
        if not self._new_data:
            return
#        self._arr = self._append1()
#        self._arr = self._append2()
        self._arr = self._append3()
        self._new_data = []

    def _append1(self):
        # always raises an error, because there are at least 2 refs:
        # self._arr and local variable 'arr' in array_append()
        return array_append(self._arr, self._new_data, refcheck = True)

    def _append2(self):
        # Does not raise an error, but unsafe in case there are other
        # references to self._arr
        return array_append(self._arr, self._new_data, refcheck = False)

    def _append3(self):
        # "inline" version: works if there are no other references
        # to self._arr, but raises an error if there are.
        added_len = len(self._new_data)
        old_len = len(self._arr)
        self._arr.resize(old_len + added_len, refcheck = True)
        self._arr[old_len:] = self._new_data
        return self._arr

dc = DataCollector()
dc.add_data(0)
dc.add_data(1)
print dc.get_data_as_array()
dc.add_data(2)
print dc.get_data_as_array()
x = dc.get_data_as_array()  # create an external reference
print x.shape
for i in xrange(5000):
    dc.add_data(999)
print dc.get_data_as_array()
print x.shape

質問:

  1. 私がやろうとしていること(numpy配列を段階的に作成する)を行うためのより良い(高速な)方法はありますか?
  2. resize()メソッドに「refcheckを実行しますが、安全であることがわかっている1つの参照(またはn個の参照)を無視する」ように指示する方法はありますか?(これにより、_append1()が常に失敗するという問題が解決されます)
4

2 に答える 2

1

このresize方法には2つの主な問題があります。1つ目は、ユーザーが。を呼び出したときに、self._arrへの参照を返すことですget_data_as_array。これで、サイズ変更は、実装に応じて2つのことのいずれかを実行します。ユーザーに指定した配列を変更します。つまり、ユーザーが取得a.shapeし、形状が予期せず変化します。または、そのアレイが破損し、メモリが不良であることを示します。常にget_data_as_arrayリターンself._arr.copy()を持っていることでその問題を解決することができますが、それは私を2番目の問題に導きます。resize実際にはあまり効率的ではありません。一般に、resizeは新しいメモリを割り当て、配列を拡張するために呼び出されるたびにコピーを実行する必要があると思います。さらに、ユーザーに配列を返すたびに配列をコピーする必要があります。

別のアプローチは、次のような独自の動的配列を設計することです。

class DynamicArray(object):

    _data = np.empty(1)
    data = _data[:0]
    len = 0
    scale_factor = 2

    def append(self, values):
        old_data = len(self.data)
        total_data = len(values) + old_data
        total_storage = len(self._data)
        if total_storage < total_data:
            while total_storage < total_data:
                total_storage = np.ceil(total_storage * self.scale_factor)
            self._data = np.empty(total_storage)
            self._data[:old_data] = self.data

        self._data[old_data:total_data] = values
        self.data = self._data[:total_data]

アレイのlog(N)倍だけ拡張する必要があり、最大2 * N-1のストレージを使用するため、これは非常に高速である必要があります。ここで、Nはアレイの最大サイズです。配列を拡張する以外は_data、コピーを必要とせず、一定時間である必要があるビューを作成しているだけです。

これがお役に立てば幸いです。

于 2013-03-06T01:09:40.127 に答える
1

私はarray.array()データ収集を行うために使用します:

import array
a = array.array("d")
for i in xrange(100):
    a.append(i*2)

収集したデータを使用して計算を行う場合は、次のように変換しnumpy.ndarraynumpy.frombufferください。

b = np.frombuffer(a, dtype=float)
print np.mean(b)

bとデータメモリを共有するaため、変換は非常に高速です。

于 2013-03-05T13:44:58.887 に答える