使用python的lambda,map的高效方法

时间:2009-09-01 16:27:08

标签: python performance list lambda map-function

我需要在Bigtable(db)中存储一个很大的整数列表。为了提高效率,我将它们存储为两个连续项之间的差异。

例如:

 original_list = [1005, 1004, 1003, 1004, 1006] 

将上面的列表(实际上包含超过1000个项目)存储为<​​/ p>

start = 1005
diff = [-1, -1, 1, 2]

我能管理得最近的是,

ltp = [start]
map(lambda x: ltp.append(ltp[-1] + x), tick)

我正在寻找一种有效的方法将其转换回原始列表。

9 个答案:

答案 0 :(得分:7)

对于这样的大型数据结构,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轻松完成,而不是像你在这里做的那样滚动你自己。

另外,在这里,我建议您在数据中读取前置开始项的空间,而不是使用前置术语重建列表,当然,这样您就不必复制了。 / p>

答案 1 :(得分:6)

以下适用于我:

orig = [start]
for x in diff:
    orig.append(orig[-1] + x)

使用map将创建一个相同大小的新数组,填充None。我还发现一个简单的for循环更具可读性,在这种情况下,你可以尽可能快地获得。

答案 2 :(得分:4)

适合发电机:

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 ))

答案 3 :(得分:2)

其他几位受访者对您要求的算法进行了合理的实施,但我不清楚您究竟要解决的问题究竟是什么。

除非存储的数字非常大(即溢出一个整数并需要bignums),否则你的差异列表将无法获得任何效率 - 整数是Python运行时POV中的整数,所以你的例子“ diff“[-1, -1, 1, 2]列表将消耗与原始列表[1005, 1004, 1003, 1004, 1006]一样多的内存。

答案 4 :(得分:2)

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]

答案 5 :(得分:1)

正如mshsayem建议的那样,使用列表推导 - 它们通常比循环或map / lambdas更快(根据Mark Lutz的书“学习Python”)。

如果你真的想要使用更多的FP-ish解决方案,那么正确的功能将是“扫描”,[我相信]没有在Python中实现,所以你必须自己实现它(这不是很难)任务)。

“scan”基本上是一个reduce,但它不是将列表缩减为单个值,而是将每个“iteration”的结果存储在一个新列表中。

如果您实施了它,您可以执行以下操作:

scan(lambda x,y: x+y, [start]++diff)

答案 6 :(得分:0)

虽然我不明白为什么这应该更有效率,但我很确定for循环会提供最佳性能:

l = [start]
for i in diff:
    l.append(l[-1] + i)

答案 7 :(得分:0)

我不知道你将整数存储为差异的原因 - rcoder给出了一个很好的答案,为什么这通常不比存储整数本身更有效 - 但如果你不需要访问一次整个列表,你使用发电机的记忆效率更高。既然你说这是一个“大清单”,你可以用这种方式节省大量内存,而不是一次分配整个列表。这是一个生成器理解,以获取您的列表:

start = 1005
def mod_start(x):
    global start
    start += x
    return start
int_generator = (mod_start(i) for i in diffs)

然后,您可以像任何列表一样遍历int_generator,而不必立即将整个列表放在内存中。但请注意,您不能下标或切片生成器,但您可以在许多有用的情况下使用它。

您可以清理示例,以便start变量不需要是全局变量。它只是不能是mod_start函数的本地。

编辑:您不必使用生成器理解来获取生成器。你也可以使用带有yield表达式的生成器函数,就像THC4k那样。这避免了启动变量范围问题,可能有点清洁。您还可以通过将生成器传递给list()内置函数来随时从生成器中获取列表。

答案 8 :(得分:0)

没有评论这个的表现,但你可以在这里使用reduce。

start = 1005
diffs = [-1,-1,1,2]
reduce(lambda undiffed_list, diff: undiffed_list + [undiffed_list[-1] + diff],diffs,[start])

获得你想要的东西。