2017-10-18 82 views
3

我有這段在應用程序運行期間多次調用的代碼。 它需要一個表示值的數組(數組array_value)。 這些應該在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循環,但我還沒有找到一個方法來消除它,並有相同的結果。

我嘗試了與計數,但我沒有成功。 使用numba jit也沒有效果。

我想遠離cython,因爲此代碼將用於沒有cython支持的Qgis插件。

測試代碼:

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小號±每個環路17.5毫秒(平均值±標準偏差7點運行時,1個循環的每一個。)

隨着bincount的執行由Divakar的建議:

203毫秒±15.2毫秒每環(平均±標準。開發7點運行,1環的每一個)

+0

的罪魁禍首不是for循環。相反,問題在於比較'zone_array == i'。對於每個zone_id'i',必須檢查所有2000x2000 = 4e6的值是否等於「i」。 – Chickenmarkus

+0

如果我減少區域ID的數量我得到一個速度增加,所以for循環仍然涉及到性能問題。因爲我沒有別的選擇,我知道沒有做'zone_array ==我'我專注於循環。最好的是,我可以以某種方式使用'zone_array == zone_ids'並跳過循環。 –

+0

您可以使用'zone_array [:,:,] == zone_ids'廣播比較,但仍然會在for循環中留下索引,並且不會提高性能。 – user2699

回答

1

隨着bincount直接使用,你就必須NaNs在求和中。因此,您可以簡單地將NaNs替換爲zeros並使用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

這適用於我的問題。非常感謝。我想我的帳號嘗試在哪裏被nan值弄亂。此外'values = out [zone_ids]'用於您想要區域子集結果的情況。 –