Pythonでデータ処理を効率化したいなら、itertoolsライブラリの習得は必須です。このライブラリを使いこなせば、複雑なループ処理がシンプルに書け、メモリ使用量を抑えながら高速な処理が実現できます。本記事では、実践的なコード例とともにitertoolsの主要な機能を解説します。
itertoolsとは
itertoolsはPythonの標準ライブラリで、イテレータを効率的に操作するための関数群を提供します。主な機能は以下の通りです:
- 組み合わせ系: combinations、permutations、productなどで可能な組み合わせを簡単に生成
- 無限イテレータ: count、cycle、repeatで無限シーケンスを省メモリで生成
- イテレータ加工: chain、zip_longest、isliceでイテレータを連結、変換
- グループ化: groupbyでデータをキーごとにグループ化
- フィルタリング: filterfalse、dropwhile、takewhileで条件に基づくフィルタリング
itertoolsには多くの機能がありますが、本記事では利用頻度の高いものに絞って説明します。
利用例
組み合わせと順列
組み合わせ(combinations)
要素の組み合わせを生成します。順序は考慮せず、重複も許可しません。
構文は以下の通りです。
combinations(iterable, r)
iterableは組み合わせを作成する元となるイテラブル、rは各組み合わせに含める要素数です。
以下のコードは、リストから2つの要素を選ぶ全ての組み合わせを生成します。
from itertools import combinations
fruits = ['りんご', 'バナナ', 'オレンジ', 'ぶどう']
for combo in combinations(fruits, 2):
print(combo)
# ('りんご', 'バナナ')
# ('りんご', 'オレンジ')
# ('りんご', 'ぶどう')
# ('バナナ', 'オレンジ')
# ('バナナ', 'ぶどう')
# ('オレンジ', 'ぶどう')
重複を許す組み合わせ(combinations_with_replacement)
要素の組み合わせを生成しますが、同じ要素を複数回選ぶことができます。
構文は以下の通りです。
combinations_with_replacement(iterable, r)
iterableは組み合わせを作成する元となるイテラブル、rは各組み合わせに含める要素数です。combinationsとの違いは、同じ要素を複数回選べる点です。
以下のコードは、リストから2つの要素を選ぶ全ての組み合わせを生成しますが、同じ要素を2回選ぶことも許可します。
from itertools import combinations_with_replacement
fruits = ['りんご', 'バナナ', 'オレンジ']
for combo in combinations_with_replacement(fruits, 2):
print(combo)
# ('りんご', 'りんご')
# ('りんご', 'バナナ')
# ('りんご', 'オレンジ')
# ('バナナ', 'バナナ')
# ('バナナ', 'オレンジ')
# ('オレンジ', 'オレンジ')
順列(permutations)
要素の順列を生成します。順序が異なれば別の組み合わせとして扱います。
構文は以下の通りです。
permutations(iterable, r=None)
iterableは順列を作成する元となるイテラブル、rは各順列に含める要素数です。rを省略した場合はiterableの長さになります。combinationsとの違いは、要素の順序を考慮する点です。
以下のコードは、リストから2つの要素を選ぶ全ての順列を生成します。
from itertools import permutations
fruits = ['りんご', 'バナナ', 'オレンジ']
for perm in permutations(fruits, 2):
print(perm)
# ('りんご', 'バナナ')
# ('りんご', 'オレンジ')
# ('バナナ', 'りんご')
# ('バナナ', 'オレンジ')
# ('オレンジ', 'りんご')
# ('オレンジ', 'バナナ')
直積(product)
複数のイテラブルの直積(デカルト積)を生成します。
構文は以下の通りです。
product(*iterables, repeat=1)
iterablesは直積を計算する複数のイテラブル、repeatは各イテラブルを繰り返す回数です。例えばrepeat=2の場合、A×A×B×Bのような直積を計算します。
以下のコードは、2つのリストの全ての組み合わせを生成します。
from itertools import product
fruits = ['りんご', 'バナナ']
colors = ['赤', '黄']
for item in product(fruits, colors):
print(item)
# ('りんご', '赤')
# ('りんご', '黄')
# ('バナナ', '赤')
# ('バナナ', '黄')
無限イテレータ
count
指定した数値から始まる無限の連続した数値を生成します。
構文は以下の通りです。
count(start=0, step=1)
startはカウントを開始する数値、stepは連続する数値間の差分です。デフォルトでは0から始まり、1ずつ増加します。無限に続くイテレータなので、通常はisliceなどと組み合わせて使用します。
以下のコードは、10から始まる連続した数値を5つ取得します。
from itertools import count, islice
# countは無限なので、isliceで最初の5要素だけを取得
for num in islice(count(10), 5):
print(num)
# 10
# 11
# 12
# 13
# 14
cycle
イテラブルの要素を無限に繰り返します。
構文は以下の通りです。
cycle(iterable)
iterableは繰り返し処理する要素を含むイテラブルです。このイテレータは無限に続くため、通常はisliceなどと組み合わせて使用します。
以下のコードは、リストの要素を繰り返し出力します。
from itertools import cycle, islice
colors = ['赤', '青', '緑']
# cycleは無限なので、isliceで最初の7要素だけを取得
for color in islice(cycle(colors), 7):
print(color)
# 赤
# 青
# 緑
# 赤
# 青
# 緑
# 赤
repeat
指定した要素を無限に(または指定回数)繰り返します。
構文は以下の通りです。
repeat(object, times=None)
objectは繰り返す要素、timesは繰り返す回数です。timesを省略すると無限に繰り返します。
以下のコードは、文字列を5回繰り返します。
from itertools import repeat
for msg in repeat("こんにちは", 5):
print(msg)
# こんにちは
# こんにちは
# こんにちは
# こんにちは
# こんにちは
イテレータの加工
chain
複数のイテラブルを連結して、一つのイテレータとして扱います。
構文は以下の通りです。
chain(*iterables)
iterablesは連結する複数のイテラブルです。これらのイテラブルを順番に処理し、一つの連続したイテレータとして扱います。
以下のコードは、3つのリストを連結して一つのイテレータとして処理します。
from itertools import chain
list1 = [1, 2, 3]
list2 = ['a', 'b', 'c']
list3 = [True, False]
for item in chain(list1, list2, list3):
print(item)
# 1
# 2
# 3
# a
# b
# c
# True
# False
zip_longest
複数のイテラブルを同時に処理しますが、長さが異なる場合は指定した値で埋めます。
構文は以下の通りです。
zip_longest(*iterables, fillvalue=None)
iterablesは同時に処理する複数のイテラブル、fillvalueは短いイテラブルを埋めるための値です。標準のzip関数との違いは、最も短いイテラブルが終わっても処理を続ける点です。
以下のコードは、長さの異なる2つのリストをzipで処理し、短い方のリストが終わった後は’不明’で埋めます。
from itertools import zip_longest
names = ['田中', '佐藤', '鈴木']
scores = [85, 92]
for name, score in zip_longest(names, scores, fillvalue='不明'):
print(f"{name}: {score}")
# 田中: 85
# 佐藤: 92
# 鈴木: 不明
islice
イテラブルの一部の要素だけを取り出します。
構文は以下の通りです。
islice(iterable, start, stop=None, step=1)
iterableはスライスするイテラブル、startは開始インデックス、stopは終了インデックス(この値は含まない)、stepはステップ幅です。リストのスライス操作と似ていますが、イテレータに対して使用できます。
以下のコードは、リストの2番目から5番目までの要素を取り出します。
from itertools import islice
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# 開始インデックス2、終了インデックス6(6は含まない)
for num in islice(numbers, 2, 6):
print(num)
# 2
# 3
# 4
# 5
グループ化
groupby
連続する同じ値をグループ化します。事前にソートしておくと効果的です。
構文は以下の通りです。
groupby(iterable, key=None)
iterableはグループ化するイテラブル、keyは各要素からキー値を計算する関数です。keyを省略すると要素自体がキーとして使用されます。同じキーを持つ連続した要素をグループ化します。
以下のコードは、文字列をアルファベットでグループ化します。
from itertools import groupby
# 事前にソートしておく必要がある
words = sorted(['apple', 'banana', 'apricot', 'cherry', 'blueberry'], key=lambda x: x[0])
for key, group in groupby(words, key=lambda x: x[0]):
print(f"{key}で始まる単語: {list(group)}")
# aで始まる単語: ['apple', 'apricot']
# bで始まる単語: ['banana', 'blueberry']
# cで始まる単語: ['cherry']
フィルタリング
filterfalse
条件を満たさない要素だけを取り出します。
構文は以下の通りです。
filterfalse(predicate, iterable)
predicateは各要素に適用する真偽値を返す関数、iterableはフィルタリングするイテラブルです。標準のfilter関数の逆で、predicateがFalseを返す要素だけを取り出します。
以下のコードは、偶数だけを取り出します(奇数を除外します)。
from itertools import filterfalse
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 奇数を除外(偶数だけを取り出す)
for num in filterfalse(lambda x: x % 2, numbers):
print(num)
# 2
# 4
# 6
# 8
# 10
dropwhile
条件を満たす間は要素をスキップし、条件を満たさなくなった以降の全ての要素を取り出します。
構文は以下の通りです。
dropwhile(predicate, iterable)
predicateは各要素に適用する真偽値を返す関数、iterableは処理するイテラブルです。predicateがTrueを返す間は要素をスキップし、最初にFalseを返した要素以降は全て取り出します。
以下のコードは、5より小さい数値をスキップし、それ以降の全ての要素を取り出します。
from itertools import dropwhile
numbers = [1, 3, 5, 7, 9, 2, 4, 6]
# 5より小さい数値をスキップ
for num in dropwhile(lambda x: x < 5, numbers):
print(num)
# 5
# 7
# 9
# 2
# 4
# 6
takewhile
条件を満たす間だけ要素を取り出し、条件を満たさなくなった時点で終了します。
構文は以下の通りです。
takewhile(predicate, iterable)
predicateは各要素に適用する真偽値を返す関数、iterableは処理するイテラブルです。predicateがTrueを返す間だけ要素を取り出し、最初にFalseを返した時点で終了します。dropwhileの逆の動作をします。
以下のコードは、5より小さい数値だけを取り出します。
from itertools import takewhile
numbers = [1, 3, 5, 7, 9, 2, 4, 6]
# 5より小さい数値だけを取り出す
for num in takewhile(lambda x: x < 5, numbers):
print(num)
# 1
# 3
実践的な使用例
データ分析での活用
itertoolsは、大量のデータを効率的に処理する必要があるデータ分析でも役立ちます。
以下のコードは、売上データを月ごとにグループ化して集計します。
from itertools import groupby
from operator import itemgetter
# (日付, 商品, 売上)のタプルのリスト
sales_data = [
('2025-01-15', 'A', 1200),
('2025-01-20', 'B', 800),
('2025-01-25', 'C', 950),
('2025-02-05', 'A', 1300),
('2025-02-10', 'B', 750),
('2025-02-15', 'C', 1000),
('2025-03-01', 'A', 1400),
('2025-03-10', 'B', 850),
('2025-03-20', 'C', 1100)
]
# 月ごとに集計(日付の最初の7文字で月を取得)
sorted_data = sorted(sales_data, key=lambda x: x[0][:7])
for month, items in groupby(sorted_data, key=lambda x: x[0][:7]):
total = sum(sale for _, _, sale in items)
print(f"{month}の売上合計: {total}円")
# 2025-01の売上合計: 2950円
# 2025-02の売上合計: 3050円
# 2025-03の売上合計: 3350円
アルゴリズム問題での活用
競技プログラミングや技術面接でよく出題される組み合わせ問題も、itertoolsを使えば簡単に解けます。
以下のコードは、与えられた数字の中から合計が特定の値になる組み合わせを見つけます。
from itertools import combinations
def find_combinations_with_sum(numbers, target_sum, r):
"""
numbersの中からr個選んで合計がtarget_sumになる組み合わせを全て見つける
"""
result = []
for combo in combinations(numbers, r):
if sum(combo) == target_sum:
result.append(combo)
return result
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
target_sum = 15
r = 3 # 3つの数字を選ぶ
result = find_combinations_with_sum(numbers, target_sum, r)
print(f"合計が{target_sum}になる{r}つの数字の組み合わせ:")
for combo in result:
print(combo)
# 合計が15になる3つの数字の組み合わせ:
# (1, 4, 10)
# (1, 5, 9)
# (1, 6, 8)
# (2, 3, 10)
# (2, 4, 9)
# (2, 5, 8)
# (2, 6, 7)
# (3, 4, 8)
# (3, 5, 7)
# (4, 5, 6)
まとめ
itertoolsライブラリは、Pythonでイテレータを扱う際の強力な味方です。組み合わせや順列の生成、無限イテレータの活用、イテレータの加工やフィルタリングなど、様々な場面で役立つ機能を提供します。
特に以下のような場面でitertoolsの活用を検討してみてください:
- 大量のデータを効率的に処理したい場合
- メモリ使用量を抑えながら複雑な繰り返し処理を行いたい場合
- 組み合わせや順列などの数学的な操作が必要な場合
- データのグループ化や条件付きフィルタリングを行いたい場合
itertoolsを使いこなすことで、コードはより簡潔になり、処理効率も向上します。Pythonでのデータ処理スキルを一段階上げるために、ぜひitertoolsの活用を検討してみてください。