Concatenation of many lists in Python
假设我有这样一个函数:
1
| def getNeighbors(vertex) |
返回与给定顶点相邻的顶点列表。现在我想创建一个列表,列出所有邻居的邻居。我是这样做的:
1 2 3
| listOfNeighborsNeighbors = []
for neighborVertex in getNeighbors(vertex):
listOfNeighborsNeighbors.append(getNeighbors(neighborsVertex)) |
有没有比这更像Python的方法?
- 不过,我认为复制品和这个问题都选择了错误的答案。请参阅此处了解更多的Python式/表演式答案。
通常的解决方案,itertools模块包含:
1 2 3 4 5 6 7 8 9 10
| >>> l1=[1, 2, 3]
>>> l2=[4, 5, 6]
>>> l3=[7, 8, 9]
>>> import itertools
>>> list(itertools.chain(l1, l2, l3))
[1, 2, 3, 4, 5, 6, 7, 8, 9] |
- 因此,问题的解决办法是list(itertools.chain.from_iterable(getNeighbors(n) for n in getNeighbors(vertex)))。
appending列表可以完成与+和sum():
1 2 3
| >>> c = [[1, 2], [3, 4]]
>>> sum(c, [])
[1, 2, 3, 4] |
- 谢谢-我知道有办法用SUM来做这个!顺便说一句,我不清楚这是否适用于两个以上的子列表或可变长度列表;所以更清楚的例子可能是:c = [[1, 2], [3, 4, 5], [6, 7]]=>[1, 2, 3, 4, 5, 6, 7]。
- 但是看看我在EMU的回答下所做的评论。不要使用SUM——对于100个项目的100个列表来说非常慢!
- 为什么需要求和的第二个参数?我认为SUM([[1,2],[3,4]])是一天的意思是[1,2]+[3,4]。
1
| [x for n in getNeighbors(vertex) for x in getNeighbors(n)] |
或
1
| sum(getNeighbors(n) for n in getNeighbors(vertex), []) |
- +我打算提出一个理解清单的建议。嗯,这是最刺激的方式。
- 但是,请参阅时间比较,作为emu答案下的注释:"itertools.chain"和"reduce(iadd)"的速度是嵌套列表理解速度的两倍以上,比sum()快得多,后者在处理了个元素后会迅速退化。
- 很高兴我找到这个。试了很多次,从来没有用这样的第二个论点来计算清单的总和。
如果速度的问题,它可能是更好的使用这个:
1 2
| from operator import iadd
reduce(iadd, (getNeighbors(n) for n in getNeighbors(vertex))) |
本代码是在该点的列表在列表list.extendconcatenating全由一对一个项目的理解会增加,如果list.append调用。saves位是架空的,制作前(根据我的测量约三倍)的速度。(通常是书面的+=iadd算子和不相同的东西的list.extend)。
使用列表(第一液)comprehensions Ignacio)仍然是正确的,它是通常的方式,更容易阅读。
但绝对避免使用sum(..., []),因为它运行在二次。这是非常impractical列表是很多(超过百或OS)。
- 感谢Re-Sum的评论——我喜欢代码的紧凑性,所以很高兴知道不要大规模使用它。imho,jochen's itertools'chain solution from'10是一个比reduce更合适的解决方案:它更直接/更简单地执行所要求的操作。
- 我收回我所说的关于链条更合适的说法。虽然我发现它更容易阅读/理解,但如果最终目标是创建一个列表,它会有一个轻微的性能问题:它一次传递一个元素,因此列表无法预测它将持续多长时间。从这个意义上讲,它更类似于列表理解/附加,而不是减少/扩展。使用timeit,对于100个列表x 100个元素,减少/iadd与链的时间大致相等(1.1)。有了4倍的数据(200个列出了x 200个元素),就可以减少获胜次数(2.6对4.0)。
- 警告:IADD修改传入的第一个列表。在示例中并不重要,因为列表是函数的结果。但我做了一个测试,在测试中我通过了一个预先计算的列表。修改了我原来的名单,这不好做。修正:代替reduce(iadd, LL)甚至reduce(iadd, (L for L in LL))必须将每个返回的l包装在list():reduce(iadd, (list(L) for L in LL))中。这将强制复制每个L。(因为尺寸已知,所以速度很快)。
- …列表理解下降更快(2.4=>9.1)。总和更糟(13.8=>130.2)!把这些数字重复在一起以便比较:(减少,链接,理解,和)@100x100=(1.1,1.1,2.6,13.8);@200x200=(2.6,4.0,9.1,130.2)。
- 测试代码(python 2.7):print timeit('all = reduce(operator.iadd, (list(list_) for list_ in LL))', number=1000, setup='n = 100; import operator; L1 = list(range(n)); LL = [[10 * x + v for v in L1] for x in range(n)]')print timeit('all = list(itertools.chain(*LL))', number=1000, setup='n = 100; L1 = list(range(n)); LL = [[10 * x + v for v in L1] for x in range(n)]')print timeit('all = [x for list_ in LL for x in list_]', number=...print timeit('all = sum(LL, [])', number=...然后用n = 200;代替100重复这4个步骤。(然后我将结果乘以10)
- @你知道为什么和在二次时间内运行吗?这让我吃惊。
- @toolmakersteve您可以将第二个参数添加到reduce作为初始值,从而避免了复制列表的需要。
- @因为它别无选择,只能在每次添加时构建一个新的列表,这是一个线性时间操作。
采用高速类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| list_of_lists = [[x,1] for x in xrange(1000)]
%timeit list(itertools.chain(*list_of_lists))
100000 loops, best of 3: 14.6 μs per loop
%timeit list(itertools.chain.from_iterable(list_of_lists))
10000 loops, best of 3: 60.2 μs per loop
min(timeit.repeat("ll=[];
for l in list_of_lists:
ll.extend(l)","list_of_lists=[[x,1] for x in xrange(1000)]",repeat=3, number=100))/100.0
9.620904922485351e-05
%timeit [y for z in list_of_lists for y in z]
10000 loops, best of 3: 108 μs per loop
%timeit sum(list_of_lists, [])
100 loops, best of 3: 3.7 ms per loop |
- itertools.chain(list_of_lists)是错误的(它不会连接任何东西,因为它只提供了一个参数)。你需要一个*,或者chain.from_iterable。
- 这些定时结果可能会过时。使用python3.6.6测试2018硬件,我看不到itertools.chain、itertools.chain.from iterable和functools.reduce/iadd解决方案之间有任何可复制的速度差异。YMMV。不过,IADD解决方案会更改输入。
我喜欢,因为它运行在itertools.chain方法(线性和二次(……)中的时间),但"约翰不展示如何处理的动态列表的长度。这里是一个解决方案是打开的。
1 2
| import itertools
list(itertools.chain(*[getNeighbors(n) for n in getNeighbors(vertex)])) |
你可以摆脱list(...)呼叫,如果你是足够的迭代变量。
- 您也可以通过这样使用chain.from_iterable:list(itertools.chain.from_iterable(getNeighbors(n) for n in getNeighbors(vertex))),来摆脱对*[getNeighbors...]的解包。
利用.extend(更新)(就地)结合而减少(新对象)和(每个小组)应更有效,但是我太懒,:)试验
1 2
| mylist = [[1,2], [3,4], [5,6]]
reduce(lambda acc_l, sl: acc_l.extend(sl) or acc_l, mylist) |
- 它确实更快,但正如亚里夫的答案所示,它不是最快的方法。