请注意,这不是this post的副本,因为我想压缩超过2个列表(或者至少我不能轻易地概括该帖子,以便在没有显式循环的情况下使用)< / p>
我想找到以特定方式合并列表列表的最佳性能(在速度方面)实现。输入是列表(或元组)的列表,按顺序排列,使得下一个列表的长度始终是前一个列表的倍数。例如:
a = ['A', 'B', 'C', 'D']
b = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
input_list = [a, b]
输出是合并列表:
output = ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'A', 'E', 'B', 'F', 'C', 'G', 'D', 'H']
也就是说,较短的列表(在这种情况下为a
)全部通过循环自身扩展到最长列表(在本例中为b
),以便列表具有相同的长度。然后所有列表以垂直堆叠方式合并。
目前我有一个基本上执行以下操作的实现:
step 1 step 2 step 3
====== ======== ======
ABCD ABCDABCD
ABCDEFGH -------> ABCDEFGH ------> AABBCCDDAEBFCGDH
它有效但效率不高:
def flatten_list(value):
return sum(value, [])
def group(value):
for idx in reversed(range(1, len(value))):
multiplier = int(len(value[idx]) / len(value[idx - 1]))
if multiplier > 1:
value[idx - 1] = flatten_list([value[idx - 1] for i in range(multiplier)])
return flatten_list(list(zip(*value)))
是否有更快的实施?性能对我的应用程序非常重要,因为输入可能很大。任何建议都表示赞赏!
答案 0 :(得分:4)
使用itertools
中的cycle
,chain
,islice
:
list(chain(*islice(
zip(*(cycle(l) for l in input_list)),
0, len(max(input_list, key=len)))))
# ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'A', 'E', 'B', 'F', 'C', 'G', 'D', 'H']
或者,在其中:
# generator producing cycles of each list
(cycle(l) for l in input_list)
# zip these cycles together: ('A', 'A') -> ('B', 'B') -> infinitely
zip(*...)
# take a slice of this iterable of tuples with the length of the longest list
islice(..., 0, len(max(input_list, key=len)))
# chain those tuples together into one list
list(chain(*...))
或说明:
lists = [
# chain--┐-----┐-----┐
# ┌--┐ ┌--┐ ┌--┐
# | ┌-┐ | ┌-┐ | ┌-┐ | ┌-┐ | ┌-┐
[1], # cycle: | |1|,| |1|,| |1|,| |1|, | |1|, ...
[1, 2], # cycle: | |1|,| |2|,| |1|,| |2|, | |1|, ...
[1, 2, 3, 4], # cycle: | |1|,| |2|,| |3|,| |4|, | |1|, ...
] # | └-┘ | └-┘ | └-┘ | └-┘ | └-┘
# | └--┘ └--┘ └--┘ |
# | zip zip zip zip | zip ...
# | |
# islice start islice stop
# --> [1,1,1,1,2,2,1,1,3,1,2,4]
时间复杂度为O(n)
,其中n
是输出列表的长度。在Python2中,您必须使用itertools.izip
而不是zip
,因为后者会尝试构建无限列表。
答案 1 :(得分:2)
使用roundrobin
itertools recipe:
两个输入
import itertools as it
a = list("ABCD")
b = list("ABCDEFGH")
list(it.chain.from_iterable(roundrobin(zip(it.cycle(a), b))))
# ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'A', 'E', 'B', 'F', 'C', 'G', 'D', 'H']
itertools.cycle()
无限延伸较短的可迭代。 zip()
在较短的可迭代后停止迭代。 roundrobin
处理迭代之间的元素交错。
更长的输入
要处理两个以上的输入,我们需要循环除最后一个迭代之外的所有输入:
def interleave(*inputs):
*rest, last = inputs
cycles = (it.cycle(x) for x in rest)
return list(it.chain.from_iterable(mit.roundrobin(zip(*cycles, last))))
现在,对于两个或更多输入可迭代,我们可以应用interleave
函数:
p = list("ab")
q = list("abcd")
r = list("abcdef")
input_list_1 = [a, b]
input_list_2 = [p, q, r]
print(interleave(*input_list_1))
# ['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'A', 'E', 'B', 'F', 'C', 'G', 'D', 'H']
print(interleave(*input_list_2))
# ['a', 'a', 'a', 'b', 'b', 'b', 'a', 'c', 'c', 'b', 'd', 'd', 'a', 'a', 'e', 'b', 'b', 'f']
注意:您可以从the docs重新实施roundrobin
食谱,也可以安装为您实施该食谱的第三方库,例如more_itertools
。 > pip install more_itertools
,然后是Python,from more_itertools import roundrobin
。
答案 2 :(得分:1)
将itertools.cycle
应用于所有短于最长的列表并将它们拼接在一起,从而使结果变得扁平化。
def special_zip(*lsts):
max_len = max(map(len, lsts))
cyclic = [lst if len(lst) == max_len else itertools.cycle(lst) for lst in lsts]
return (el for grp in zip(*cyclic) for el in grp)
副手这应该是O(2n),其中n是列表长度的总和。如果您知道最长的列表并且可以单独传递它,则变为O(n)。同样,如果您知道需要多少输出(因为您可以将itertools.cycle
应用于每个列表并从无限输出中拉出来)
答案 3 :(得分:1)
这会有用吗?
final_result=[]
def recursive_approach(aa,bb):
if not bb:
return 0
else:
for i in zip(aa,bb):
final_result.extend([i[0],i[1]])
return recursive_approach(aa,bb[len(aa):])
recursive_approach(a,b)
print(final_result)
输出:
['A', 'A', 'B', 'B', 'C', 'C', 'D', 'D', 'A', 'E', 'B', 'F', 'C', 'G', 'D', 'H']