I am using itertools
to run a numerical simulation iterating over all possible combinations of my input parameters. In the example below, I have two parameters and six possible combinations:
import itertools
x = [0, 1]
y = [100, 200, 300]
myprod = itertools.product(x, y)
for p in myprod:
print p[0], p[1]
# run myfunction using p[0] as the value of x and p[1] as the value of y
How can I get the size of myprod
(six, in the example)? I'd need to print this before the for
loop starts.
I understand myprod
is not a list. I can calculate len(list(myprod))
, but this consumes the iterator so the for
loop no longer works.
I tried:
myprod2=copy.deepcopy(myprod)
mylength = len(list(myprod2))
but this doesn't work, either. I could do:
myprod2=itertools.product(x,y)
mylength = len(list(myprod2))
but it's hardly elegant and pythonic!
To implement Kevin's answer for an arbitrary number of source iterables, combining reduce
and mul
:
>>> import functools, itertools, operator
>>> iters = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> functools.reduce(operator.mul, map(len, iters), 1)
27
>>> len(list(itertools.product(*iters)))
27
Note that this will not work if your source iterables are themselves iterators, rather than sequences, for the same reason your initial attempts to get the length of the itertools.product
failed. Python generally and itertools
specifically can work in a memory-efficient way with iterators of any length (including infinite!) so finding out lengths up-front isn't really a case it was designed to deal with.
How about:
mylength = len(x) * len(y)
Alternative solution I used:
import itertools
param = (('a', 'b'), (1, 2)) # a list of lists
# Calculate all combinations
combinations = itertools.product(*param)
# Calculate number of combinations
total_combinations = 1
for i in param:
total_combinations = total_combinations * len(i)
While this doesn't answer the question directly, very often we want to find the length of generators to estimate the progress/runtime.
For this, do consider using tqdm
's (version >= 4.42.0) wrappers around generator functions that don't forget the lengths of iterators (tqdm
is a progressbar library). E.g.,
from tqdm.contrib.itertools import product
from time import sleep
for i, j in product(range(3), range(4)):
sleep(1)
will show a progress bar. The length of the product is shown as the total
of the tqdm
object (e.g.., the 6
in 3/6 [00:03<00:03]
shown).