Pythonのラムダ、マップを使用するための効率的な方法
-
20-09-2019 - |
質問
私はBigtableの(デシベル)で整数の大きなリストを格納する必要があります。効率化のために、私は2つの連続で項目間の差分としてそれらを格納しています。
などのために:
original_list = [1005, 1004, 1003, 1004, 1006]
のような(実際より1000K項目を含む)は、上記リストを記憶
start = 1005 diff = [-1, -1, 1, 2]
私が管理できる最も近いがある、
ltp = [start] map(lambda x: ltp.append(ltp[-1] + x), tick)
私は、元のリストに戻ってそれを変換する効率的な方法を探しています。
解決
私のために、次の作品ます:
orig = [start]
for x in diff:
orig.append(orig[-1] + x)
map
を使用すると、None
で満たされ、同じサイズの新しい配列を作成します。私は読みやすく、かつ早くあなたが得ることができるように、この場合の簡単なfor
ループをも見つけます。
他のヒント
うまくいくnumpyのような大規模なデータ構造のために。この例では、それは(下記参照)の 200倍以上の高速化のだ、とのコードに少し簡単に、基本的には
add.accumulate(diff)
numpyのと直接リスト操作との比較:
import numpy as nx
import timeit
N = 10000
diff_nx = nx.zeros(N, dtype=nx.int)
diff_py = list(diff_nx)
start = 1005
def f0():
orig = [start]
for x in diff_py:
orig.append(orig[-1] + x)
def f1():
diff_nx[0] = start
nx.add.accumulate(diff_nx)
t = timeit.Timer("f0()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)
t = timeit.Timer("f1()", "from __main__ import f0, f1, diff_nx, diff_py, nx, start")
print t.timeit(number=1000)
与えます。
13.4044158459 # for list looping
0.0474112033844 # for numpy accumulate
本当に、しかし、簡単に PyTables ではなく、あなたがここでやっているようだのような独自のローリングます。
また、ここでは、私はあなたが、もちろん、プリペンド用語でリストを再構築するのではなく、プリペンドスタート用語のための部屋でデータを読み込むことを示唆していますので、あなたは、コピーを行う必要はありません。
発電機に最適ます:
def diff2abs( diffs, start ):
yield start
for diff in diffs:
start += diff
yield start
start = 1005
diffs = [-1, -1, 1, 2]
original_list = list( diff2abs( diffs, start ))
他の回答者のいくつかのあなたが尋ねたアルゴリズムの合理的な実装を持っていますが、私はそれはあなたが本当に解決しようとしている正確に何である問題には不明です。
保存されている数字は(つまり、整数をオーバーフローさせ、bignumsを必要とする)非常に大きい場合を除き、差分のリストはあなたにどんな効率を得ることはありません - 整数PythonランタイムPOVの整数であり、「あなたの例ので、 [-1, -1, 1, 2]
の差分」リストは、元のリストの[1005, 1004, 1003, 1004, 1006]
と同じくらい多くのメモリを消費します。
class runningtotal:
def __init__(self, start = 0):
self.total = start
def __call__(self, value):
self.total += value
return self.total
今すぐ試してみてください。
>>> map(runningtotal(start), [0,]+diff)
[1005, 1004, 1003, 1004, 1006]
mshsayemは、使用リストの内包表記を示唆したように - 。彼らは一般的に高速ループやマップ/ラムダ(に従って行うマーク・ルッツの本学習のPython)
よりもあります あなたは本当に多くのFPっぽいソリューションを使用する場合は、は、適切な機能は、「スキャン」、ウィッヒ[私は信じている]あなたは難しいことではありませんこれは(それを自分で実装する必要がありますので、Pythonで実装されていないだろうタスク)。
「スキャン」基本的に、その代わりに単一の値にリストを低減する低減され、それは新たなリストの各「反復」の結果を格納する。
あなたはそれを実装した場合、あなたのような何かを行うことができます:
scan(lambda x,y: x+y, [start]++diff)
私は、これは、より効率的であるべき理由を得ることはありませんが、私は最高のパフォーマンスが得られますforループかなり確信A:
l = [start]
for i in diff:
l.append(l[-1] + i)
私は差分として整数を格納するために、あなたの推理は知らないが、あなたはへのアクセス権を持ってする必要がない場合あなたは発電機を使用するためにリスト全体を一度、それはより効率的なメモリが賢明です。あなたはこれがあると言うので、「大きなリスト、」あなたの代わりに、一度にリスト全体を確保するので、このように大量のメモリを節約することができます。ここであなたのリストを取り戻すために発電機の理解があります:
start = 1005
def mod_start(x):
global start
start += x
return start
int_generator = (mod_start(i) for i in diffs)
あなたが任意のリストだろうようにあなたはその後、一度にメモリ内のリスト全体を持たずに、int_generatorを反復処理することができます。あなたは添字できないか、発電機をスライスしていますが、多くの有用な状況でそれを使用することができ、ただし、注意します。
スタート変数がグローバルである必要はないように、あなたは一例をクリーンアップすることができます。それはちょうどmod_start機能に対してローカルではないことができます。
の編集のあなたは、発電機を取得するために発電機の理解を使用する必要はありません。 THC4kがやったように、あなたはまた、降伏式でジェネレータ機能を使用することができます。それは、開始変数のスコープの問題を回避し、おそらく少しクリーナーです。また、リスト()組み込み関数に渡すことで任意の時点で発電機からリストを取得することができます。
こののパフォーマンスにコメントをしていますが、ここに減らす使用することができます。
start = 1005
diffs = [-1,-1,1,2]
reduce(lambda undiffed_list, diff: undiffed_list + [undiffed_list[-1] + diff],diffs,[start])
あなたが欲しいものを取得します。