ループを二回こすとは無視できる、むしろ関数フィルターより速い
items = [{}...{}]
がありまして、特定の条件でフィルターしたいとします。
条件が2つあります。
この場合は
filterred_items = [ item for item in items if condition1]
filterred_items_2 = [ item for item in filterred_items if condition2]
と二回よびだすパターンと
一回で2つの条件をやってしまうこのパターン
filterred_items = [ item
for item in items
if condition1
if condition2 ]
のどっちがはやいんでしょうか?
という検証です。
で今回、少し特殊かもしれませんが、条件自体もitemの状態により有効な場合と
無効な場合の2値ありえる処理をかんがえます。
百聞は一見にしかず
#-*- coding: utf-8 -*- import time from collections import defaultdict def timeit(method): def timed(*args, **kw): global calc calc = defaultdict(int) print ('===========') ts = time.time() result = method(*args, **kw) te = time.time() print ('%r %2.8f ms' % (method.__name__, (te - ts) * 1000)) print("calc %s times" % sum(calc.values())) return result return timed # items = [ # {'item': 1, 'ts': 1569488756, 'value':500}, # {'item': 2, 'ts': 1569488750, 'value':2} # ] import random @timeit def loop_twice(items): key1 = "ts" val1 = 8688499900 if key1 and val1: ts = time.time() items = [i for i in items if abs(i.get(key1, 0) - ts) < val1] key2 = 'item' key3 = 'value' val3 = 3 ng_items = { item[key2] for item in items if item.get(key2) if item.get(key3) and val3 if item.get(key3) < val3 } print(len(ng_items)) return @timeit def loop_once(items): key1 = "ts" val1 = 8688499900 if key1 and val1: ts = time.time() def ts_filter(doc): return abs(doc.get(key1, 0) - ts) < val1 else: def ts_filter(doc): return True key2 = 'item' key3 = 'value' val3 = 3 if key3 and val3: def rr_filter(doc): return doc.get(key3, -sys.maxsize) < val3 else: def rr_filter(doc): return True ng_items = { item[key2] for item in items if item.get(key2) if ts_filter(item) if rr_filter(item) } print(len(ng_items)) return for i in range(5): N = 40 * 10**i items = [ dict(zip(('item', 'ts', 'value'), (x, y, z))) for x, y, z in zip( range(1, N+1), range(1569488750, 1569488750 + (100*N), 100), [ random.uniform(0, 10) for _ in range(N)] ) ] print('N:%s' % N) loop_twice(items) #loop_once(items)
結果ですが
loop_twiseのほうが速い結果となりました。
loop_twiceの結果はこちら
N:40 =========== 6 'loop_twice' 0.03480911 ms calc 0 times N:400 =========== 112 'loop_twice' 0.23603439 ms calc 0 times N:4000 =========== 1174 'loop_twice' 2.00223923 ms calc 0 times N:40000 =========== 11973 'loop_twice' 20.94793320 ms calc 0 times N:400000 =========== 120030 'loop_twice' 209.94615555 ms calc 0 times
で、loop_onceの結果はこちら
N:40 =========== 9 'loop_once' 0.04100800 ms calc 0 times N:400 =========== 125 'loop_once' 0.26583672 ms calc 0 times N:4000 =========== 1214 'loop_once' 2.27904320 ms calc 0 times N:40000 =========== 12116 'loop_once' 24.24407005 ms calc 0 times N:400000 =========== 120233 'loop_once' 234.66014862 ms calc 0 times
なんでしょう。だいたい20%遅いです、ループ一つにして関数filterでフィルターする処理のほうが。以外でした。残念でした、だってloop_onceのほうが綺麗なのに。。。
一応def関数ではなくlambda関数でもためしましたが全く同じ結果となりました。
import sys @timeit def loop_once_lambda(items): key1 = "ts" val1 = 8688499900 if key1 and val1: ts = time.time() ts_filter = lambda doc: abs(doc.get(key1, 0) - ts) < val1 else: ts_filter = lambda doc: True key2 = 'item' key3 = 'value' val3 = 3 if key3 and val3: rr_filter = lambda doc: doc.get(key3, -sys.maxsize) < val3 else: rr_filter = lambda doc: True ng_items = { item[key2] for item in items if item.get(key2) if ts_filter(item) if rr_filter(item) } print(len(ng_items)) return
この仮設を検証するために
defって関数で条件つきでパターンわけしてかえせるの?
もしらべましたので興味あるかたはこちら(python 関数を条件付きで返す方法)で。
ということで結果
ループを二回こすとは無視できる、むしろ関数フィルターより速い
とい結論に至りました。
以上です