[Python] Creating Combinations, Product Sets, and Cartesian Products with a Dictionary

Posted by

Want to know about combinations and product sets from multiple items?

Combination

A combination is a selection of r items from a set of n distinct items where the order of selection does not matter. The number of combinations is calculated using the formula

C(n, r) = \frac{n!}{r!(n-r)!}

Here, n! denotes the factorial of n, and C(n,r) represents the number of combinations of choosing r items from n items. For example, the number of ways to choose 2 fruits from a set of 5 distinct fruits is

C(5,2) = \frac {5!} {2!(5-2)!} =10

Product\ Set,\ Cartesian\ Product

The Product Set or Cartesian Product is a set that contains all possible ordered pairs formed by combining elements from two given sets. If we have two sets A and B, the Cartesian Product of A and B is defined as

A \times B=\{ (a,b) \mid a \in A\ and\  b \in B \}

where a and b are elements of sets A and B, respectively.

For example, if set\ A = \{ 1, 2 \} and set\ B = \{ x, y \}, the Cartesian Product of A and B would be

A \times B=\{ (1,x),(1,y),(2,x),(2,y) \}

The Cartesian Product represents all possible combinations of elements from the two sets, allowing for the representation of all possible states of two variables.

When coding, sometimes you need to create cases with combinations of values and loop through them for calculations or analysis.

Similarly, I needed the product set of various values. My problem was to check all possible combinations composed of one element from each of three lists.

list_a = [1, 2, 3]
list_b = ['a', 'b']
list_c = [2021, 2022]

result = [(1, 'a', 2021), (1, 'a', 2022), (1, 'b', 2021), (1, 'b', 2022), (2, 'a', 2021), (2, 'a', 2022), (2, 'b', 2021), (2, 'b', 2022), (3, 'a', 2021), (3, 'a', 2022), (3, 'b', 2021), (3, 'b', 2022)]

It’s not difficult to write this result by hand, but it becomes complex or prone to mistakes as the list elements increase. In such cases, it’s convenient and accurate to use Python’s standard library, itertools.

from itertools import product

result = product(list_a, list_b, list_c) 

print(result)

This returns an iterator <itertools.product object at 0x000020D5....>. Wrapping it with list() will display it correctly.

from itertools import product

result = list(product(list_a, list_b, list_c))
print(result)
>> [(1, 'a', 2021), (1, 'a', 2022), (1, 'b', 2021), (1, 'b', 2022), (2, 'a', 2021), (2, 'a', 2022), (2, 'b', 2021), (2, 'b', 2022), (3, 'a', 2021), (3, 'a', 2022), (3, 'b', 2021), (3, 'b', 2022)]

However, doing this requires defining the list each time, and it becomes difficult to name the list based on the variable type or specific value(although there are ways).

In such cases, my preferred method is to use a dictionary.

dict_for_product = {}

Declaring this allows you to create variables dynamically by specifying key and value pairs, and managing them in one dictionary based on their characteristics makes post-management easier.

dict_for_product['list_a'] = [1, 2, 3]
dict_for_product['list_b'] = ['a', 'b']
dict_for_product['list_c'] = [2021, 2022]

print(dict_for_product)
>> {'list_a': [1, 2, 3], 'list_b': ['a', 'b'], 'list_c': [2021, 2022]}

Applying the product function to the keys of the dictionary dict_for_product will display the keys as tuples.

from itertools import product

list(product(dict_for_product))
>> [('list_a',), ('list_b',), ('list_c',)]

So what should we do? Let’s create a function that takes the values of the dictionary as arguments and generates their product set, as shown below.

from itertools import product

def product_dict(**kwargs):
    vals = kwargs.values()
    for instance in product(*vals):
        yield instance

Using it like this will give us the desired result.

list(product_dict(**dict_for_product))
>> [(1, 'a', 2021),
 (1, 'a', 2022),
 (1, 'b', 2021),
 (1, 'b', 2022),
 (2, 'a', 2021),
 (2, 'a', 2022),
 (2, 'b', 2021),
 (2, 'b', 2022),
 (3, 'a', 2021),
 (3, 'a', 2022),
 (3, 'b', 2021),
 (3, 'b', 2022)]

However, there are times when you might wonder which list value in the dictionary each element in the tuple comes from. In such cases, you can create and use a function that takes keys and values as variables and combines them using the zip function.

from itertools import product

def product_dict(**kwargs):
    keys = kwargs.keys()
    vals = kwargs.values()
    for instance in product(*vals):
        yield dict(zip(keys, instance))

list(product_dict(**dict_for_product))
>> [{'list_a': 1, 'list_b': 'a', 'list_c': 2021},
 {'list_a': 1, 'list_b': 'a', 'list_c': 2022},
 {'list_a': 1, 'list_b': 'b', 'list_c': 2021},
 {'list_a': 1, 'list_b': 'b', 'list_c': 2022},
 {'list_a': 2, 'list_b': 'a', 'list_c': 2021},
 {'list_a': 2, 'list_b': 'a', 'list_c': 2022},
 {'list_a': 2, 'list_b': 'b', 'list_c': 2021},
 {'list_a': 2, 'list_b': 'b', 'list_c': 2022},
 {'list_a': 3, 'list_b': 'a', 'list_c': 2021},
 {'list_a': 3, 'list_b': 'a', 'list_c': 2022},
 {'list_a': 3, 'list_b': 'b', 'list_c': 2021},
 {'list_a': 3, 'list_b': 'b', 'list_c': 2022}]

Now, you can directly create the desired variables within the program(inside the dictionary) and generate combinations as well.

Leave a Reply

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다