简体   繁体   English

在 Python 中生成和 zip 两个列表的最干净有效的方法

[英]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.chainzip_longestitertools.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)
  • The outer 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.

相关问题 Python:比较两个整数列表的最有效方法 - Python: Most efficient way to compare two lists of integers Python Pandas:在循环中比较两个列表的最有效方法是什么? - Python Pandas: What is the most efficient way to compare two lists in a loop? 在Python中相交列表最有效的方法? - Most efficient way to intersect list of lists in Python? 比较列表的最有效方法 Python - Most efficient way to compare lists Python Python - 生成填充的最有效方法是什么? - Python - What is the most efficient way to generate padding? 在 Python 中将列表(列表列表)写入 csv 的最有效方法? - Most efficient way of writing a list (of lists of lists) to a csv in Python? Python3:计算两个列表总和为100的所有排列的最有效方法是什么? - Python3: What is the most efficient way to calculate all permutations of two lists summing to 100? Python - 比较两个字符串/列表中“正确”顺序排序的单词#的最有效方法 - Python - Most efficient way to compare # of words sequenced in “right” order across two strings/lists 从嵌套为 python 中的字典值的两个列表中检索数据的最有效方法 - Most efficient way to retrieve data from two lists nested as values of dictionary in python 使用两个列表创建dict的最有效方法是什么? - what is the most efficient way to creating a dict with two lists?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM