2

2 次元の numpy 配列があり、各要素をシーケンス内の最も近い数値に丸めたいと思います。配列には形状があり(28000, 24)ます。

たとえば、シーケンスは[0, 0.05, 0.2, 0.33, 0.5].

たとえば、オリジナル0.27は に丸められ0.330.42に丸められます。0.5

これは私が今まで使っていたものですが、もちろん二重ループで本当に遅いです。

MWE:

arr = np.array([[0.14, 0.18], [0.20, 0.27]])
new = []
sequence = np.array([0, 0.05, 0.2, 0.33, 0.5])
for i in range(len(arr)):
    row = []
    for j in range(len(arr[0])):
        temp = (arr[i][j] - sequence)**2
        row.append(list(sequence[np.where(temp == min(temp))])[0])
    new.append(row)

結果:

[[0.2000001, 0.2000001], [0.2000001, 0.33000001]]  

動機:

機械学習では、予測を行っています。結果は専門家の信頼を反映しているため、2/3 が 1 を与えた可能性があります (つまり 0.66)。したがって、このデータでは、0、0.1、0.2、0.33、0.66、0.75 などが比較的多く発生します。ただし、私の予測は 0.1724 のようなものです。この場合は 0.2 に丸めることで、多くの予測誤差を取り除くことができます。

すべての要素の丸めを最適化する方法は?

更新:メモリを事前に割り当てたので、常に追加する必要はありません。

 # new = [[0]*len(arr[0])] * len(arr), then unloading into new[i][j],
 # instead of appending 

タイミング:

Original problem: 36.62 seconds
Pre-allocated array: 15.52 seconds  
shx2 SOLUTION 1 (extra dimension): 0.47 seconds
shx2 SOLUTION 2 (better for big arrays): 4.39 seconds
Jaime's np.digitize: 0.02 seconds
4

3 に答える 3

4

処理対象の配列よりも大きくない中間ストレージを備えた別の真にベクトル化されたソリューションは、 を中心に構築できますnp.digitize

>>> def round_to_sequence(arr, seq):
...     rnd_thresholds = np.add(seq[:-1], seq[1:]) / 2
...     arr = np.asarray(arr)
...     idx = np.digitize(arr.ravel(), rnd_thresholds).reshape(arr.shape)
...     return np.take(seq, idx)
... 
>>> round_to_sequence([[0.14, 0.18], [0.20, 0.27]],
...                   [0, 0.05, 0.2, 0.33, 0.5])
array([[ 0.2 ,  0.2 ],
       [ 0.2 ,  0.33]])

更新 何が起こっているのか... 関数の最初の行は、シーケンス内のアイテム間の中間点が何であるかを把握します。この値は、丸めのしきい値です。それ未満の場合は切り捨て、それを超える場合は切り上げる必要があります。明示的にnumpy配列に変換する必要なくリストまたはタプルを受け入れるようnp.addに、より明確なものの代わりにを使用します。seq[:-1] + seq[1:]

>>> seq = [0, 0.05, 0.2, 0.33, 0.5]
>>> rnd_threshold = np.add(seq[:-1], seq[1:]) / 2
>>> rnd_threshold
array([ 0.025,  0.125,  0.265,  0.415])

次にnp.digitize、これらのしきい値で区切られたように、配列内の各項目がどのビンにあるかを調べるために使用します。np.digitizeは 1D 配列のみを受け取るため、配列の元の形状を維持するために.ravelプラスのことを行う必要があります。.reshape上限に達した項目は切り上げられるという標準的な規則をそのまま使用しrightます。キーワード引数を使用して、この動作を逆にすることができます。

>>> arr = np.array([[0.14, 0.18], [0.20, 0.27]])
>>> idx = np.digitize(arr.ravel(), seq).reshape(arr.shape)
>>> idx
array([[2, 2],
       [3, 3]], dtype=int64)

あとは、 の形状の配列を作成し、idxそのエントリを使用して、丸め先の値のシーケンスにインデックスを付けるだけです。これは で実現できますがseq[idx]、多くの場合 (常に?) 高速です (こちらを参照) np.take

>>> np.take(seq, idx)
array([[ 0.2 ,  0.2 ],
       [ 0.33,  0.33]])
于 2013-11-01T12:45:33.127 に答える