【问题标题】:Large list generation optimization大列表生成优化
【发布时间】:2015-03-17 00:38:31
【问题描述】:

我需要一个 python 函数,它可以采用以下形式的字符串列表:

seq = ['A[0]','B[2:5]','A[4]']

并返回具有保留顺序的“扩展”元素的新列表,如下所示:

expanded = ['A[0]', 'B[2]', 'B[3]', 'B[4]', 'B[5]', 'A[4]']

为了实现我的目标,我编写了这个简单的函数:

def expand_seq(seq):
    #['item[i]' for item in seq for xrange in item]
    return ['%s[%s]'%(item.split('[')[0],i) for item in seq for i in xrange(int(item.split('[')[-1][:-1].split(':')[0]),int(item.split('[')[-1][:-1].split(':')[-1])+1)]

在处理生成少于 500k 个项目的序列时,它运行良好,但在生成非常大的列表(超过 100 万个)时,它会减慢很多。例如:

# let's generate 10 million items!
seq = ['A[1:5000000]','B[1:5000000]']
t1 = time.clock()
seq = expand_seq(seq)
t2 = time.clock()
print round(t2-t1, 3)
# RESULT: 9.541 seconds

我正在寻找改进此功能的方法,并希望在处理大型列表时加快它的速度。如果有人有建议,我很乐意听取他们的意见!

【问题讨论】:

  • 听起来像是发电机的工作。
  • 通过迭代 xrange 在列表中创建 10,000,000 个元素预计会很慢。您将如何处理生成的数据?
  • 适当的优化很大程度上取决于您之后想要对列表执行的操作。如果您可以/想要一个一个地处理生成的元素,那么生成器将是您的选择。如果你想做其他事情,那么它可能会更复杂。

标签: python python-2.7


【解决方案1】:

以下似乎提供了 35% 的加速:

import re

r = re.compile(r"(\w+)\[(\d+)(?::(\d+))?\]")

def expand_seq(seq):
    result = []
    for item in seq:
        m = r.match(item)
        name, start, end = m.group(1), int(m.group(2)), m.group(3)
        rng = xrange(start, int(end)) if end else (start,)
        t = name + "["
        result.extend(t + str(i) + "]" for i in rng)
    return result

使用此代码:

  • 我们编译一个正则表达式以在函数中使用。
  • 我们直接连接我们的字符串。

【讨论】:

  • 这个解决方案很有前途!我不擅长使用正则表达式,我真的应该更加努力。您的解决方案的唯一问题是它无法处理没有“范围”的项目......例如:A[0].. 像 A[0:2] 这样的项目也应该扩展到 A[0],A[ 1],A[2],因为它停在 A[1]... 将 int(end) 更改为 int(end)+1 将修复
  • 实际上是关于不处理“无范围”项目的最后一条评论,该错误是由于使用具有非 ascii 字符的列表进行测试(在本例中为点:'A.p[0]'这打破了正则表达式
【解决方案2】:

我不确定您是否会获得戏剧性的加速,因为您无法从根本上改进算法。通过这种方式,我确实从你那里得到了大约 20% 的加速:

def expand_seq(seq):
    expanded = []
    for s in seq:
        name, indices = s[0:-1].split("[")
        if ":" in indices:
            index1, index2 = [int(i) for i in indices.split(":")]
        else:
            index1 = int(indices)
            index2 = index1
        for n in range(index1, index2 + 1):
            expanded.append("{}[{}]".format(name, n))
    return expanded

我认为加速主要是由于不重复某些操作(例如 intsplit),您必须执行这些操作才能使您的解决方案保持单线。

不过,正如建议的那样,如果您使用生成器,您可以立即开始使用结果。像这样:

def expand_seq(seq):
    for s in seq:
        name, indices = s[0:-1].split("[")
        if ":" in indices:
            index1, index2 = [int(i) for i in indices.split(":")]
        else:
            index1 = int(indices)
            index2 = index1
        for n in range(index1, index2 + 1):
            yield "{}[{}]".format(name, n)

【讨论】:

  • 感谢您的建议!在做了一些测试后,我决定实施 JuniorCompressor 的解决方案,因为它是最快的!
猜你喜欢
  • 1970-01-01
  • 2013-02-26
  • 2018-07-19
  • 2018-04-11
  • 2023-03-15
  • 2023-04-04
  • 1970-01-01
  • 2014-08-11
  • 1970-01-01
相关资源
最近更新 更多