3

私は、ある範囲 (例: 15 年) の間、ndimage.filters.convolve配列 (例: array1) をたたみ込むために使用されるいくつかのコードを書きました。その後、結果の配列 (例: array2) は、ランダムに生成された数値の上にあり、別の配列 ( array3 に値 1 が割り当てられると、毎年カウントアップされ、最終的に特定の値 (例: 5) に達すると、array1 はこの場所で更新されます。

これが少し混乱している場合は申し訳ありません。実際に を使用してスクリプトを動作させましたnumpy.where(boolean expression, value, value)が、複数の式 (例: where array2 == 1 and array3 == 0) が必要な場合は、for ループを使用して配列内の各値を反復処理しました。これはここの例ではうまく機能しますが、配列をより大きな配列に置き換えると (完全なスクリプトは GIS グリッドをインポートして配列に変換します)、この for ループは毎年の処理に数分かかります。モデルを 60 年以上にわたって 1000 回実行する必要があるため、これらの配列を処理するためのより効率的な方法を見つける必要があります。

numpy.where 内で複数の式を使用しようとしましたが、それを機能させる方法がわかりませんでした。また、配列を一緒に圧縮するために zip(array) を試しましたが、配列要素のタプルが作成されたため、それらを更新できませんでした。

スクリプトのコピーを添付しました。前述のとおり、必要に応じて正確に機能します。ただし、これをより効率的に行う必要があります。誰かが何か提案があれば、それは素晴らしいでしょう。これは Python に関する私の最初の投稿なので、私はまだ自分自身を初心者だと考えています。

import numpy as np
from scipy import ndimage
import random
from pylab import *

###################### FUNCTIONS ###########################

def convolveArray1(array1, kern1):

    newArray = ndimage.filters.convolve(array1, kern1, mode='constant')

    return newArray


######################## MAIN ##############################

## Set the number of years
nYears = range(1,16)

## Cretae array1
array1 = np.zeros((10,10), dtype=np.int) # vegThreshMask

# Add some values to array1
array1[[4,4],[4,5]] = 8
array1[5,4] = 8
array1[5,5] = 8

## Create kerna; array
kernal = np.ones((3,3), dtype=np.float32)

## Create an empty array to be used as counter
array3 = np.zeros((10,10), dtype=np.int)

## iterate through nYears
for y, yea in enumerate(nYears):

    # Create a random number for the year
    randNum = randint(7, 40)
    print 'The random number for year %i is %i' % (yea, randNum)
    print

    # Call the convolveArray function
    convArray = convolveArray1(array1, kernal)

    # Update array2 where it is greater than the random number    
    array2 = np.where(convArray > randNum, 1, 0)
    print 'Where convArray > randNum in year %i' % (yea)
    print array2
    print 

    # Iterate through array2 
    for a, ar in enumerate(array2):
        for b, arr in enumerate(ar):
            if all(arr == 1 and array3[a][b] == 0):
                array3[a][b] = 1
            else:
                if array3[a][b] > 0:
                    array3[a][b] = array3[a][b] + 1
            if array3[a][b] == 5:
                array1[a][b] = 8

    # Remove the initial array (array1) from the updated array3   
    array3 = np.where(array1 > 0, 0, array3)
    print 'New array3 after %i years' % (yea)
    print '(Excluding initial array)'
    print array3
    print    

print 'The final output of the initial array'
print array1
4

2 に答える 2

3

ブロードキャストを使い始めると、かなりのスピードアップが得られると思います。たとえば、あなたの行から始めて# Iterate through array2、明示的なループを削除し、変更したい変数を単純にブロードキャストできます。わかりやすくするために、AX代わりに使用していることに注意してください。arrayX

# Iterate through A2

idx  = (A2==1) & (A3==0)
idx2 = (~idx)  & (A3>0)
A3[idx ]  = 1
A3[idx2] += 1
A1[A3==5] = 8

さらに、このスタイルに慣れると、インデックス ( yourabhere ) を明示的に処理しないため、コードの明瞭さが大幅に向上します。

苦労する価値はありますか?

上記のコードを試した後、OPに速度テストを行うように依頼しました:

ループの変更を実装する場合は、実際のコードでのスピードアップを教えてください。与えられたアドバイスが単なる美化された構文糖衣なのか、それとも注目に値する効果があるのか​​を知ることは役に立ちます。

テスト後、応答は大幅に 40 倍高速化されました。単純なマスクが実行されている連続したデータの大規模な配列を処理する場合、numpyネイティブの python リストよりもはるかに優れた代替手段です。

于 2012-03-01T15:00:02.830 に答える
1

np.whereのような式を使用して複数の条件を使用しようとしていたようarray1 > 0 and array2 < 0です。ここに記載されているように、Python でブール演算が機能する方法が原因で、これは機能しません。最初にが評価され、メソッドarray1 > 0を使用してブール値に変換されます( Python 3 で名前が変更されました)。配列をブール値に変換する独自の便利な方法はなく、現在、ブール演算子の動作をオーバーライドする方法はありません (ただし、これは将来のバージョンで議論されていると思います) 。例外を発生させます。代わりに、期待どおりの動作をする、、およびを使用できます。__nonzero____bool__ndarray.__nonzero__np.logical_andnp.logical_ornp.logical_not

ただし、これによりどの程度の速度向上が得られるかはわかりません。ループ内で多数の配列インデックス操作を実行することになった場合は、 cythonを調べる価値があるかもしれません。これを使用すると、配列操作を C 拡張に移動することで簡単に高速化できます。

于 2012-03-01T12:31:04.733 に答える