2

numpy 構造化配列の列をより効率的に割り当てる方法を探しています。

例:

my_col = fn_returning_1D_array(...)

私のマシンでは、構造化配列の列への同じ割り当てよりも 2 倍以上高速に実行されます。

test = np.ndarray(shape=(int(8e6),), dtype=dtype([('column1', 'S10'), ...more columns...]))
test['column1'] = fn_returning_1D_array(...)

Fortran順序で作成testしてみましたが、役に立ちませんでした。おそらく、フィールドはメモリ内でインターリーブされたままです。

ここで何か考えがある人はいますか?低レベルの numpy インターフェイスと cython が役に立てば喜んで使用します。


編集 1: hpaulj の回答に応じて

再配列列の割り当てと「通常の」配列列の割り当ての明らかな同等性は、後者が行優先順で作成された場合にのみ得られます。列優先の順序付けでは、2 つの割り当ては同等とは言えません。

行優先

In [1]: import numpy as np

In [2]: M,N=int(1e7),10

In [4]: A1=np.zeros((M,N),'f')

In [9]: dt=np.dtype(','.join(['f' for _ in range(N)]))

In [10]: A2=np.zeros((M,),dtype=dt)

In [11]: X=np.arange(M+0.0)

In [13]: %timeit for n in range(N):A1[:,n]=X
1 loops, best of 3: 2.36 s per loop

In [15]: %timeit for n in dt.names: A2[n]=X
1 loops, best of 3: 2.36 s per loop

In [16]: %timeit A1[:,:]=X[:,None]
1 loops, best of 3: 334 ms per loop

In [8]: A1.flags
Out[8]:
  C_CONTIGUOUS : True
  F_CONTIGUOUS : False
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

列優先

In [1]: import numpy as np

In [2]: M,N=int(1e7),10

In [3]: A1=np.zeros((M,N),'f', 'F')

In [4]: dt=np.dtype(','.join(['f' for _ in range(N)]))

In [5]: A2=np.zeros((M,),dtype=dt)

In [6]: X=np.arange(M+0.0)

In [8]: %timeit for n in range(N):A1[:,n]=X
1 loops, best of 3: 374 ms per loop

In [9]: %timeit for n in dt.names: A2[n]=X
1 loops, best of 3: 2.43 s per loop

In [10]: %timeit A1[:,:]=X[:,None]
1 loops, best of 3: 380 ms per loop

In [11]: A1.flags
Out[11]:
  C_CONTIGUOUS : False
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

列優先の順序付けの場合、2 つのバッファーはもはや同一ではないことに注意してください。

In [6]: A3=np.zeros_like(A2)

In [7]: A3.data = A1.data

In [20]: A2[0]
Out[20]: (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)

In [21]: A2[1]
Out[21]: (1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)

In [16]: A3[0]
Out[16]: (0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0)

In [17]: A3[1]
Out[17]: (10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0)
4

1 に答える 1

1

これらは同等のアクションではありません。配列を生成するだけです(そしてそれを変数に割り当てる、マイナーアクション)。もう 1 つは配列を生成し、構造化配列の列を埋めます。

my_col = fn_returning_1D_array(...)
test['column1'] = fn_returning_1D_array(...)

より公平な比較は、2D 配列の列を埋めることだと思います。

In [38]: M,N=1000,10
In [39]: A1=np.zeros((M,N),'f')   # 2D array
In [40]: dt=np.dtype(','.join(['f' for _ in range(N)]))
In [41]: A2=np.zeros((M,),dtype=dt)   # structured array
In [42]: X=np.arange(M+0.0)

In [43]: A1[:,0]=X   # fill a column
In [44]: A2['f0']=X   # fill a field

In [45]: timeit for n in range(N):A1[:,n]=X
10000 loops, best of 3: 65.3 µs per loop

In [46]: timeit for n in dt.names: A2[n]=X
10000 loops, best of 3: 40.6 µs per loop

構造化された配列を埋める方が速いことに少し驚いています。

もちろん、2D 配列をすばやく埋める方法は、ブロードキャストを使用することです。

In [50]: timeit A1[:,:]=X[:,None]
10000 loops, best of 3: 29.2 µs per loop

しかし、フィールドを埋めることに対する改善はそれほど大きくありません。

構造化配列をフィールドごとに埋めることに大きな問題はありません。タプルのリストを生成して配列全体を埋めるよりも高速でなければなりません。

私は信じA1ておりA2、同一のデータバッファを持っています。たとえば、A2 のゼロのコピーを作成すると、そのデータ バッファーを に置き換えてA1's、有効な構造化配列を取得できます。

In [64]: A3=np.zeros_like(A2)
In [65]: A3.data=A1.data

したがって、構造化された配列を埋めるためのより高速な方法は、最速の 2D 塗りつぶしを行い、その後にこのdata代入を行うことです。

しかし、一般的な場合、課題は互換性のある 2D 配列を作成することです。すべてのフィールドの dtype が同じ場合は簡単です。dtype が混在していると、バイト レベルで作業する必要があります。dtypeこのようなマッピングを容易にする高度な仕様 (オフセットなど)がいくつかあります。


これで、フォーカスを Fortran オーダーに移しました。役立つ2次元配列の場合。ただし、行指向の操作を犠牲にしてそうします。

In [89]: A1=np.zeros((M,N),'f',order='F')

In [90]: timeit A1[:,:]=X[:,None]
100000 loops, best of 3: 18.2 µs per loop

少なくとも質問を最後に書き直す前に、あなたが言及していないことの1つは、この配列をどのように使用するかということです。多数の配列を名前で保存するだけの場所である場合は、単純な Python 辞書を使用できます。

In [96]: timeit D={name:X.copy() for name in dt.names}
10000 loops, best of 3: 25.2 µs per loop

これは本当にタイムテストですがX.copy()

いずれにせよ、dtype を扱う場合、Fortran の順序に相当するものはありません。reshapeswapaxes、 、ブロードキャストなどの配列操作はどれもstrides「dtype」境界を越えません。

于 2015-04-18T23:05:59.993 に答える