一連のリストのデカルト積を取得しますか?


317

リストのグループからデカルト積(値の可能なすべての組み合わせ)を取得するにはどうすればよいですか?

入力:

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

望ましい出力:

[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), (2, 'a', 5) ...]

24
「可能なすべての組み合わせ」は「デカルト積」とまったく同じではないことに注意してください。デカルト積では、重複が許可されています。
トリプティク

7
デカルト積の重複しないバージョンはありますか?
KJW 2013年

16
@KJWはい、set(cartesian product)
NoBugs

5
入力リスト自体に重複が含まれていない限り、デカルト積には重複があってはなりません。デカルト積で重複が必要ない場合は、set(inputlist)すべての入力リストに対して使用します。結果ではありません。
CamilB 2017

@Triptychなに?デカルト積の標準的な定義はセットです。なぜそれほど多くの人々が賛成票を投じるのですか?
PascalIv

回答:


378

itertools.product

Python 2.6から利用できます。

import itertools

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]
for element in itertools.product(*somelists):
    print(element)

これは同じです

for element in itertools.product([1, 2, 3], ['a', 'b'], [4, 5]):
    print(element)

22
OPによって提供される変数somelistsを使用する場合は、「*」文字を追加したいだけです。
ブライアンバック

1
@jaska:結果に要素をproduct()生成しnitems_in_a_list ** nlistsます(reduce(mul, map(len, somelists)))。単一の要素の生成がO(nlists)(償却)されないことを信じる理由はありません。つまり、時間の複雑さは単純な入れ子のforループの場合と同じです。たとえば、質問の入力nlists=33*2*2、結果の要素の総数:、各要素にはnlistsアイテムがあります(3この場合)。
jfs

2
*somelists の前の使用は何ですか?それは何をするためのものか?
Vineet Kumar Doshi 2015

6
@VineetKumarDoshi:リストを関数呼び出しの複数の引数にunpcakするために使用されます。もっとここで読む:stackoverflow.com/questions/36901/...
Moberg

4
注:これは、各リストに少なくとも1つのアイテムが含まれている場合にのみ機能します
igo

84
import itertools
>>> for i in itertools.product([1,2,3],['a','b'],[4,5]):
...         print i
...
(1, 'a', 4)
(1, 'a', 5)
(1, 'b', 4)
(1, 'b', 5)
(2, 'a', 4)
(2, 'a', 5)
(2, 'b', 4)
(2, 'b', 5)
(3, 'a', 4)
(3, 'a', 5)
(3, 'b', 4)
(3, 'b', 5)
>>>

38

Python 2.5以前の場合:

>>> [(a, b, c) for a in [1,2,3] for b in ['a','b'] for c in [4,5]]
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), 
 (2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5), 
 (3, 'b', 4), (3, 'b', 5)]

これはの再帰バージョンです product()(単なるイラスト)の。

def product(*args):
    if not args:
        return iter(((),)) # yield tuple()
    return (items + (item,) 
            for items in product(*args[:-1]) for item in args[-1])

例:

>>> list(product([1,2,3], ['a','b'], [4,5])) 
[(1, 'a', 4), (1, 'a', 5), (1, 'b', 4), (1, 'b', 5), (2, 'a', 4), 
 (2, 'a', 5), (2, 'b', 4), (2, 'b', 5), (3, 'a', 4), (3, 'a', 5), 
 (3, 'b', 4), (3, 'b', 5)]
>>> list(product([1,2,3]))
[(1,), (2,), (3,)]
>>> list(product([]))
[]
>>> list(product())
[()]

一部argsがイテレータである場合、再帰バージョンは機能しません。
jfs 2009

20

itertools.product

import itertools
result = list(itertools.product(*somelists))

6
*somelists の前の使用は何ですか?
Vineet Kumar Doshi 2015

@VineetKumarDoshi "product(somelists)"は、Pythonが最初に"[1、2、3]"を要素として取得し、次のコマンドの後に他の要素を取得するという方法で、サブリスト間のデカルト積であり、改行なので最初の積項は([1、2、3]、)で、2番目も同様です([4、5]、)なので、"[([1、2、3]、)、([4、5]、)、( [6、7]、)] "。タプル内の要素間でデカルト積を取得したい場合は、アスタリスク付きのPythonにタプル構造について通知する必要があります。辞書には**を使用します。詳細はこちら
hhh '15

19

私はリスト内包表記を使用します:

somelists = [
   [1, 2, 3],
   ['a', 'b'],
   [4, 5]
]

cart_prod = [(a,b,c) for a in somelists[0] for b in somelists[1] for c in somelists[2]]

1
私はリスト内包表記を使用したこのソリューションが本当に好きです。なぜこれ以上賛成されないのか分かりませんが、とても簡単です。
llekn 2016年

20
@lleknコードは、リストの数に固定されているように見えるので
BANG力丸

11

これは、一時的なリストを保存しない再帰的なジェネレータです。

def product(ar_list):
    if not ar_list:
        yield ()
    else:
        for a in ar_list[0]:
            for prod in product(ar_list[1:]):
                yield (a,)+prod

print list(product([[1,2],[3,4],[5,6]]))

出力:

[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]

1
ただし、それらはスタックに格納されます。
クエンティンプラデット2015年

