12

numpy配列を(派手なインデックスを介して)スライスするための私のコードは非常に遅いです。これは現在、プログラムのボトルネックです。

a.shape
(3218, 6)

ts = time.time(); a[rows][:, cols]; te = time.time(); print('%.8f' % (te-ts));
0.00200009

行列aの行'行'と列'列'のサブセットで構成される配列を取得するための正しいnumpy呼び出しは何ですか?(実際、この結果の転置が必要です)

4

4 に答える 4

20

Jaime と TheodrosZelleke による優れた回答を要約し、いくつかのコメントを混ぜてみましょう。

  1. 高度な (派手な) インデックス作成では、ビューではなく、常にコピーが返されます。
  2. a[rows][:,cols]2 つの複雑なインデックス作成操作を意味するため、中間コピーa[rows]が作成されて破棄されます。便利で読みやすいですが、あまり効率的ではありません。[:,cols]さらに、通常、C-cont から Fortran 連続コピーを生成することに注意してください。ソース。
  3. a[rows.reshape(-1,1),cols]rows.reshape(-1,1)とが意図した結果の形にcolsブロードキャストれるという事実に基づく、単一の高度なインデックス付け式です。
  4. 一般的な経験では、フラット化された配列でのインデックス作成は、派手なインデックス作成よりも効率的である可能性があるため、別のアプローチは次のとおりです。

    indx = rows.reshape(-1,1)*a.shape[1] + cols
    a.take(indx)
    

    また

    a.take(indx.flat).reshape(rows.size,cols.size)
    
  5. 効率は、メモリ アクセス パターンと、開始配列が C カウントか Fortran 連続かによって異なるため、実験が必要です。

  6. ファンシーなインデックス作成は本当に必要な場合にのみ使用 a[rstart:rstop:rstep, cstart:cstop:cstep]てください。

于 2013-01-18T11:20:41.180 に答える
15

驚いたことに、最初の線形 1D インデックスを計算する一種の長い式は、質問で提示された連続した配列のインデックス付けよりも50% 以上高速です。

(a.ravel()[(
   cols + (rows * a.shape[1]).reshape((-1,1))
   ).ravel()]).reshape(rows.size, cols.size)

更新: OP は初期配列の形状の説明を更新しました。更新されたサイズにより、スピードアップは99%を超えています。

In [93]: a = np.random.randn(3218, 1415)

In [94]: rows = np.random.randint(a.shape[0], size=2000)

In [95]: cols = np.random.randint(a.shape[1], size=6)

In [96]: timeit a[rows][:, cols]
10 loops, best of 3: 186 ms per loop

In [97]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 1.56 ms per loop

最初の答え: ここにトランスクリプトがあります:

In [79]: a = np.random.randn(3218, 6)
In [80]: a.shape
Out[80]: (3218, 6)

In [81]: rows = np.random.randint(a.shape[0], size=2000)
In [82]: cols = np.array([1,3,4,5])

時間方式 1:

In [83]: timeit a[rows][:, cols]
1000 loops, best of 3: 1.26 ms per loop

時間方式 2:

In [84]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 568 us per loop

結果が実際に同じであることを確認します。

In [85]: result1 = a[rows][:, cols]
In [86]: result2 = (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)

In [87]: np.sum(result1 - result2)
Out[87]: 0.0
于 2013-01-17T20:56:27.413 に答える
6

ファンシーなインデックス作成とブロードキャストを使用してスライスすると、速度が向上します。

from __future__ import division
import numpy as np

def slice_1(a, rs, cs) :
    return a[rs][:, cs]

def slice_2(a, rs, cs) :
    return a[rs[:, None], cs]

>>> rows, cols = 3218, 6
>>> rs = np.unique(np.random.randint(0, rows, size=(rows//2,)))
>>> cs = np.unique(np.random.randint(0, cols, size=(cols//2,)))
>>> a = np.random.rand(rows, cols)
>>> import timeit
>>> print timeit.timeit('slice_1(a, rs, cs)',
                        'from __main__ import slice_1, a, rs, cs',
                        number=1000)
0.24083110865
>>> print timeit.timeit('slice_2(a, rs, cs)',
                        'from __main__ import slice_2, a, rs, cs',
                        number=1000)
0.206566124519

パーセンテージで考えると、何かを 15% 高速化することは常に良いことですが、私のシステムでは、配列のサイズに対して、これはスライスを行うのに 40 us 少なくて済みます。 240usがボトルネックになります。

于 2013-01-17T20:15:30.223 に答える