Lấy sản phẩm cartesian của một loạt các danh sách?


317

Làm cách nào tôi có thể nhận được sản phẩm của Cartesian (mọi sự kết hợp có thể của các giá trị) từ một nhóm danh sách?

Đầu vào:

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

Sản phẩm chất lượng:

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

24
lưu ý rằng 'mọi sự kết hợp có thể' không hoàn toàn giống với 'sản phẩm của Cartesian', vì trong các sản phẩm của Cartesian, các bản sao được cho phép.
Triptych

7
Có một phiên bản không trùng lặp của sản phẩm cartesian?
KJW

16
@KJW Có,set(cartesian product)
NoBugs

5
Không nên có sự trùng lặp trong một sản phẩm của Cartesian, trừ khi danh sách đầu vào có chứa các bản sao. Nếu bạn không muốn trùng lặp trong sản phẩm của Cartesian, hãy sử dụng set(inputlist)trên tất cả các danh sách đầu vào của bạn. Không có kết quả.
CamilB

@Triptych gì? Định nghĩa tiêu chuẩn của một sản phẩm của Cartesian là một bộ. Tại sao nhiều người upvote?
PascalIv

Câu trả lời:


378

itertools.product

Có sẵn từ Python 2.6.

import itertools

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

Giống như,

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

22
Chỉ muốn thêm ký tự '*' là bắt buộc nếu bạn sử dụng biến số somelists theo quy định của OP.
brian buck

1
@jaska: product()tạo nitems_in_a_list ** nlistscác phần tử trong kết quả ( reduce(mul, map(len, somelists))). Không có lý do để tin rằng năng suất là yếu tố duy nhất được không O(nlists)(khấu hao) tức là, mức độ phức tạp thời gian là tương tự như đối đơn giản lồng nhau for-loops ví dụ, cho đầu vào trong câu hỏi: nlists=3, tổng số phần tử trong kết quả: 3*2*2, và mỗi phần tử có nlistscác mục ( 3trong trường hợp này).
jfs

2
Việc sử dụng *trước khi somelists là gì? Nó làm gì?
Vineet Kumar Doshi

6
@VineetKumarDoshi: Ở đây, nó được sử dụng để hủy bỏ danh sách thành nhiều đối số cho lệnh gọi hàm. Đọc thêm tại đây: stackoverflow.com/questions/36901/
Mạnh

4
Lưu ý: Điều này chỉ hoạt động nếu mỗi danh sách chứa ít nhất một mục
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

Đối với Python 2.5 trở lên:

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

Đây là phiên bản đệ quy của product()(chỉ là một minh họa):

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

Thí dụ:

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

Phiên bản đệ quy không hoạt động nếu một số trong số đó argslà các trình vòng lặp.
jfs

20

với itertools.product :

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

6
Việc sử dụng *trước khi somelists là gì?
Vineet Kumar Doshi

@VineetKumarDoshi "sản phẩm (somelists)" là sản phẩm của cartesian giữa các danh sách con theo cách mà Python trước tiên lấy "[1, 2, 3]" làm thành phần và sau đó lấy phần tử khác sau dấu phẩy tiếp theo và đó là dòng sản phẩm đầu tiên thuật ngữ là ([1, 2, 3],), similary cho lần thứ hai ([4, 5],) và vì vậy "[([1, 2, 3],), ([4, 5],), ( [6, 7],)] " . Nếu bạn muốn có được một sản phẩm cartesian giữa các yếu tố bên trong các bộ dữ liệu, bạn cần nói với Python với Asterisk về cấu trúc bộ dữ liệu. Đối với từ điển, bạn sử dụng **. Thêm ở đây .
hhh

19

Tôi sẽ sử dụng danh sách hiểu:

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
Tôi thực sự thích giải pháp này bằng cách sử dụng danh sách hiểu. Tôi không biết tại sao không được nâng cấp nhiều hơn, nó rất đơn giản.
llekn

