4

numpy einsum を使用して、形状 (3,N) の列ベクトル pts の配列とそれ自体の内積を計算し、形状 (N,N) の行列 dotps をすべての内積で計算しています。これは私が使用するコードです:

dotps = np.einsum('ij,ik->jk', pts, pts)

これは機能しますが、主対角線より上の値のみが必要です。すなわち。対角線を除いた結果の上三角部分。これらの値のみを einsum で計算することは可能ですか? または、行列全体を計算するために einsum を使用するよりも高速な他の方法はありますか?

私のpts配列は非常に大きくなる可能性があるため、必要な値だけを計算できれば、計算速度が2倍になります.

4

1 に答える 1

5

関連する列をスライスしてから使用できますnp.einsum-

R,C = np.triu_indices(N,1)
out = np.einsum('ij,ij->j',pts[:,R],pts[:,C])

サンプルラン -

In [109]: N = 5
     ...: pts = np.random.rand(3,N)
     ...: dotps = np.einsum('ij,ik->jk', pts, pts)
     ...: 

In [110]: dotps
Out[110]: 
array([[ 0.26529103,  0.30626052,  0.18373867,  0.13602931,  0.51162729],
       [ 0.30626052,  0.56132272,  0.5938057 ,  0.28750708,  0.9876753 ],
       [ 0.18373867,  0.5938057 ,  0.84699103,  0.35788749,  1.04483158],
       [ 0.13602931,  0.28750708,  0.35788749,  0.18274288,  0.4612556 ],
       [ 0.51162729,  0.9876753 ,  1.04483158,  0.4612556 ,  1.82723949]])

In [111]: R,C = np.triu_indices(N,1)
     ...: out = np.einsum('ij,ij->j',pts[:,R],pts[:,C])
     ...: 

In [112]: out
Out[112]: 
array([ 0.30626052,  0.18373867,  0.13602931,  0.51162729,  0.5938057 ,
        0.28750708,  0.9876753 ,  0.35788749,  1.04483158,  0.4612556 ])

さらに最適化 -

アプローチの時間を計って、パフォーマンスに関して改善の余地があるかどうかを見てみましょう。

In [126]: N = 5000

In [127]: pts = np.random.rand(3,N)

In [128]: %timeit np.triu_indices(N,1)
1 loops, best of 3: 413 ms per loop

In [129]: R,C = np.triu_indices(N,1)

In [130]: %timeit np.einsum('ij,ij->j',pts[:,R],pts[:,C])
1 loops, best of 3: 1.47 s per loop

メモリの制約内にとどまると、最適化について多くのことができるようには見えませんnp.einsum。では、焦点を に移しましょうnp.triu_indices

についてN = 4は、次のとおりです。

In [131]: N = 4

In [132]: np.triu_indices(N,1)
Out[132]: (array([0, 0, 0, 1, 1, 2]), array([1, 2, 3, 2, 3, 3]))

規則的なパターンを作成しているように見えますが、シフトするようなものです。3これは、それらと5位置にシフトがある累積合計で書くことができます。一般的に考えると、次のようにコーディングすることになります-

def triu_indices_cumsum(N):

    # Length of R and C index arrays
    L = (N*(N-1))/2

    # Positions along the R and C arrays that indicate 
    # shifting to the next row of the full array
    shifts_idx = np.arange(2,N)[::-1].cumsum()

    # Initialize "shift" arrays for finally leading to R and C
    shifts1_arr = np.zeros(L,dtype=int)
    shifts2_arr = np.ones(L,dtype=int)

    # At shift positions along the shifts array set appropriate values, 
    # such that when cumulative summed would lead to desired R and C arrays.
    shifts1_arr[shifts_idx] = 1
    shifts2_arr[shifts_idx] = -np.arange(N-2)[::-1]

    # Finall cumsum to give R, C
    R_arr = shifts1_arr.cumsum()
    C_arr = shifts2_arr.cumsum()
    return R_arr, C_arr

色々と時間を計りましょうN's

In [133]: N = 100

In [134]: %timeit np.triu_indices(N,1)
10000 loops, best of 3: 122 µs per loop

In [135]: %timeit triu_indices_cumsum(N)
10000 loops, best of 3: 61.7 µs per loop

In [136]: N = 1000

In [137]: %timeit np.triu_indices(N,1)
100 loops, best of 3: 17 ms per loop

In [138]: %timeit triu_indices_cumsum(N)
100 loops, best of 3: 16.3 ms per loop

したがって、まともなように見えますがN's、カスタマイズされた cumsum ベースtriu_indicesは一見の価値があるかもしれません!

于 2016-04-13T13:40:01.140 に答える