2017-10-18 6 views
3

アプリケーションの実行中に複数回呼び出されるこのコードはあります。 値を表す数値の配列をとります(value_array)。 これらは、zone_arrayで定義されたゾーンで合計される必要があります。 zone_idsは、zone_array内のすべてのゾーンのリストを表します。値がループインデックスに等しい別の配列でインデックスされた配列から値を合計するnumpyループを最適化する方法

基本的には次のようなものです:私は母集団ラスタマップを取得しました。ゾーンマップの各ゾーンにいくつの人が住んでいるか知りたいと思います。

コード:

values = np.zeros(len(zone_ids)) 
for i in zone_ids: 
    values[i] = round(np.nansum(value_array[zone_array == i]), 2) 
return values 

犯人は、forループのようですが、私はそれを排除し、同じ結果を持ってする方法を発見していません。

私はbincountで試しましたが、私は成功しませんでした。 numba jitを使用しても効果はありません。

このコードはCythonのサポートがないQgisプラグインで使用されるので、私はcythonから遠ざかりたいと思います。

テストコード:ループあたり17.5ミリ秒±

import numpy as np 


def fill_values(zone_array, value_array, zone_ids): 
    values = np.zeros(len(zone_ids)) 
    for i in zone_ids: 
     values[i] = round(np.nansum(value_array[zone_array == i]), 2) 
    return values 


def run(): 
    # 300 different zones 
    zone_ids = range(300) 
    # zone map with 300 zones 
    zone_array = (np.random.rand(2000, 2000) * 300).astype(int) 
    # value map from which we want the sum of values per zone (real map can have NaN values) 
    value_array = (np.random.rand(2000, 2000) * 10.) 
    value_array[5, 5] = np.NAN 
    fill_values(zone_array, value_array, zone_ids) 


if __name__ == '__main__': 
    run() 

1.92 sが(STD DEV平均±7つのラン、1つのループ毎の)bincountの実装で

Divakarによって示唆されるように:

203ミリ秒(STD平均±。DEV。7つの実行、1つのループごとの)ループあたり15.2ミリ秒±

+0

犯人は、forループではない -

従って、実装があろう。代わりに、問題は比較 'zone_array == i'内です。すべての2000x2000 = 4e6値は、各zone_id「i」に対して「i」と等しいかどうかチェックされなければならない。 – Chickenmarkus

+0

もし私がゾーンIDの量を減らすなら、私は速度が上がるので、forループはパフォーマンスの問題に関わっています。そして、私は 'zone_array == i'をやっていないために知っている選択肢がないので、ループに焦点を当てます。最高ののは、私が何とか 'zone_array == zone_ids'を使用してループをスキップできることです。 –

+0

'zone_array [、、、None] == zone_ids'で比較をブロードキャストすることはできますが、それでもforループにインデックスが残っており、パフォーマンスの改善はほとんどありません。 – user2699

答えて

1

bincountの直接の使用に、あなたはを持っているでしょう合計で。したがって、NaNszerosに置き換えてbincountを使用するだけです。これははるかに高速で、ベクトル化されたソリューションでなければなりません。

val_nonan = np.where(np.isnan(value_array), 0, value_array) 
out = np.round(np.bincount(zone_array.ravel(), val_nonan.ravel()),2) 
+0

これは私の問題のために働く。どうもありがとう。私は、私の結びつきは、ナノ値によって混乱しているところを試していると思います。さらに、ゾーンのサブセットの結果が必要な場合の 'values = out [zone_ids]'もあります。 –

関連する問題