20
@llekn vì mã dường như được cố định với số lượng danh sách
Bằng Rikimaru

11

Đây là một trình tạo đệ quy, không lưu trữ bất kỳ danh sách tạm thời nào

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

Đầu ra:

[(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
Chúng được lưu trữ trong ngăn xếp, mặc dù.
Quentin Pradet

@QuentinPradet có nghĩa là một trình tạo như thế def f(): while True: yield 1sẽ tiếp tục tăng kích thước ngăn xếp của nó khi chúng ta đi qua nó?
Anurag Uniyal

@QuentinPradet yeah, nhưng ngay cả trong trường hợp này chỉ có stack cần cho độ sâu tối đa, không phải toàn bộ danh sách, vì vậy trong trường hợp này stack 3
Anurag Uniyal 17/03/2015

Đó là sự thật, xin lỗi. Một điểm chuẩn có thể thú vị. :)
Quentin Pradet

11

Trong Python 2.6 trở lên, bạn có thể sử dụng 'itertools.product`. Trong các phiên bản cũ hơn của Python, bạn có thể sử dụng tương đương (gần như - xem tài liệu) sau đây từ tài liệu , ít nhất là điểm bắt đầu:

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)

Kết quả của cả hai là một trình vòng lặp, vì vậy nếu bạn thực sự cần một danh sách để xử lý thêm, hãy sử dụng list(result).


Theo tài liệu, việc triển khai itertools.product thực tế KHÔNG xây dựng kết quả trung gian, có thể tốn kém. Sử dụng kỹ thuật này có thể thoát khỏi tầm tay khá nhanh đối với các danh sách có kích thước vừa phải.
ba

4
tôi chỉ có thể chỉ OP cho tài liệu, không đọc nó cho anh ta.

1
Mã từ tài liệu này có nghĩa là để chứng minh chức năng sản phẩm làm gì, không phải là cách giải quyết cho các phiên bản trước của Python.
Triptych

9

Mặc dù đã có nhiều câu trả lời, tôi muốn chia sẻ một vài suy nghĩ của mình:

Phương pháp lặp

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

Phương pháp đệ quy

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]

Phương pháp tiếp cận Lambda

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

Trong "Cách tiếp cận lặp lại", tại sao kết quả được khai báo là result = [[]] Tôi biết rằng đó là list_of_list nhưng nói chung ngay cả khi chúng tôi đã khai báo list_of_list, chúng tôi sử dụng [] chứ không phải [[]]
Sachin S

Tôi là một chút mới mẻ về các giải pháp Pythonic. Bạn hoặc một số người qua đường xin vui lòng viết phần hiểu danh sách theo "cách tiếp cận lặp lại" trong các vòng lặp riêng biệt?
Johnny Boy

4

Phương pháp đệ quy:

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)

Phương pháp lặp lại:

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

Một sửa đổi nhỏ cho giải pháp máy phát đệ quy ở trên trong hương vị dao động:

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

Và dĩ nhiên, một trình bao bọc làm cho nó hoạt động giống hệt như giải pháp đó:

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

với một sự đánh đổi : nó kiểm tra xem đệ quy có nên phá vỡ mỗi vòng lặp bên ngoài hay không, và một lợi ích : không mang lại kết quả cho cuộc gọi trống, ví dụ product(()), mà tôi cho rằng sẽ đúng hơn về mặt ngữ nghĩa (xem tài liệu).

Về hiểu biết danh sách: định nghĩa toán học áp dụng cho một số lượng đối số tùy ý, trong khi hiểu danh sách chỉ có thể xử lý một số lượng đã biết.


2

Chỉ cần thêm một chút vào những gì đã được nói: nếu bạn sử dụng sympy, bạn có thể sử dụng các ký hiệu thay vì các chuỗi làm cho chúng có ích về mặt toán học.

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

Về sympy .


1

Tôi tin rằng điều này hoạt động:

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

0

Cách tiếp cận Stonehenge:

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

đầu ra:

[[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']]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.