【问题标题】:All combinations of a list of lists列表列表的所有组合
【发布时间】:2010-10-22 09:09:23
【问题描述】:

我基本上是在寻找Combination of List<List<int>>的python版本

给定一个列表列表,我需要一个新列表来提供列表之间所有可能的项目组合。

[[1,2,3],[4,5,6],[7,8,9,10]] -> [[1,4,7],[1,4,8],...,[3,6,10]]

列表的数量是未知的,所以我需要一些适用于所有情况的东西。优雅加分!

【问题讨论】:

    标签: python combinations


    【解决方案1】:

    你需要itertools.product:

    >>> import itertools
    >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
    >>> list(itertools.product(*a))
    [(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
    

    【讨论】:

    • 有人能解释一下*a中星号的含义吗?
    • *a 表示这些是传递给函数或方法的参数。 def fn(a,b,c): 会回复 fn(*[1,2,3]) reference
    • @mjallday,是否可以添加这些组合:(7,4,1),(8,4,1),(9,4,1),(10,4, 1),(7,5,1),(8,5,1),(9,5,1),(10,5,1) 等等?
    • @Reman 不完全清楚你想要得到什么,但如果它是,例如,每个元组的反转,你可以使用一个包装函数,将a 作为输入,迭代@ 987654330@ 和yields 都是由itertools 生成的元组和反向版本(例如,创建一个列表,reverse() 它并转换回元组)。最好提出一个新问题。
    • 如果您熟悉 javascript [*a] 将与 js 扩展运算符 [...a] 相同。 dicts 也是如此
    【解决方案2】:

    最优雅的解决方案是在 python 2.6 中使用itertools.product

    如果您使用的不是 Python 2.6,itertools.product 的文档实际上显示了一个等效功能,可以以“手动”方式完成产品:

    def product(*args, **kwds):
        # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
        # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
        pools = map(tuple, args) * kwds.get('repeat', 1)
        result = [[]]
        for pool in pools:
            result = [x+[y] for x in result for y in pool]
        for prod in result:
            yield tuple(prod)
    

    【讨论】:

      【解决方案3】:
      listOLists = [[1,2,3],[4,5,6],[7,8,9,10]]
      for list in itertools.product(*listOLists):
        print list;
      

      我希望你发现它和我第一次遇到它时一样优雅。

      【讨论】:

      • 那个分号是怎么回事? :)
      • 习惯的力量。我喜欢 Python 让你放一个分号,只是为了帮助我们这些 C/Java 程序员。但很清楚;当您执行诸如 print("foo");; 之类的操作时,它并不是真正的语句终止符;这在 C 或 Java 中是完全合法的(尽管毫无意义),但在 Python 中被禁止。
      【解决方案4】:

      这个任务的直接递归没有错,如果你需要一个可以处理字符串的版本,这可能符合你的需要:

      combinations = []
      
      def combine(terms, accum):
          last = (len(terms) == 1)
          n = len(terms[0])
          for i in range(n):
              item = accum + terms[0][i]
              if last:
                  combinations.append(item)
              else:
                  combine(terms[1:], item)
      
      
      >>> a = [['ab','cd','ef'],['12','34','56']]
      >>> combine(a, '')
      >>> print(combinations)
      ['ab12', 'ab34', 'ab56', 'cd12', 'cd34', 'cd56', 'ef12', 'ef34', 'ef56']
      

      【讨论】:

        【解决方案5】:

        Numpy 可以做到:

         >>> import numpy
         >>> a = [[1,2,3],[4,5,6],[7,8,9,10]]
         >>> [list(x) for x in numpy.array(numpy.meshgrid(*a)).T.reshape(-1,len(a))]
        [[ 1, 4, 7], [1, 5, 7], [1, 6, 7], ....]
        

        【讨论】:

        • 有人能解释一下吗?
        • 如果 a 中的东西是数组,这不起作用。 a=[[[array A], [array B], [array C]], [...]
        • 这感觉就像在问“我如何烧水”,而不是说“使用水壶”,而是“降低水周围的大气压力”。两者都有效!
        【解决方案6】:

        可以为此使用基础 python。代码需要一个函数来展平列表:

        def flatten(B):    # function needed for code below;
            A = []
            for i in B:
                if type(i) == list: A.extend(i)
                else: A.append(i)
            return A
        

        然后可以运行:

        L = [[1,2,3],[4,5,6],[7,8,9,10]]
        
        outlist =[]; templist =[[]]
        for sublist in L:
            outlist = templist; templist = [[]]
            for sitem in sublist:
                for oitem in outlist:
                    newitem = [oitem]
                    if newitem == [[]]: newitem = [sitem]
                    else: newitem = [newitem[0], sitem]
                    templist.append(flatten(newitem))
        
        outlist = list(filter(lambda x: len(x)==len(L), templist))  # remove some partial lists that also creep in;
        print(outlist)
        

        输出:

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

        【讨论】:

          【解决方案7】:

          这主要模仿使用itertools.productAnswer by Jarret Hardie 等解决方案,但有以下区别:

          • 这会将参数传递给itertools.product 内联,而不是通过变量a - 所以内联参数不需要*args 语法
          • 如果你的mypy type-linter 像我的一样,并且你可以让你的代码以其他方式“工作”使用带有内联product 参数的*args 语法(如product(*[[1,2,3],[4,5,6],[7,8,9,10]])),mypy 可能仍然会失败(类似error: No overload variant of "product" matches argument type "List[object]")
          • 所以mypy 的解决方案是不使用*args 语法,如下所示:
              >>> import itertools
              >>> list(itertools.product([1,2,3],[4,5,6],[7,8,9,10]))
              [(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]
          

          【讨论】:

            【解决方案8】:

            这个答案不如使用 itertools 干净,但这些想法可能很有用。

            从 zip() here 的构造中汲取灵感,我们可以做到以下几点。

            >>> a = iter([[1,2,3],[4,5,6],[7,8,9,10]])
            >>> sentinel = object()
            >>> result = [[]]
            >>> while True:
            >>>     l = next(a,sentinel)
            >>>     if l == sentinel:
            >>>         break
            >>>     result = [ r + [digit] for r in result for digit in l]
            >>> print(result)
            [[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 5, 10], [1, 6, 7], [1, 6, 8], [1, 6, 9], [1, 6, 10], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 4, 10], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 5, 10], [2, 6, 7], [2, 6, 8], [2, 6, 9], [2, 6, 10], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 4, 10], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 5, 10], [3, 6, 7], [3, 6, 8], [3, 6, 9], [3, 6, 10]]
            

            我们使用a 作为迭代器,以便连续获取它的下一项,而不需要先验知道有多少。当我们用完a 中的列表时,next 命令将输出sentinel(这是一个仅为进行比较而创建的对象,请参阅here 以获得一些解释),从而导致if 语句触发我们跳出循环。

            【讨论】:

            • 确实,这或多或少是 itertools.product 所做的, -- 参见 here -- 除了使用哨兵,而 itertools.product 使用 yield
            【解决方案9】:
            from itertools import product 
            list_vals = [['Brand Acronym:CBIQ', 'Brand Acronym :KMEFIC'],['Brand Country:DXB','Brand Country:BH']]
            list(product(*list_vals))
            

            输出:

            [('品牌缩写:CBIQ', '品牌国家:DXB'),
            ('品牌缩写:CBIQ','品牌国家:BH'),
            ('品牌缩写:KMEFIC','品牌国家:DXB'),
            ('品牌缩写:KMEFIC','品牌国家:BH')]

            【讨论】:

            • 这个答案应该被接受,因为它是唯一一个使用内置函数的答案,同时强调它也适用于任何类型和异构类型。
            • 这个答案与多年前提供的答案有何不同?
            • 这里的参数类型是同质的,不是异质的。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2014-07-31
            • 1970-01-01
            • 1970-01-01
            • 2017-11-27
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多