Search code examples
pythonpython-itertools

Python: How to generate Full Factorial Combinations?


How can I use python to generate a full factorial of combinations? Is there a fancy itertools function that generates a full factorial?

I'm drawing a blank and can't think of another way to describe this other than "full factorial of combinations", so my search efforts have turned up with nothing relevant. What's the right terminology here? Search has turned up with combinations of certain lengths, but that's not what I'm after. The length is fixed by the number of factors.

Bonus: how to exclude certain combinations? how to do for arbitrary number of factors with arbitrary number of levels with the condition that the "levels" are not necessarily strings but any python object?

Let's say I have 5 factors, each with two or more levels. In this case I have 3 two-level factors and 2 three-level factors. The full factorial of combinations would have (2^3)*(3^2) = 8*9 = 72 combinations. Keep in mind that "L1" in X1 is different than "L1" in X2.

Input:

X1 = ["L1", "L2"]
X2 = ["L1", "L2", "L3"]
X3 = ["L1", "L2"]
X4 = ["L1", "L2"]
X5 = ["L1", "L2", "L3"]

full_factorial_combinations = itertools.fancyfunction(X1, X2, X3, X4, X5)
full_factorial_combinations #optionally produces a generator instead of list of tuples shown here

Output:

[("L1", "L1", "L1", "L1", "L1"),
("L1", "L1", "L1", "L1", "L2"),
("L1", "L1", "L1", "L1", "L3"),
("L1", "L1", "L1", "L2", "L1"),
("L1", "L1", "L1", "L2", "L2"),
("L1", "L1", "L1", "L2", "L3"),
("L1", "L1", "L2", "L1", "L1"),
("L1", "L1", "L2", "L1", "L2"),
("L1", "L1", "L2", "L1", "L3"),
("L1", "L1", "L2", "L2", "L1"),
("L1", "L1", "L2", "L2", "L2"),
("L1", "L1", "L2", "L2", "L3"),
("L1", "L2", "L1", "L1", "L1"),
("L1", "L2", "L1", "L1", "L2"),
("L1", "L2", "L1", "L1", "L3"),
("L1", "L2", "L1", "L2", "L1"),
("L1", "L2", "L1", "L2", "L2"),
("L1", "L2", "L1", "L2", "L3"),
("L1", "L2", "L2", "L1", "L1"),
("L1", "L2", "L2", "L1", "L2"),
("L1", "L2", "L2", "L1", "L3"),
("L1", "L2", "L2", "L2", "L1"),
("L1", "L2", "L2", "L2", "L2"),
("L1", "L2", "L2", "L2", "L3"),
("L1", "L3", "L1", "L1", "L1"),
("L1", "L3", "L1", "L1", "L2"),
("L1", "L3", "L1", "L1", "L3"),
("L1", "L3", "L1", "L2", "L1"),
("L1", "L3", "L1", "L2", "L2"),
("L1", "L3", "L1", "L2", "L3"),
("L1", "L3", "L2", "L1", "L1"),
("L1", "L3", "L2", "L1", "L2"),
("L1", "L3", "L2", "L1", "L3"),
("L1", "L3", "L2", "L2", "L1"),
("L1", "L3", "L2", "L2", "L2"),
("L1", "L3", "L2", "L2", "L3"),
("L2", "L1", "L1", "L1", "L1"),
("L2", "L1", "L1", "L1", "L2"),
("L2", "L1", "L1", "L1", "L3"),
("L2", "L1", "L1", "L2", "L1"),
("L2", "L1", "L1", "L2", "L2"),
("L2", "L1", "L1", "L2", "L3"),
("L2", "L1", "L2", "L1", "L1"),
("L2", "L1", "L2", "L1", "L2"),
("L2", "L1", "L2", "L1", "L3"),
("L2", "L1", "L2", "L2", "L1"),
("L2", "L1", "L2", "L2", "L2"),
("L2", "L1", "L2", "L2", "L3"),
("L2", "L2", "L1", "L1", "L1"),
("L2", "L2", "L1", "L1", "L2"),
("L2", "L2", "L1", "L1", "L3"),
("L2", "L2", "L1", "L2", "L1"),
("L2", "L2", "L1", "L2", "L2"),
("L2", "L2", "L1", "L2", "L3"),
("L2", "L2", "L2", "L1", "L1"),
("L2", "L2", "L2", "L1", "L2"),
("L2", "L2", "L2", "L1", "L3"),
("L2", "L2", "L2", "L2", "L1"),
("L2", "L2", "L2", "L2", "L2"),
("L2", "L2", "L2", "L2", "L3"),
("L2", "L3", "L1", "L1", "L1"),
("L2", "L3", "L1", "L1", "L2"),
("L2", "L3", "L1", "L1", "L3"),
("L2", "L3", "L1", "L2", "L1"),
("L2", "L3", "L1", "L2", "L2"),
("L2", "L3", "L1", "L2", "L3"),
("L2", "L3", "L2", "L1", "L1"),
("L2", "L3", "L2", "L1", "L2"),
("L2", "L3", "L2", "L1", "L3"),
("L2", "L3", "L2", "L2", "L1"),
("L2", "L3", "L2", "L2", "L2"),
("L2", "L3", "L2", "L2", "L3")]

Solution

  • This is called the Cartesian product as implemented by itertools.product.

    itertools.product(X1, X2, X3, X4, X5)