Bir dizi listenin kartezyen ürününü al?


317

Kartezyen ürünü (her olası değer birleşimini) bir grup listeden nasıl alabilirim?

Giriş:

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

Istenilen çıktı:

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

24
'olası her kombinasyonun' 'Kartezyen ürün' ile tam olarak aynı olmadığını unutmayın, çünkü Kartezyen ürünlerde kopyalara izin verilir.
Triptik

7
Kartezyen ürünün kopya olmayan bir versiyonu var mı?
KJW

16
@KJW Evet,set(cartesian product)
NoBugs

5
Girdi listeleri kendilerinin kopya içermediği sürece, Kartezyen bir üründe kopya olmamalıdır. Kartezyen üründe hiçbir kopya istemiyorsanız, set(inputlist)tüm giriş listelerinizi kullanın. Sonuçta değil.
CamilB

@Triptych ne? Kartezyen bir ürünün standart tanımı bir settir. Neden bu kadar çok insan yükseliyor?
PascalIv

Yanıtlar:


378

itertools.product

Python 2.6'dan edinilebilir.

import itertools

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

Hangisi ile aynı,

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

22
OP tarafından sağlanan değişken somelistleri kullanırsanız sadece '*' karakterini eklemek istersiniz.
brian buck

1
@jaska: sonuçtaki öğeleri product()oluşturur nitems_in_a_list ** nlists( reduce(mul, map(len, somelists))). Tek bir öğenin verilmesinin O(nlists)(amortismana tabi tutulmadığına ) inanmak için bir neden yoktur , yani zaman karmaşıklığı basit iç içe fordöngülerle aynıdır, örneğin, sorudaki girdi için :, sonuçtakinlists=3 toplam öğe sayısı:, 3*2*2ve her öğenin nlistsöğeleri vardır ( 3bu durumda).
jfs

2
Ne işe yarar *somelists önce? Bu ne işe yarıyor?
Vineet Kumar Doshi

6
@VineetKumarDoshi: Burada, bir listeyi işlev çağrısına yönelik birden çok argümana çıkarmak için kullanılır. Daha fazla
bilgiyi

4
Not: Bu yalnızca her liste en az bir öğe içeriyorsa çalışır
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 ve üstü için:

>>> [(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)]

İşte özyinelemeli bir sürümü product()(sadece bir örnek):

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

Misal:

>>> 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())
[()]

Bazıları argsyineleyiciler ise özyinelemeli sürüm çalışmaz .
jfs

20

ile itertools.product :

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

6
Ne işe yarar *somelists önce?
Vineet Kumar Doshi

@VineetKumarDoshi "ürün (somelistler)" , alt listeler arasında Python'un ilk olarak bir eleman olarak "[1, 2, 3]" elde edip bir sonraki komutandan sonra başka bir eleman aldığı ve bu da ilk ürün terim ([1, 2, 3],), ikinciye benzerlik ([4, 5],) ve benzeri "[([1, 2, 3],), ([4, 5],), ( [6, 7],)] " . Tuples içindeki elemanlar arasında kartezyen bir ürün almak istiyorsanız, Python'a Asterisk ile tuple yapısını anlatmanız gerekir. Sözlük için ** kullanın. Daha burada .
hhh

19

Liste kavrayışını kullanırdım:

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
Liste çözümlerini kullanarak bu çözümü gerçekten seviyorum. Neden daha fazla oy verilmediğini bilmiyorum, çok basit.
17'de llekn

20
@llekn, çünkü kod listelerin sayısına sabit görünüyor
Bằng Rikimaru

11

Geçici listeleri saklamayan yinelemeli bir jeneratör var

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]]))

Çıktı:

[(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
Yine de, yığın halinde saklanırlar.
Quentin Pradet

@QuentinPradet, benzeri bir jeneratörün, ilerledikçe def f(): while True: yield 1yığın boyutunu artırmaya devam edeceğini mi kastediyorsunuz ?
Anurag Uniyal

@QuentinPradet evet, ama bu durumda bile sadece liste derinliği için gerekli olan yığın, tüm liste için değil, bu durumda 3 yığını
Anurag Uniyal

Doğru, özür dilerim. Bir kıyaslama ilginç olabilir. :)
Quentin Pradet

11

Python 2.6 ve üzeri sürümlerde 'itertools.product` kullanabilirsiniz. Python'un daha eski sürümlerinde , en azından bir başlangıç ​​noktası olarak , belgelerden aşağıdaki (neredeyse - belgelere bakın) eşdeğer kodu kullanabilirsiniz :

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)

Her ikisinin sonucu bir yineleyicidir, bu nedenle daha fazla işlem için gerçekten bir listeye ihtiyacınız varsa kullanın list(result).


Belgelere göre, gerçek itertools.product uygulaması pahalı olabilecek ara sonuçlar oluşturmaz. Bu tekniği kullanmak orta büyüklükteki listeler için oldukça hızlı bir şekilde kontrolden çıkabilir.
Triptik

4
Ben sadece onun için okumak değil, belgelere OP işaret edebilir.

1
Belgelerdeki kod, Python'un önceki sürümleri için bir geçici çözüm olarak değil, ürün işlevinin ne yaptığını göstermek içindir.
Triptik

9

Zaten birçok cevap olmasına rağmen, bazı düşüncelerimi paylaşmak istiyorum:

Iteratif yaklaşım

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

Özyinelemeli Yaklaşım

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]

Lambda Yaklaşımı

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

"Yinelemeli Yaklaşım" da neden sonuç olarak bildirilir = [[]] Ben list_of_list olduğunu biliyorum ama genel olarak list_of_list ilan var olsa bile biz [] kullanmak değil [] kullanın
Sachin S

Pythonic çözümleri açısından biraz yeniyim. Siz ya da bir yoldan geçen kişi, liste kavrayışını "yinelemeli yaklaşım" a ayrı döngüler halinde yazar mısınız?
Johnny Boy

4

Özyinelemeli Yaklaşım:

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)

Iteratif yaklaşım:

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

Variadik aromada yukarıdaki özyinelemeli jeneratör çözümünde küçük bir değişiklik:

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

Ve elbette, bu çözümle tamamen aynı şekilde çalışmasını sağlayan bir sarıcı:

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

bir değiş tokuş ile : özyinelemenin her dış döngüde kırılıp kırılmayacağını kontrol eder ve bir kazanç : boş çağrı üzerine verim olmaz, örneğin product(()), sanırım anlamsal olarak daha doğru olur (doktora bakın).

Liste kavramaya ilişkin olarak: matematiksel tanım, rastgele sayıda argüman için geçerliyken, liste kavraması sadece bilinen bir sayı ile ilgilenebilir.


2

Daha önce söylenenlere biraz eklemek için: Eğer senfoni kullanırsanız, onları matematiksel olarak kullanışlı yapan dizeler yerine semboller kullanabilirsiniz.

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

Hakkında sympy .


1

Bunun işe yaradığına inanıyorum:

def cartesian_product(L):  
   if L:
       return {(a,) + b for a in L[0] 
                        for b in cartesian_product(L[1:])}
   else:
       return {()}

0

Stonehenge yaklaşımı:

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))

çıktı:

[[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']]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.