8

大きな(1,000,000 x 3,000)ブール値のnumpy配列全体でインデックス付きの重みの合計を計算したいと思います。大きなブール配列はめったに変更されませんが、重みはクエリ時に発生するため、大きな配列全体をコピーしたり、小さな重みの配列を大きな配列のサイズに拡張したりせずに、非常に高速に回答する必要があります。

結果は、1,000,000エントリの配列になります。各エントリには、その行のTrue値に対応する重み配列エントリの合計が含まれます。

マスクされた配列の使用を検討しましたが、大きなブール配列のサイズの重み配列を作成する必要があるようです。

以下のコードは正しい結果を示していますが、乗算ステップ中にそのコピーを購入する余裕はありません。値の配列はブール値であるため、乗算は必要ありませんが、少なくともブロードキャストを適切に処理します。

私はnumpyに不慣れで、それを愛していますが、この特定の問題のためにそれをあきらめようとしています。私は、Pythonでループするものから離れることを知るのに十分な数を学びました。

次のステップは、このルーチンをCで作成することです(ちなみに、バイトではなくビットを使用してメモリを節約できるという追加の利点があります)。

あなたのだらしない教祖の一人が私をcythonから救うことができない限り?

from numpy import array, multiply, sum

# Construct an example values array, alternating True and False.
# This represents four records of three attributes each:
#    array([[False,  True, False],
#           [ True, False,  True],
#           [False,  True, False],
#           [ True, False,  True]], dtype=bool)
values = array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3))

# Construct example weights, one for each attribute:
#    array([1, 2, 3])
weights = array(range(1, 4))

# Create expensive NEW array with the weights for the True attributes.
# Broadcast the weights array into the values array.
#    array([[0, 2, 0],
#           [1, 0, 3],
#           [0, 2, 0],
#           [1, 0, 3]])
weighted = multiply(values, weights)

# Add up the weights:
#    array([2, 4, 2, 4])
answers = sum(weighted, axis=1)

print answers

# Rejected masked_array solution is too expensive (and oddly inverts
# the results):
masked = numpy.ma.array([[1,2,3]] * 4, mask=values)
4

4 に答える 4

4

ドット積(または内積)はあなたが望むものです。これにより、サイズの行列とm×n長さのベクトルを取得し、nそれらを乗算して長さのベクトルを生成できますm。ここで、各エントリは、行列の行とそのベクトルのエントリの重み付き合計です。

Numpyはこれをarray1.dot(array2)(またはnumpy.dot(array1, array2)古いバージョンで)実装します。例えば:

from numpy import array

values = array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3))

weights = array(range(1, 4))

answers = values.dot(weights)
print answers
# output: [ 2 4 2 4 ]

timeit(ただし、モジュールを使用して、これをベンチマークする必要があります。)

于 2012-04-19T01:17:31.310 に答える
3

dbauppの答え​​は正しいと思われます。しかし、多様性のために、メモリを節約する別のソリューションがあります。numpyこれは、同等の機能が組み込まれていない操作でも機能します。

>>> values = numpy.array([(x % 2) for x in range(12)], dtype=bool).reshape((4,3))
>>> weights = numpy.array(range(1, 4))
>>> weights_stretched = numpy.lib.stride_tricks.as_strided(weights, (4, 3), (0, 8))

numpy.lib.stride_tricks.as_strided素晴らしい小さな機能です!これにより、小さな配列がはるかに大きな配列を模倣できるようにする値shapeを指定できます。strides観察してください-ここには実際には4つの行はありません。それはそのように見えます:

>>> weights_stretched[0][0] = 4
>>> weights_stretched 
array([[4, 2, 3],
       [4, 2, 3],
       [4, 2, 3],
       [4, 2, 3]])

したがって、巨大な配列をに渡す代わりにMaskedArray、小さい配列を渡すことができます。(しかし、すでにお気づきのように、numpyマスキングは予想とは逆の方法で機能します。真実のマスクは明らかにするのではなく、values反転したものを保存する必要があります。)ご覧のとおりMaskedArray、データはコピーされません。何が入っているかを反映しているだけですweights_stretched

>>> masked = numpy.ma.MaskedArray(weights_stretched, numpy.logical_not(values))
>>> weights_stretched[0][0] = 1
>>> masked
masked_array(data =
 [[-- 2 --]
 [1 -- 3]
 [-- 2 --]
 [1 -- 3]],
      mask =
 [[ True False  True]
 [False  True False]
 [ True False  True]
 [False  True False]],
      fill_value=999999)

これで、合計に渡すことができます。

>>> sum(masked, axis=1)
masked_array(data = [2 4 2 4],
      mask = [False False False False],
      fill_value=999999)

numpy.dot以上を1,000,000x30アレイに対してベンチマークしました。これは、比較的最新のMacBook Pro(numpy.dotis dot1; mine is dot2)での結果です。

>>> %timeit dot1(values, weights)
1 loops, best of 3: 194 ms per loop
>>> %timeit dot2(values, weights)
1 loops, best of 3: 459 ms per loop

ご覧のとおり、組み込みのnumpyソリューションの方が高速です。しかしstride_tricks、それでも知っておく価値があるので、これは残しておきます。

于 2012-04-19T02:07:20.370 に答える
1

これはあなたのために働きますか?

a = np.array([sum(row * weights) for row in values])

これは値sum()をすぐに合計するために使用されるrow * weightsため、すべての中間値を格納するためのメモリは必要ありません。次に、リスト内包はすべての値を収集します。

「Pythonでループする」ものはすべて避けたいとおっしゃいました。これは、明示的なPythonループではなく、少なくともPythonのCガッツでループを実行しますが、コンパイルされたCまたはFortranを使用するため、NumPyソリューションほど高速にはなりません。

于 2012-04-19T01:16:47.740 に答える
0

私はあなたがそのような何かのためにnumpyを必要としないと思います。そして、1000000x3000は巨大な配列です。これはRAMに収まらない可能性があります。

私はそれをこのようにします:

データが元々テキストファイルにあるとしましょう。

False,True,False
True,False,True
False,True,False
True,False,True

私のコード:

weight = range(1,4)    
dicto = {'True':1, 'False':0}

with open ('my_data.txt') as fin:

    a = sum(sum(dicto[ele]*w for ele,w in zip(line.strip().split(','),weight)) for line in fin)

結果:

>>> a
12

編集:

初めて質問を少し読み間違えて、すべてをまとめたと思います。OPが求める正確なソリューションを提供するソリューションは次のとおりです。

weight = range(1,4)
dicto = {'True':1, 'False':0}

with open ('my_data.txt') as fin:

    a = [sum(dicto[ele]*w for ele,w in zip(line.strip().split(','),weight)) for line in fin]

結果:

>>> a
[2, 4, 2, 4]
于 2012-04-19T01:20:40.893 に答える