Python’s “Itertools”

Python has an itertools module, which provides a core set of fast, memory-efficient tools for creating iterators. The majority of these functions create generators. We will briefly showcase a few itertools. It is hard to overstate the utility of this module - it is strongly recommended that you take some time to see what it has in store.

Actually, there are three functions that belong in itertools, but are so useful that they are included in Python by default, and do not need to be imported. It is essential that range, enumerate, and zip become tools that you are comfortable using.

range

generate a sequence of integers in the specified “range”:

# will generate 0.. 1.. 2.. ... 8.. 9
range(10)

enumerate

“enumerate” the items in an iterable:

# will generate (0, 'apple').. (1, 'banana').. (2, 'cat').. (3, 'dog')]
enumerate(["apple", "banana", "cat", "dog"])

zip

“zip” together the corresponding elements of several iterables into tuples:

>>> names = ["Angie", "Brian", "Cassie", "David"]
>>> exam_1_scores = [90, 82, 79, 87]
>>> exam_2_scores = [95, 84, 72, 91]

# will generate ('Angie', 90, 95).. ('Brian', 82, 84).. ('Cassie', 79, 72).. ('David', 87, 91)]
>>> zip(names, exam_1_scores, exam_2_scores)
<zip at 0x20de1082608>

The following are some of the many useful tools provided by the itertools module:

itertools.chain

“chain” together multiple iterables, end-to-end:

>>> from itertools import chain
>>> gen_1 = range(0, 5, 2)               # 0.. 2.. 4
>>> gen_2 = (i**2 for i in range(3, 6))  # 9.. 16.. 25
>>> iter_3 = ["moo", "cow"]
>>> iter_4 = "him"

# will generate: 0.. 2.. 4.. 9.. 16.. 25.. 'moo'.. 'cow'.. 'h'.. 'i'.. 'm'
>>> chain(gen_1, gen_2, iter_3, iter_4)
<itertools.chain at 0x20de109ec18>

itertools.combinations Generate all length-n “combinations” from an iterable:

>>> from itertools import combinations

# will generate: (0, 1, 2).. (0, 1, 3).. (0, 2, 3).. (1, 2, 3)
>>> combinations([0, 1, 2, 3], 3)  # generate all length-3 combinations from [0, 1, 2, 3]
<itertools.combinations at 0x20de10a7728>

Reading Comprehension: Itertools I

Using the itertools.combinations function, find the probability that two randomly drawn items from the list ["apples", "bananas", "pears", "pears", "oranges"] would yield a combination of “apples” and “pears”.

Reading Comprehension: Itertools II

Given the list x_vals = [0.1, 0.3, 0.6, 0.9], create a generator, y_gen, that will generate the y-value \(y = x^2\) for each value of \(x\). Then, using zip, create a list of the \((x, y)\) pairs, each pair stored in a tuple.

Reading Comprehension: Solutions

Itertools I: Solution

>>> from itertools import combinations
>>> ls = ["apples", "bananas", "pears", "pears", "oranges"]
>>> comb_ls = list(combinations(ls, 2))
>>> comb_ls.count(("apples", "pears")) / len(comb_ls)
0.2

Itertools II: Solution

>>> x_vals = [0.1, 0.3, 0.6, 0.9]
>>> y_gen = (x**2 for x in x_vals)
>>> list(zip(x_vals, y_gen))
[(0.1, 0.01), (0.3, 0.09), (0.6, 0.36), (0.9, 0.81)]

In this instance, the use of zip is a bit contrived. We could have foregone creating y_gen by just using the following list-comprehension:

>>> x_vals = [0.1, 0.3, 0.6, 0.9]
>>> [(x, x**2) for x in x_vals]
[(0.1, 0.01), (0.3, 0.09), (0.6, 0.36), (0.9, 0.81)]