【问题标题】:Expand list with identical elements展开具有相同元素的列表
【发布时间】:2020-05-21 14:12:12
【问题描述】:

假设我有以下嵌套列表:

initial_list = [[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]]

我想把它变成如下:

desired_list = [[1, 1, 1, 2, 2, 2, 3, 3, 3], 
                [4, 4, 4, 5, 5, 5, 6, 6, 6], 
                [7, 7, 7, 8, 8, 8, 9, 9, 9]]

如果我不关心订单,我可以做类似的事情

new_list = [sorted(x*3) for x in initial_list]

但是,顺序应与initial_list 中的相同。 我能做的最好的是将每个元素放在一个列表中并将其乘以 3(任意数),然后加入生成的 inner_list

multiplied_list = [[[element]*3 for element in inner_list] for inner_list in initial_list]
desired_list = [[element for element_list in inner_list for element in element_list] for inner_list in multiplied_list]

(在两个供人类理解的列表中)

是否有更易于理解/充分/pythonic 的方式来执行此操作?

【问题讨论】:

    标签: python list sorting list-comprehension nested-lists


    【解决方案1】:

    您可以使用以下列表理解。请注意,我的 initial_list 与 OP 中的 initial_list 不同,以证明订单被保留。

    代码:

    >>> initial_list = [[1, 3, 2], [4, 5, 6], [7, 8, 9]]
    >>> [[x for x in sl for _ in range(3)] for sl in initial_list]
    [[1, 1, 1, 3, 3, 3, 2, 2, 2],
     [4, 4, 4, 5, 5, 5, 6, 6, 6],
     [7, 7, 7, 8, 8, 8, 9, 9, 9]]
    

    或者,在您的示例中为 sorted 函数添加一个键:

    >>> [sorted(x*3, key=x.index) for x in initial_list]
    [[1, 1, 1, 3, 3, 3, 2, 2, 2],
     [4, 4, 4, 5, 5, 5, 6, 6, 6],
     [7, 7, 7, 8, 8, 8, 9, 9, 9]]
    

    不同 n*n 列表大小的方法的时间比较:

    使用perfplot 生成 - 重现代码:

    from itertools import chain
    from functools import reduce
    import perfplot
    from copy import deepcopy
    import numpy as np
    import random
    
    def shuffle(x):
        random.shuffle(x)
        return x
    
    def cdjb(initial_list):
        return [[x for x in sl for _ in range(3)] for sl in initial_list]
    
    def aurora_sorted(initial_list):
        return [sorted(x*3, key=x.index) for x in initial_list]
    
    def aurora_list_comp(initial_list):
        return [[element for element_list in inner_list for element in element_list] for inner_list in [[[element]*3 for element in inner_list] for inner_list in initial_list]]
    
    def kederrac(initial_list):
        new_list = deepcopy(initial_list)
        for l in new_list:
            for j in range(0, 3*len(l), 3):
                l[j: j + 1] = [l[j]] * 3
        return new_list
    
    def alain_chain(initial_list):
        return [list(chain(*(i3 for i3 in zip(*[sl]*3)))) for sl in initial_list]
    
    def alain_reduce(initial_list):
        return [list(reduce(lambda a,e:a+[e]*3,sl,[]))for sl in initial_list]
    
    def alain_zip(initial_list):
        return [[i for i3 in zip(*[sl]*3) for i in i3] for sl in initial_list]
    
    def binyamin_numpy(initial_list):
        return np.array(initial_list).repeat(3).reshape(len(initial_list), -1).tolist()
    
    
    perfplot.show(
        setup=lambda n: [shuffle([i for i in range(n)]) for j in range(n)],
        n_range=[2**k for k in range(12)],
        kernels=[
            cdjb,aurora_sorted, aurora_list_comp, kederrac, alain_chain, alain_reduce, alain_zip, binyamin_numpy
            ],
        xlabel='len(x)',
        )
    

    【讨论】:

      【解决方案2】:

      您可以使用 zip 和 chain(来自 itertools)来做到这一点:

        from itertools import chain
        aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
        aList3 = [ list(chain(*(i3 for i3 in zip(*[sl]*3)))) for sl in aList ]
      

      或使用 functools 中的 reduce(在较大的列表中会慢得多):

        from functools import reduce
        aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
        aList3 = [ list(reduce(lambda a,e:a+[e]*3,sl,[]))for sl in aList ]
      

      或带有嵌套理解的 zip(比链式压缩要快一点):

        aList  = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
        aList3 = [[i for i3 in zip(*[sl]*3) for i in i3] for sl in initial_list]
      

      【讨论】:

        【解决方案3】:

        这是一个使用 2 个 for 循环的简单示例:

        for l in initial_list:
            for j in range(0, 3*len(l), 3):
                l[j: j + 1] = [l[j]] * 3
        

        我一直在测试@CDJB 解决方案(带排序):

        from random import choice
        def test1():
            initial_list = [[choice(range(1000)) for _ in range(1000)] for _ in range(100)] 
            def my_func(initial_list):
                for l in initial_list:
                    for j in range(0, 3*len(l), 3):
                        l[j: j + 1] = [l[j]] * 3
                return initial_list
        
            my_func(initial_list)
        
        def test2():
            initial_list = [[choice(range(1000)) for _ in range(1000)] for _ in range(100)]  
            [sorted(x*3, key=x.index) for x in initial_list]
        

        结果如下:

        %timeit test2()
        1.55 s ± 5.12 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
        

        和:

        %timeit test1()
        165 ms ± 542 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
        

        这个简单的解决方案速度快 9 倍,当然取决于您的数据

        【讨论】:

          【解决方案4】:

          numpy 和 1 行代码:

           arr=np.array(initial_list)
           arr.repeat(3).reshape(3,-1)
          

          输出:

          Out[44]: 
          array([[1, 1, 1, 2, 2, 2, 3, 3, 3],
                 [4, 4, 4, 5, 5, 5, 6, 6, 6],
                 [7, 7, 7, 8, 8, 8, 9, 9, 9]])
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-06-25
            • 1970-01-01
            相关资源
            最近更新 更多