10

申し訳ありませんが、回答が得られない場合に再質問するためのプロトコルがわかりません。この質問は数ヶ月前にここで尋ねられました:2次元配列のインデックスのペア間のNumpy合計

2次元のnumpy配列(MxN)と、合計したい2次元配列の各行の開始インデックスと終了インデックスを表す2つの1次元配列(Mx1)があります。私はこれを大規模な配列で行うための最も効率的な方法を探しています(できれば、現在行っているループを使用せずに)。私がやりたいことの例は次のとおりです。

>>> random.seed(1234)
>>> a = random.rand(4,4)
>>> print a
[[ 0.19151945  0.62210877  0.43772774  0.78535858]
 [ 0.77997581  0.27259261  0.27646426  0.80187218]
 [ 0.95813935  0.87593263  0.35781727  0.50099513]
 [ 0.68346294  0.71270203  0.37025075  0.56119619]]
>>> b = array([1,0,2,1])
>>> c = array([3,2,4,4])
>>> d = empty(4)
>>> for i in xrange(4):
    d[i] = sum(a[i, b[i]:c[i]]) 

>>> print d
[ 1.05983651  1.05256841  0.8588124   1.64414897]

私の問題は次の質問に似ていますが、そこで提示された解決策はあまり効率的ではないと思います。インデックスのペア間のサブ配列内の値の数の合計その質問では、同じ行の複数のサブセットの合計を見つけたいので、cumsum()使用できます。ただし、行ごとに1つの合計しか検出されないため、これが合計を計算するための最も効率的な手段ではないと思います。

4

4 に答える 4

11

編集以下の@sebergのコメントに続くOPのコードを含む、これまでのすべての回答のタイミング結果を追加しました。OPの方法が最速です。

def sliced_sum_op(a, b, c) :
    d = np.empty(a.shape[0])
    for i in xrange(a.shape[0]):
        d[i] = np.sum(a[i, b[i]:c[i]]) 
    return d

元のアレイのサイズと同等のストレージが必要になりますがnp.cumsum 、大幅なスピードブーストでそれを実行できます。

def sliced_sum(a, b, c) :
    cum = np.cumsum(a, axis=1)
    cum = np.hstack((np.zeros((a.shape[0], 1), dtype=a.dtype), cum))
    rows = np.arange(a.shape[0])
    return cum[rows, c] - cum[rows, b]

配列のサイズが小さい場合、メソッドは実際にはこれよりもわずかに高速であるため、タイミングは配列を欺くものです。しかし、numpyはすぐにそれを勝ち取ります。サイズのランダムな正方形配列のタイミングについては、以下のグラフを参照してください(n, n)

ここに画像の説明を入力してください

上記はで生成されました

import timeit
import matplotlib.pyplot as plt

n = np.arange(10, 1000, 10)
op = np.zeros(n.shape[0])
me = np.zeros(n.shape[0])
th = np.zeros(n.shape[0])
jp = np.zeros(n.shape[0])
for j, size in enumerate(n) :
    a = np.random.rand(size, size)
    b, c = indices = np.sort(np.random.randint(size + 1,
                                               size=(2, size)), axis=0)
    np.testing.assert_almost_equal(sliced_sum_op(a, b, c),
                                   sliced_sum(a, b, c))
    np.testing.assert_almost_equal(sliced_sum_op(a, b, c),
                                   sum_between2(a, b, c))
    np.testing.assert_almost_equal(sliced_sum_op(a, b, c),
                                   sum_between_mmult(a, b, c))

    op[j] = timeit.timeit('sliced_sum_op(a, b, c)',
                          'from __main__ import sliced_sum_op, a, b, c',
                          number=10)
    me[j] = timeit.timeit('sliced_sum(a, b, c)',
                          'from __main__ import sliced_sum, a, b, c',
                          number=10)
    th[j] = timeit.timeit('sum_between2(a, b, c)',
                          'from __main__ import sum_between2, a, b, c',
                          number=10)
    jp[j] = timeit.timeit('sum_between_mmult(a, b, c)',
                          'from __main__ import sum_between_mmult, a, b, c',
                          number=10)
plt.subplot(211)
plt.plot(n, op, label='op')
plt.plot(n, me, label='jaime')
plt.plot(n, th, label='thorsten')
plt.plot(n, jp, label='japreiss')
plt.xlabel('n')
plt.legend(loc='best')
plt.show()
于 2013-01-14T17:36:18.013 に答える
3

@Jaimeの答えは好きですが、別のアプローチがあります。問題を行列の乗算で再キャストできます。

すべて1のベクトルを掛けるaと、出力ベクトルの各要素には、の対応する行の合計が含まれますa。必要なものを取得するにdは、各行から除外されている要素をマスクしてから、すべての要素のベクトルを掛けてを取得しますd

def sum_between_mmult(ar, b, c):
    copy = np.copy(ar)
    nrows = ar.shape[0]
    ncols = ar.shape[1]
    for i in range(nrows):
        copy[i, :b[i]] = 0
        copy[i, c[i]:] = 0
    onevec = np.ones(ncols)
    return np.dot(copy, onevec)

@Jaimeと同じように、マトリックスサイズが大きい場合にのみスピードアップが見られました。ある種の凝った索引付けのトリックがforループを取り除き、より高速化できると私は感じています。元の配列が必要ない場合は、コピーを作成する代わりに上書きすることができますが、これは私のテストではあまり高速化されませんでした。

于 2013-01-14T19:23:40.590 に答える
2

結果を計算する別の方法があります。これはループを使用せずに機能しますが、ループアプローチよりも実際には高速ではありません。

import time
import numpy as np

def sum_between1(ar, idc_l, idc_u):
    d = np.empty(ar.shape[0])
    for i in xrange(ar.shape[0]):
        d[i] = sum(ar[i, b[i]:c[i]]) 
    return d

def sum_between2(ar, idc_l, idc_u):
    indices = np.arange(ar.shape[1]).reshape(1,-1).repeat(ar.shape[0], axis=0)
    lower = idc_l.reshape(-1,1).repeat(ar.shape[1], axis=1)    
    upper = idc_u.reshape(-1,1).repeat(ar.shape[1], axis=1)
    mask = ~((indices>=lower) * (indices<upper))
    masked = np.ma.MaskedArray(ar, mask)
    return masked.sum(axis=1)

np.random.seed(1234)
a = np.random.rand(4,4)
print a
b = np.array([1,0,2,1])
c = np.array([3,2,4,4])

t0 = time.time()
for i in range(100000):
    d1 = sum_between1(a,b,c)
print "sum_between1: %.3f seconds" % (time.time()-t0)
print d1

t0 = time.time()
for i in range(100000):
    d2 = sum_between2(a,b,c)
print "sum_between2: %.3f seconds" % (time.time()-t0)
print d2

私にとっての出力は

  [[ 0.19151945  0.62210877  0.43772774 ...,  0.92486763  0.44214076
   0.90931596]
 [ 0.05980922  0.18428708  0.04735528 ...,  0.53585166  0.00620852
   0.30064171]
 [ 0.43689317  0.612149    0.91819808 ...,  0.18258873  0.90179605
   0.70652816]
 ..., 
 [ 0.70568819  0.76402889  0.34460786 ...,  0.6933128   0.07778623
   0.4040815 ]
 [ 0.51348689  0.80706629  0.09896631 ...,  0.91118062  0.87656479
   0.96542923]
 [ 0.20231131  0.72637586  0.57131802 ...,  0.5661444   0.14668441
   0.09974442]]
sum_between1: 2.263 seconds
[ 1.05983651  0.24409631  1.54393475  2.27840642  1.65049179  1.86027107
  0.74002457  0.91248001  1.29180203  1.03592483  0.30448954  0.78028893
  1.15511632  1.74568981  1.0551406   1.73598504  1.32397106  0.22902658
  0.77533999  2.11800627  1.09181484  0.92074516  1.04588589  2.07584895
  1.13615918  1.33172081  1.41323751  2.01996291  1.69677797  0.57592999
  1.18049304  1.13052798  0.90715138  0.63876336  1.76712974  1.15138181
  0.29005541  1.46971707  0.57149804  1.8816212 ]
sum_between2: 1.817 seconds
[1.05983651005 0.244096306594 1.54393474534 2.27840641818 1.65049178537
 1.86027106627 0.740024568268 0.91248000774 1.29180203183 1.03592482812
 0.304489542783 0.78028892993 1.1551163203 1.74568980609 1.05514059758
 1.73598503833 1.32397105753 0.229026581839 0.77533999391 2.11800626878
 1.09181484127 0.92074516366 1.04588588779 2.07584895325 1.13615918351
 1.33172081033 1.41323750936 2.01996291037 1.69677797485 0.575929991717
 1.18049303662 1.13052797976 0.907151384823 0.638763358104 1.76712974497
 1.15138180543 0.290055405809 1.46971707447 0.571498038664 1.88162120474]

誰か他の人が私のアプローチを改善してより速くする方法を考えているかもしれないので、私はこの答えを投稿します。

于 2013-01-14T16:06:17.787 に答える
1

これは約25%高速です。

def zip_comp(a,b,c):
    return [np.sum(aa[bb:cc]) for aa, bb, cc in zip(a,b,c)]

以前のコードをリファクタリングして、スライスの2つのリストを生成する代わりに、単一のバイナリ2D配列を生成できる場合は、@japreissメソッドの後半などを使用して大幅な向上を実現できます。これらすべてのメソッドの速度低下は、クレイジーなインデックス作成をいじり回すのに費やされた時間です。

Jaimeのコードを使用した速度比較:

ここに画像の説明を入力してください

于 2013-01-16T02:49:40.933 に答える