[英]Most clean and efficient way to generate and zip two lists in Python
Given these two lists鉴于这两个列表
zeros = [2,3,1,2]
ones = [3,4,5]
(with the condition that always len(zeros) == len(ones) + 1
) (条件总是len(zeros) == len(ones) + 1
)
I want to create one list with alternating 0's and 1's of the magnitude mentioned in the list.我想创建一个列表,其中交替包含列表中提到的大小的 0 和 1。 I can achieve this by:我可以通过以下方式实现:
zeros_list = [[0]*n for n in zeros]
ones_list = [[1]*n for n in ones]
output = [z for x in zip(zeros_list, ones_list) for y in x for z in y]
output += [0]*zeros[-1]
print(output)
> [0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0]
Is this however the most efficient / clean way?然而,这是最有效/最干净的方式吗? I get a performance of 2.66 µs ± 78.8 ns, but I still have the idea this could be done in a one-liner, and maybe more efficient我得到了 2.66 µs ± 78.8 ns 的性能,但我仍然认为这可以在单行中完成,而且效率可能更高
Two apparently faster solutions, using the "trick" to special-treat the first zero rather than the last, using it to initialize output
.两个明显更快的解决方案,使用“技巧”来特殊处理第一个零而不是最后一个零,使用它来初始化output
。
def superb_rain(zeros, ones):
zeros = iter(zeros)
output = [0] * next(zeros)
for o in ones:
output += (1,) * o
output += (0,) * next(zeros)
return output
def superb_rain2(zeros, ones):
z = iter(zeros).__next__
output = [0] * z()
for o in ones:
output += (1,) * o
output += (0,) * z()
return output
(As @schwobaseggl pointed out, tuples made it about 30% faster.) (正如@schwobaseggl 指出的那样,元组使它的速度提高了大约 30%。)
Benchmark results:基准测试结果:
0.14 us 0.13 us 0.13 us baseline
3.04 us 3.02 us 2.98 us original
3.27 us 3.19 us 3.29 us chepner_1
5.03 us 5.12 us 5.25 us chepner_2
4.66 us 4.74 us 4.68 us chepner_2__superb_rain
2.52 us 2.53 us 2.47 us Alain_T
3.35 us 3.27 us 3.42 us python_user
1.02 us 0.99 us 1.04 us superb_rain
1.07 us 1.11 us 1.09 us superb_rain2
Benchmark code:基准代码:
import timeit
from itertools import zip_longest, cycle, islice, repeat, chain
def baseline(zeros, ones):
pass
def original(zeros, ones):
zeros_list = [[0]*n for n in zeros]
ones_list = [[1]*n for n in ones]
output = [z for x in zip(zeros_list, ones_list) for y in x for z in y]
output += [0]*zeros[-1]
return output
def chepner_1(zeros, ones):
return list(chain.from_iterable(chain.from_iterable(zip_longest((repeat(0, x) for x in zeros), (repeat(1, x) for x in ones), fillvalue=[]))))
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
for next in nexts:
yield next()
except StopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -= 1
nexts = cycle(islice(nexts, num_active))
def chepner_2(zeros, ones):
zero_groups = (repeat(0, x) for x in zeros)
one_groups = (repeat(1, x) for x in ones)
return list(chain.from_iterable(roundrobin(zero_groups, one_groups)))
def chepner_2__superb_rain(zeros, ones):
return list(chain.from_iterable(map(repeat, cycle([0, 1]), roundrobin(zeros, ones))))
def Alain_T(zeros, ones):
return [B for N,P in zip(zeros+[0],ones+[0]) for B in ([0]*N+[1]*P)]
def python_user(zeros, ones):
res = [None] * (len(ones) + len(zeros))
res[::2] = ([0]*n for n in zeros)
res[1::2] = ([1]*n for n in ones)
res = [y for x in res for y in x]
return res
def superb_rain(zeros, ones):
zeros = iter(zeros)
output = [0] * next(zeros)
for o in ones:
output += (1,) * o
output += (0,) * next(zeros)
return output
def superb_rain2(zeros, ones):
z = iter(zeros).__next__
output = [0] * z()
for o in ones:
output += (1,) * o
output += (0,) * z()
return output
funcs = [
baseline,
original,
chepner_1,
chepner_2,
chepner_2__superb_rain,
Alain_T,
python_user,
superb_rain,
superb_rain2,
]
zeros = [2,3,1,2]
ones = [3,4,5]
number = 10**5
expect = original(zeros, ones)
for func in funcs:
print(func(zeros, ones) == expect, func.__name__)
print()
tss = [[] for _ in funcs]
for _ in range(4):
for func, ts in zip(funcs, tss):
t = min(timeit.repeat(lambda: func(zeros, ones), number=number)) / number
ts.append(t)
print(*('%.2f us ' % (1e6 * t) for t in ts[1:]), func.__name__)
print()
Zip with a list comprehension should do the trick.具有列表理解的 Zip 应该可以解决问题。
zeros = [2,3,1,2]
ones = [3,4,5]
output = [B for N,P in zip(zeros,ones+[0]) for B in [0]*N+[1]*P]
print(output)
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0]
Note the ones+[0]
is to ensure that you don't drop the last value from the zeroes list in the zip operation.请注意, ones+[0]
是为了确保您不会在 zip 操作中删除零列表中的最后一个值。
You can use itertools.chain
, zip_longest
, and itertools.repeat
to create a not-too-confusing one-liner.您可以使用itertools.chain
、 zip_longest
和itertools.repeat
创建一个不太容易混淆的单行代码。
>>> list(chain.from_iterable(chain.from_iterable(zip_longest((repeat(0, x) for x in zeros), (repeat(1, x) for x in ones), fillvalue=[]))))
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0]
On my machine, this took 3.34µs.在我的机器上,这花费了 3.34µs。 More importantly, it's the wrapper to list
that takes this time.更重要的是,这次是要list
的包装器。 The iterator itself produces the elements on demand, if you don't actually need them all at once.迭代器本身会按需生成元素,如果您实际上并不一次需要它们的话。
list(chain.from_iterable(
chain.from_iterable(zip_longest((repeat(0, x) for x in zeros),
(repeat(1, x) for x in ones),
fill_value=[]))))
(repeat(0, x) for x in zeros)
creates a sequence of repeat
objects that represent your runs of 0s; (repeat(0, x) for x in zeros)
创建一系列repeat
对象,代表您运行的 0; likewise to create the groups of 1s.同样创建 1 组。zip_longest
zips them together into a sequence of pairs, adding a do-nothing empty list to balance the extra value in zeros
zip_longest
将它们压缩成一对序列,添加一个什么都不做的空列表来平衡zeros
中的额外值chain.from_iterable
flattens that sequence (from (a, b), (c, d)
to (a, b, c, d)
. chain.from_iterable
将该序列展平 (from (a, b), (c, d)
to (a, b, c, d)
。chain.from_iterable
then flattens the repeat
objects in to a single sequence, which list
turns into a list.外层的chain.from_iterable
然后将repeat
的对象压平成一个序列,这个list
变成一个列表。 You can also simplify the one-liner using the roundrobin
recipe from the itertools
documentation, which handles both merging the zero groups and one groups, as well as the first round of flattening.您还可以使用itertools
文档中的roundrobin
方法简化单行代码,它处理合并零组和一个组,以及第一轮展平。
from itertools import cycle, islice, repeat, chain
zeros = [2,3,1,2]
ones = [3,4,5]
# From the itertools documentation
def roundrobin(*iterables):
"roundrobin('ABC', 'D', 'EF') --> A D E B F C"
# Recipe credited to George Sakkis
num_active = len(iterables)
nexts = cycle(iter(it).__next__ for it in iterables)
while num_active:
try:
for next in nexts:
yield next()
except StopIteration:
# Remove the iterator we just exhausted from the cycle.
num_active -= 1
nexts = cycle(islice(nexts, num_active))
zero_groups = (repeat(0, x) for x in zeros)
one_groups = (repeat(1, x) for x in ones)
print(list(chain.from_iterable(roundrobin(zero_groups, one_groups))))
Given these two lists鉴于这两个列表
zeros = [2,3,1,2]
ones = [3,4,5]
(with the condition that always len(zeros) == len(ones) + 1
) (条件总是len(zeros) == len(ones) + 1
)
I want to create one list with alternating 0's and 1's of the magnitude mentioned in the list.我想创建一个列表,其中包含列表中提到的数量级的交替 0 和 1。 I can achieve this by:我可以通过以下方式实现:
zeros_list = [[0]*n for n in zeros]
ones_list = [[1]*n for n in ones]
output = [z for x in zip(zeros_list, ones_list) for y in x for z in y]
output += [0]*zeros[-1]
print(output)
> [0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0]
Is this however the most efficient / clean way?然而,这是最有效/最干净的方式吗? I get a performance of 2.66 µs ± 78.8 ns, but I still have the idea this could be done in a one-liner, and maybe more efficient我得到了 2.66 µs ± 78.8 ns 的性能,但我仍然认为这可以在单线中完成,而且效率可能更高
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.