3

にはNumPyd x n配列と長さAのリストがあり、行列の各列をどこに配置するかを記述しています。アイデアは、行列の列は、 の対応する値が である のすべての列の であるということです。LnABiBsumALi

forループでこれを行うことができます:

A = np.arange(15).reshape(3,5)
L = [0,1,2,1,1]
n_cols = 3
B = np.zeros((len(A), n_cols)) 
# assume I know the desired number of columns, 
# which is also one more than the maximum value of L
for i, a in enumerate(A.T):
    B[:, L[i]] += a

A配列をスライスすることによって(または他の方法でベクトル化された方法で)これを行う方法があるかどうか疑問に思っていましたか?

4

2 に答える 2

3

それらの列を選択するためにA使用して、列を合計削減/折りたたみます。Lまた、要素の一意性に基づいて出力配列の列を更新していますL

np.add.reduceatしたがって、次のようにベクトル化されたソリューションに使用できます-

sidx = L.argsort()
col_idx, grp_start_idx = np.unique(L[sidx],return_index=True)
B_out = np.zeros((len(A), n_cols))
B_out[:,col_idx] = np.add.reduceat(A[:,sidx],grp_start_idx,axis=1)

実行時テスト -

In [129]: def org_app(A,n_cols):
     ...:     B = np.zeros((len(A), n_cols)) 
     ...:     for i, a in enumerate(A.T):
     ...:         B[:, L[i]] += a
     ...:     return B
     ...: 
     ...: def vectorized_app(A,n_cols):
     ...:     sidx = L.argsort()
     ...:     col_idx, grp_start_idx = np.unique(L[sidx],return_index=True)
     ...:     B_out = np.zeros((len(A), n_cols))
     ...:     B_out[:,col_idx] = np.add.reduceat(A[:,sidx],grp_start_idx,axis=1)
     ...:     return B_out
     ...: 

In [130]: # Setup inputs with an appreciable no. of cols & lesser rows
     ...: # so as that memory bandwidth to work with huge number of 
     ...: # row elems doesn't become the bottleneck
     ...: d,n_cols = 10,5000
     ...: A = np.random.rand(d,n_cols)
     ...: L = np.random.randint(0,n_cols,(n_cols,))
     ...: 

In [131]: np.allclose(org_app(A,n_cols),vectorized_app(A,n_cols))
Out[131]: True

In [132]: %timeit org_app(A,n_cols)
10 loops, best of 3: 33.3 ms per loop

In [133]: %timeit vectorized_app(A,n_cols)
100 loops, best of 3: 1.87 ms per loop

行数が の列数に匹敵するようになるとA、ベクトル化されたアプローチに必要な高いメモリ帯域幅が、それによる顕著なスピードアップを相殺します。

于 2016-09-11T09:08:47.260 に答える
1

``B` でのこの反復は同じですか (テストされていません)?

 for I in range(B.shape[1]):
       B[:, I] = A[:, L==I].sum(axis=1)

ループ回数が少なくなります。しかし、より重要なことは、他のベクトル化の洞察を与える可能性があることです。

(編集)テストでは、これは機能しますが、はるかに遅くなります。

+========

scipy.sparse1 の行列で行列積を使用して列の合計を実行します。ここでも同じことができますか?右側Cの列に 1 を含む配列を作成します

def my2(A,L):
    n_cols = L.shape[0]
    C = np.zeros((n_cols,n_cols),int)
    C[np.arange(n_cols), L] = 1
    return A.dot(C)

これはループよりも 7 倍高速で、@Divakars reduceatコードよりもわずかに高速です。

==========

In [126]: timeit vectorized_app(A,L)
The slowest run took 8.16 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 99.7 µs per loop
In [127]: timeit val2 = my2(A,L)
The slowest run took 10.46 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 81.6 µs per loop
In [128]: timeit org1(A,n_cols)
1000 loops, best of 3: 623 µs per loop
In [129]: d,n_cols
Out[129]: (10, 100)
于 2016-09-11T08:59:19.957 に答える