使用Python的lambda,地图有效的方法
-
20-09-2019 - |
题
我需要存储整数的Bigtable中(DB)的大名单。为了提高效率,我将它们存储为连续的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 ))
若干的其他受访者要求您提供的算法的合理实现,但我不清楚它到底是什么问题,你真的是在努力解决的问题。
除非被存储的数字是非常大的(即溢出整数,需要大数),您的diff将不会获得任何你效率列表 - 一个整数是从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建议,使用列表推导 - 它们一般比环或地图/ lambda表达式(根据做马克·鲁茨的书学习的Python)
快一些如果你真的想使用更多的FP-ISH的解决方案,适当的功能将是“扫描”,至极[我相信]是不是用Python实现,所以你就必须实现它自己(这是不是一个硬任务)。
“扫描”基本上一个减少,但代替该列表缩减为单个值,它存储在一个新的列表中的每个“迭代”的结果。
如果你实现它,你可以这样做:
scan(lambda x,y: x+y, [start]++diff)
虽然我不明白为什么这应该是更有效的,我敢肯定for循环将提供最佳性能:
l = [start]
for i in diff:
l.append(l[-1] + i)
我不知道你的推理,用于存储整数作为的diff - rcoder了为什么这个一般不超过存储整数自己更高效的一个很好的答案 - 但如果你不需要访问整个列表一次,它的效率更高的内存明智供你使用一台发电机。既然你说这是一个“大名单”,可以节省大量的内存这种方式,而不是一次分配的完整列表。这里有一台发电机理解让你列表返回:
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])
得到你想要的东西。