@QuentinPradetこれは、ジェネレーターdef f(): while True: yield 1がスタックサイズを増やしていくことを意味しますか?
Anurag Uniyal 2015年

@QuentinPradetええ、でもこの場合でも、リスト全体ではなく、最大深度に必要なスタックのみなので、この場合は3のスタック
Anurag Uniyal

本当、ごめんなさい。ベンチマークは興味深いかもしれません。:)
クエンティンプラデット2015年

11

Python 2.6以降では、 'itertools.product`を使用できます。古いバージョンのPython では、少なくとも開始点として、次の(ほぼ-ドキュメントを参照)の同等のコードをドキュメントから使用できます。

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)

両方の結果はイテレータなので、さらに処理するためのリストが本当に必要な場合は、を使用しますlist(result)


ドキュメントによれば、実際のitertools.product実装は中間結果を構築しないため、コストがかかる可能性があります。この手法を使用すると、適度なサイズのリストの場合、すぐに手に負えなくなる可能性があります。
トリプティク

4
OPをドキュメントにポイントするだけで、彼のために読むことはできません。

1
ドキュメントのコードは、製品の関数が何をするかを示すことを目的としており、以前のバージョンのPythonの回避策としてではありません。
トリプティク

9

すでに多くの答えがありますが、私は私の考えのいくつかを共有したいと思います:

反復的なアプローチ

def cartesian_iterative(pools):
  result = [[]]
  for pool in pools:
    result = [x+[y] for x in result for y in pool]
  return result

再帰的アプローチ

def cartesian_recursive(pools):
  if len(pools) > 2:
    pools[0] = product(pools[0], pools[1])
    del pools[1]
    return cartesian_recursive(pools)
  else:
    pools[0] = product(pools[0], pools[1])
    del pools[1]
    return pools
def product(x, y):
  return [xx + [yy] if isinstance(xx, list) else [xx] + [yy] for xx in x for yy in y]

ラムダアプローチ

def cartesian_reduct(pools):
  return reduce(lambda x,y: product(x,y) , pools)

「反復的アプローチ」で、結果がresult = [[]]として宣言されている理由はなぜですかlist_of_listであることはわかっていますが、一般的にlist_of_listを宣言した場合でも[]を使用し、[[]]は使用しません
Sachin S

Pythonicソリューションに関しては、私は少し初心者です。あなたまたは一部の通行人は、「反復アプローチ」のリスト内包を別々のループで記述してください。
ジョニーボーイ

4

再帰的アプローチ:

def rec_cart(start, array, partial, results):
  if len(partial) == len(array):
    results.append(partial)
    return 

  for element in array[start]:
    rec_cart(start+1, array, partial+[element], results)

rec_res = []
some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]  
rec_cart(0, some_lists, [], rec_res)
print(rec_res)

反復的なアプローチ:

def itr_cart(array):
  results = [[]]
  for i in range(len(array)):
    temp = []
    for res in results:
      for element in array[i]:
        temp.append(res+[element])
    results = temp

  return results

some_lists = [[1, 2, 3], ['a', 'b'], [4, 5]]  
itr_res = itr_cart(some_lists)
print(itr_res)

3

上記の再帰的なジェネレーターソリューションのマイナーな変更は、可変個のフレーバーで:

def product_args(*args):
    if args:
        for a in args[0]:
            for prod in product_args(*args[1:]) if args[1:] else ((),):
                yield (a,) + prod

そしてもちろん、そのソリューションとまったく同じように機能させるラッパー:

def product2(ar_list):
    """
    >>> list(product(()))
    [()]
    >>> list(product2(()))
    []
    """
    return product_args(*ar_list)

1つのトレードオフ:それチェック再帰がそれぞれ外側のループ時に壊す必要がある場合、および1つのゲイン:例えば、空のコール時にノー収量、product(())私が思う、(doctestのを参照してください)意味的に、より正確になります。

リスト内包表記について:数学的定義は任意の数の引数に適用されますが、リスト内包表記は既知の数の引数しか処理できませんでした。


2

すでに述べたことに少し追加するだけです。sympyを使用する場合は、文字列ではなくシンボルを使用できるため、数学的に役立ちます。

import itertools
import sympy

x, y = sympy.symbols('x y')

somelist = [[x,y], [1,2,3], [4,5]]
somelist2 = [[1,2], [1,2,3], [4,5]]

for element in itertools.product(*somelist):
  print element

sympy



0

ストーンヘンジのアプローチ:

def giveAllLists(a, t):
    if (t + 1 == len(a)):
        x = []
        for i in a[t]:
            p = [i]
            x.append(p)
        return x
    x = []

    out = giveAllLists(a, t + 1)
    for i in a[t]:

        for j in range(len(out)):
            p = [i]
            for oz in out[j]:
                p.append(oz)
            x.append(p)
    return x

xx= [[1,2,3],[22,34,'se'],['k']]
print(giveAllLists(xx, 0))

出力:

[[1, 22, 'k'], [1, 34, 'k'], [1, 'se', 'k'], [2, 22, 'k'], [2, 34, 'k'], [2, 'se', 'k'], [3, 22, 'k'], [3, 34, 'k'], [3, 'se', 'k']]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.