2017-09-29 5 views
4

私は同じ長さの2つのリストを持っています。私は1つのリストの条件をチェックしたい。条件が真であれば、他のリストで非常にメモリ/処理が集中した関数を実行します。リスト内包で3万回の致命的な機能を実行することを避けるにはどうすればよいですか?

私の最初の試みはほどだった:

records = [(a, deadly_func(b)) for a, b in zip(listA, listB) if a == "condition"] 

これはすぐに私のデスクトップ上のすべてのメモリを割り当てられ、私はそれを殺した前に、しばらくの間に行ってきました。明らかに、listBのすべての30,000項目についてdeadly_func(b)を実行しましたが、リストBを約30項目までフィルタリングするためにif文を使用することが意図されていました。

私は作業バージョンを作ることができた:なぜ私の最初の試みはうまくいきませんでした

records = [(a, i) for a, i in zip(listA, range(len(listB)) if a == "condition"] 
records = [(a, deadly_func(listB[i]) for a, i in records] 

?この作品を作るためのより無邪気な方法がありますか?


編集:いただきありがとうございます。

import shapefile, shapely.geometry as shpgeo 

lat = 42.3968243 
lon = -71.0313479 

sf = shapefile.Reader("/opt/ziplfs/tl_2014_us_zcta510.shp") 

records = [(r[0], shpgeo.shape(s.__geo_interface__)) for r, s in zip(sf.records(), sf.shapes()) if haversine(lon, lat, float(r[8]), float(r[7])) < 10] 

半正矢()LATと長い二対のを取って、ユーザ製半正矢関数であり、キロメートルに距離を返す:ここで働いてなかった両バージョン

ための実際のコードです。

from math import sqrt, sin, cos, radians, asin 
def haversine(lon1, lat1, lon2, lat2): 
    """ 
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees). Return is in kilometers 
    """ 
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2]) 

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles 
    return c * r 

シェープファイル(「tl_2014_us_zcta510.shp」)は国勢調査局から、米国内のすべてのジップコードです。あなたが本当にシェイプファイルを愛している場合はhereをダウンロードし、ハードドライブに800 MBのファイルを用意してください。

このスクリプトでは、アメリカのすべての郵便番号を表すタプルのリストを、チェルシー(マサチューセッツ州)から10km以内のセントロイドで返します。作業バージョンについては

、持つレコードラインを置き換える:

records = [(r[0], i) for r, i in zip(sf.records(), range(len(sf.records()))) if haversine(lon, lat, float(r[8]), float(r[7])) < 10] 
shapes = [shpgeo.shape(sf.shape(i).__geo_interface__) for r, i in records] 

私はいくつかのタイミングテストをしました。 「非稼働」バージョン:

$ python test.py 
Time Elapsed: 0:00:14.221533 
$ python test.py 
Time Elapsed: 0:00:14.637827 
$ python test.py 
Time Elapsed: 0:00:14.253425 

と作業バージョン:

$ python test.py 
Time Elapsed: 0:00:01.887987 
$ python test.py 
Time Elapsed: 0:00:01.886635 
$ python test.py 
Time Elapsed: 0:00:01.982547 

たぶんと言うあたりの「致命的」、しかし重要ではありませんあなたは30K回繰り返します。

+0

をあなたは一度も致命的な機能を実行したいと思う理由:

また、あなたは追加することにより、デバッグのために最初の1000にご入力のサイズを小さくすることができますか? –

+0

@JohnColemanは 'a!=" condition "'の場合にのみ関数が致命的であると言うことができます: – Dekel

+0

リストの種類の例がありますか?私は 'print'といくつかの小さなコンテナを使ってこれを複製しようとしました。 – mwchase

答えて

6

No repro?このコードは、listBのすべての要素にでなく、を実行します。deadly_funcです。対応listA値がTrueあるものだけ:更新された質問に基づいて

listA = [True, False, True, False] 
listB = [1, 2, 3, 4] 

def deadly_func(x): 
    print("Called with {}".format(x)) 
    return x 

print([(a, deadly_func(b)) for a, b in zip(listA, listB) if a]) 

# Output: 
# Called with 1 
# Called with 3 
# [(True, 1), (True, 3)] 

EDIT

、私の推測では、sf.shapes()が高価な部分であるということです。したがって、必要な要素のサブセットのみにsf.shape(i)を呼び出す方が効率的です。

私の推測が正しければ、これはトリックを行う必要があります。

records = [(r[0], shpgeo.shape(sf.shape(i).__geo_interface__)) for i, r in enumerate(sf.records()) if haversine(lon, lat, float(r[8]), float(r[7])) < 10] 

(。もちろん、これはあなたがすでにやったことほとんどである)

+0

ところで、これは本当に質問に対する答えではないことが分かっています...しかし、回答を投稿せずにコードを共有するのは難しいです。元の問題が明らかになったら、これを喜んで削除します。 – smarx

+1

答えとしては大丈夫だと思います。最初のコードが30,000回実行されるという仮説は間違っているとOPから伝えられています。それは重要ですので、+1してください。 –

+0

はい、この機能が何度も実行されていないことを実証してくれてありがとう。元の質問を実際のコードで更新しますが、800 MBの米国国勢調査局シェイプファイルが含まれているため、すぐに複製できません。 – kingledion

1

あなたはリストの内包に問題がある場合は、時にはそれをfor loop同等物に分解する方が簡単です。

ような:

things = [] 

for a, b in zip(listA, listB): 
    if a == "condition": 
     things.append(a, deadly_func(b)) 

あなたの入力/出力を見て参考になります。これは完全な答えではありませんが、コードのデバッグに役立ちます。

for a, b, in zip(listA, listB)[:1000]: 
.... 
+0

あなたの「入力を減らす」提案は間違っています。これはPy3であり、 'zip'は' list'ではなくジェネレータを返します。 'zip(listA [:1000]、listB)'で操作する 'list'の1つをスライスするか、任意の反復可能な入力を処理するか、' itertools.islice'( 'zip'オブジェクトまたはいずれかの入力): 'islice(zip(listA、listB)、1000)' – ShadowRanger

関連する問題