【完全保存版】Python itertoolsモジュールの使い方|組み合わせ・繰り返し処理をラクに!

python

Pythonで繰り返し処理(ループ)を効率よく書きたいと思ったことはありませんか?

そんなときに活躍するのが、itertools(イター・ツールズ)モジュールです。

itertoolsは、標準ライブラリに含まれていて、メモリ効率が良く、高速な反復処理を実現できます。for文だけでは難しかった「組み合わせ」「繰り返し」「フィルタ処理」などが、1行で書けるようになります。

この記事では、定番の使い方から応用例、実際のプロジェクトで使える実践的なテクニックまで、itertoolsを徹底的にわかりやすく解説します。

この記事で学べること:

  • itertoolsの基本概念とイテレータの仕組み
  • 主要関数の使い方と実用例
スポンサーリンク

itertoolsとは?基本概念を理解する

itertoolsモジュールの特徴

itertoolsは、Python標準ライブラリの中でも特に強力なモジュールの一つです。

「効率的なループのためのイテレータを作成する関数」を提供します。

主な特徴:

  • メモリ効率:大量のデータを一度にメモリに読み込まない
  • 遅延評価:必要な時にのみ値を生成
  • 高速処理:C言語レベルで最適化された実装
  • 組み合わせ:複雑なループ処理を簡潔に記述
  • 標準搭載:追加インストール不要

インポート方法と基本的な使い方

import itertools

# または個別の関数をインポート
from itertools import count, cycle, repeat, product, combinations

Pythonに標準で含まれているため、追加インストールは不要です。

イテレータの基本概念

itertoolsの関数はイテレータ(iterator)を返します。

イテレータとは「一度に一つずつ値を返すオブジェクト」で、for文などと相性抜群です。

基本例:

import itertools

# 無限カウンター(1から始まる)
counter = itertools.count(1)

# 最初の5個だけ取得
for i, x in enumerate(counter):
    if i >= 5:
        break
    print(x)  # 1, 2, 3, 4, 5

出力結果:

1
2
3
4
5

重要なポイント:

  • イテレータは一度しか使えない(使い切りオブジェクト)
  • **list()**で明示的にリストに変換可能
  • 大量データでもメモリを節約できる

無限イテレータ関数群

count():無限カウンター

指定した値から無限にカウントアップするイテレータを生成します。

基本構文:

itertools.count(start=0, step=1)

実用例:

from itertools import count

# 基本的な使用例
print("1から5まで:")
for i in count(1):
    if i > 5:
        break
    print(i)

print("\n偶数を10個生成:")
even_numbers = count(0, 2)  # 0から2ずつ増加
for i, num in enumerate(even_numbers):
    if i >= 10:
        break
    print(num, end=' ')

print("\n\nIDジェネレーター:")
id_generator = count(1000)
print(f"ユーザーID: {next(id_generator)}")  # 1000
print(f"ユーザーID: {next(id_generator)}")  # 1001
print(f"ユーザーID: {next(id_generator)}")  # 1002

出力結果:

1から5まで:
1
2
3
4
5

偶数を10個生成:
0 2 4 6 8 10 12 14 16 18 

IDジェネレーター:
ユーザーID: 1000
ユーザーID: 1001
ユーザーID: 1002

cycle():要素の無限繰り返し

イテラブルの要素を無限に繰り返すイテレータを生成します。

基本構文:

itertools.cycle(iterable)

実用例:

from itertools import cycle

# 基本的な使用例
colors = cycle(['red', 'green', 'blue'])
print("色の無限ループ(最初の10個):")
for i, color in enumerate(colors):
    if i >= 10:
        break
    print(f"{i+1}: {color}")

# 実践例:ラウンドロビン方式での処理
servers = cycle(['server1', 'server2', 'server3'])
requests = ['req1', 'req2', 'req3', 'req4', 'req5', 'req6']

print("\nサーバーへの負荷分散:")
for request in requests:
    server = next(servers)
    print(f"{request} → {server}")

出力結果:

色の無限ループ(最初の10個):
1: red
2: green
3: blue
4: red
5: green
6: blue
7: red
8: green
9: blue
10: red

サーバーへの負荷分散:
req1 → server1
req2 → server2
req3 → server3
req4 → server1
req5 → server2
req6 → server3

repeat():同じ値の繰り返し

指定した値を指定回数(または無限に)繰り返すイテレータを生成します。

基本構文:

itertools.repeat(object, times=None)

実用例:

from itertools import repeat

# 基本的な使用例
print("0を5回繰り返し:")
zeros = repeat(0, 5)
print(list(zeros))

# 無限繰り返し(危険なので注意!)
print("\n'Hello'を3回繰り返し:")
hellos = repeat('Hello', 3)
for greeting in hellos:
    print(greeting)

# 実践例:初期値リストの作成
print("\nマトリックスの初期化:")
matrix_size = 3
initial_value = 0
matrix = [list(repeat(initial_value, matrix_size)) for _ in range(matrix_size)]
for row in matrix:
    print(row)

# map()との組み合わせ例
print("\n各要素を2倍にする:")
numbers = [1, 2, 3, 4, 5]
multiplier = repeat(2)
doubled = list(map(lambda x, y: x * y, numbers, multiplier))
print(f"元の数値: {numbers}")
print(f"2倍した結果: {doubled}")

出力結果:

0を5回繰り返し:
[0, 0, 0, 0, 0]

'Hello'を3回繰り返し:
Hello
Hello
Hello

マトリックスの初期化:
[0, 0, 0]
[0, 0, 0]
[0, 0, 0]

各要素を2倍にする:
元の数値: [1, 2, 3, 4, 5]
2倍した結果: [2, 4, 6, 8, 10]

組み合わせ・順列関数群

product():直積(デカルト積)

複数のイテラブルの直積を生成します。全ての組み合わせを作成する際に便利です。

基本構文:

itertools.product(*iterables, repeat=1)

実用例:

from itertools import product

# 基本的な使用例
colors = ['red', 'green', 'blue']
sizes = ['S', 'M', 'L']

print("商品の全組み合わせ:")
for item in product(colors, sizes):
    print(f"色: {item[0]}, サイズ: {item[1]}")

# repeatオプションの使用
print("\n3文字の組み合わせ(最初の10個のみ):")
letters = ['a', 'b', 'c']
for i, combo in enumerate(product(letters, repeat=3)):
    if i >= 10:
        break
    print(''.join(combo))

# 実践例:座標の生成
print("\n3x3グリッドの座標:")
for x, y in product(range(3), range(3)):
    print(f"({x}, {y})", end=' ')
print()

# パスワード生成例(簡単な例)
import string
print("\n4文字の英数字パスワード(最初の20個):")
chars = string.ascii_lowercase[:3] + string.digits[:3]  # 'abc123'
for i, pwd in enumerate(product(chars, repeat=4)):
    if i >= 20:
        break
    print(''.join(pwd), end=' ')
print()

出力結果:

商品の全組み合わせ:
色: red, サイズ: S
色: red, サイズ: M
色: red, サイズ: L
色: green, サイズ: S
色: green, サイズ: M
色: green, サイズ: L
色: blue, サイズ: S
色: blue, サイズ: M
色: blue, サイズ: L

3文字の組み合わせ(最初の10個のみ):
aaa
aab
aac
aba
abb
abc
aca
acb
acc
baa

3x3グリッドの座標:
(0, 0) (0, 1) (0, 2) (1, 0) (1, 1) (1, 2) (2, 0) (2, 1) (2, 2) 

4文字の英数字パスワード(最初の20個):
aaaa aaab aaac aaa1 aaa2 aaa3 aaba aabb aabc aab1 aab2 aab3 aaca aacb aacc aac1 aac2 aac3 aa1a aa1b

permutations():順列

要素の順列(並び順が重要)を生成します。

基本構文:

itertools.permutations(iterable, r=None)

実用例:

from itertools import permutations

# 基本的な使用例
members = ['Alice', 'Bob', 'Charlie']
print("3人の並び順(順列):")
for i, perm in enumerate(permutations(members)):
    print(f"{i+1}: {perm}")

# 長さを指定した順列
print("\n2人ずつの並び順:")
for perm in permutations(members, 2):
    print(perm)

# 実践例:レースの順位
print("\nレースの順位(上位3名):")
runners = ['田中', '佐藤', '鈴木', '高橋']
for i, ranking in enumerate(permutations(runners, 3)):
    if i >= 6:  # 最初の6パターンのみ表示
        break
    print(f"1位: {ranking[0]}, 2位: {ranking[1]}, 3位: {ranking[2]}")

# 文字列の順列
print("\n'ABC'の全ての並び替え:")
for perm in permutations('ABC'):
    print(''.join(perm))

出力結果:

3人の並び順(順列):
1: ('Alice', 'Bob', 'Charlie')
2: ('Alice', 'Charlie', 'Bob')
3: ('Bob', 'Alice', 'Charlie')
4: ('Bob', 'Charlie', 'Alice')
5: ('Charlie', 'Alice', 'Bob')
6: ('Charlie', 'Bob', 'Alice')

2人ずつの並び順:
('Alice', 'Bob')
('Alice', 'Charlie')
('Bob', 'Alice')
('Bob', 'Charlie')
('Charlie', 'Alice')
('Charlie', 'Bob')

レースの順位(上位3名):
1位: 田中, 2位: 佐藤, 3位: 鈴木
1位: 田中, 2位: 佐藤, 3位: 高橋
1位: 田中, 2位: 鈴木, 3位: 佐藤
1位: 田中, 2位: 鈴木, 3位: 高橋
1位: 田中, 2位: 高橋, 3位: 佐藤
1位: 田中, 2位: 高橋, 3位: 鈴木

'ABC'の全ての並び替え:
ABC
ACB
BAC
BCA
CAB
CBA

combinations():組み合わせ

要素の組み合わせ(順序は関係なし)を生成します。

基本構文:

itertools.combinations(iterable, r)

実用例:

from itertools import combinations

# 基本的な使用例
members = ['Alice', 'Bob', 'Charlie', 'David']
print("2人組のペア:")
for pair in combinations(members, 2):
    print(pair)

# 実践例:チーム分け
print("\n3人組のチーム:")
for team in combinations(members, 3):
    print(team)

# 数値の組み合わせ
numbers = [1, 2, 3, 4, 5]
print("\n数値から3つを選ぶ組み合わせ:")
for combo in combinations(numbers, 3):
    print(combo)

# 宝くじシミュレーション
print("\n宝くじの番号(1-10から3つ選択):")
lottery_numbers = range(1, 11)
for i, combo in enumerate(combinations(lottery_numbers, 3)):
    if i >= 10:  # 最初の10通りのみ
        break
    print(f"番号: {combo}")

出力結果:

2人組のペア:
('Alice', 'Bob')
('Alice', 'Charlie')
('Alice', 'David')
('Bob', 'Charlie')
('Bob', 'David')
('Charlie', 'David')

3人組のチーム:
('Alice', 'Bob', 'Charlie')
('Alice', 'Bob', 'David')
('Alice', 'Charlie', 'David')
('Bob', 'Charlie', 'David')

数値から3つを選ぶ組み合わせ:
(1, 2, 3)
(1, 2, 4)
(1, 2, 5)
(1, 3, 4)
(1, 3, 5)
(1, 4, 5)
(2, 3, 4)
(2, 3, 5)
(2, 4, 5)
(3, 4, 5)

宝くじの番号(1-10から3つ選択):
番号: (1, 2, 3)
番号: (1, 2, 4)
番号: (1, 2, 5)
番号: (1, 2, 6)
番号: (1, 2, 7)
番号: (1, 2, 8)
番号: (1, 2, 9)
番号: (1, 2, 10)
番号: (1, 3, 4)
番号: (1, 3, 5)

combinations_with_replacement():重複組み合わせ

同じ要素を複数回選択できる組み合わせを生成します。

実用例:

from itertools import combinations_with_replacement

# 基本的な使用例
colors = ['red', 'green', 'blue']
print("重複ありの2色組み合わせ:")
for combo in combinations_with_replacement(colors, 2):
    print(combo)

# 実践例:コインの組み合わせ
coins = [1, 5, 10]  # 1円、5円、10円
print("\n硬貨3枚の組み合わせ(重複あり):")
for combo in combinations_with_replacement(coins, 3):
    total = sum(combo)
    print(f"硬貨: {combo}, 合計: {total}円")

出力結果:

重複ありの2色組み合わせ:
('red', 'red')
('red', 'green')
('red', 'blue')
('green', 'green')
('green', 'blue')
('blue', 'blue')

硬貨3枚の組み合わせ(重複あり):
硬貨: (1, 1, 1), 合計: 3円
硬貨: (1, 1, 5), 合計: 7円
硬貨: (1, 1, 10), 合計: 12円
硬貨: (1, 5, 5), 合計: 11円
硬貨: (1, 5, 10), 合計: 16円
硬貨: (1, 10, 10), 合計: 21円
硬貨: (5, 5, 5), 合計: 15円
硬貨: (5, 5, 10), 合計: 20円
硬貨: (5, 10, 10), 合計: 25円
硬貨: (10, 10, 10), 合計: 30円

集約・変換関数群

accumulate():累積計算

累積和や累積積などの累積計算を行います。

基本構文:

itertools.accumulate(iterable, func=operator.add, initial=None)

実用例:

from itertools import accumulate
import operator

# 基本的な累積和
numbers = [1, 2, 3, 4, 5]
print("累積和:")
cumsum = list(accumulate(numbers))
print(f"元の数値: {numbers}")
print(f"累積和: {cumsum}")

# 累積積
print("\n累積積:")
cumulative_product = list(accumulate(numbers, operator.mul))
print(f"累積積: {cumulative_product}")

# 累積最大値
print("\n累積最大値:")
data = [3, 1, 4, 1, 5, 9, 2, 6]
cumulative_max = list(accumulate(data, max))
print(f"元のデータ: {data}")
print(f"累積最大値: {cumulative_max}")

# 実践例:売上の累積計算
print("\n月次売上の累積:")
monthly_sales = [100, 150, 120, 200, 180]
months = ['1月', '2月', '3月', '4月', '5月']
cumulative_sales = list(accumulate(monthly_sales))

for month, monthly, cumulative in zip(months, monthly_sales, cumulative_sales):
    print(f"{month}: 月次 {monthly}万円, 累積 {cumulative}万円")

# 初期値の設定
print("\n初期値ありの累積和:")
initial_value = 100
result = list(accumulate(numbers, initial=initial_value))
print(f"初期値 {initial_value} からの累積: {result}")

出力結果:

累積和:
元の数値: [1, 2, 3, 4, 5]
累積和: [1, 3, 6, 10, 15]

累積積:
累積積: [1, 2, 6, 24, 120]

累積最大値:
元のデータ: [3, 1, 4, 1, 5, 9, 2, 6]
累積最大値: [3, 3, 4, 4, 5, 9, 9, 9]

月次売上の累積:
1月: 月次 100万円, 累積 100万円
2月: 月次 150万円, 累積 250万円
3月: 月次 120万円, 累積 370万円
4月: 月次 200万円, 累積 570万円
5月: 月次 180万円, 累積 750万円

初期値ありの累積和:
初期値 100 からの累積: [100, 101, 103, 106, 110, 115]

groupby():グループ化

同じキーを持つ連続する要素をグループ化します。

基本構文:

itertools.groupby(iterable, key=None)

重要な注意点: groupby()は連続する同じ値のみをグループ化します。事前にソートが必要な場合があります。

実用例:

from itertools import groupby
from operator import itemgetter

# 基本的な使用例(連続する同じ値のグループ化)
data = [1, 1, 2, 2, 2, 3, 1, 1]
print("連続する同じ値のグループ化:")
for key, group in groupby(data):
    items = list(group)
    print(f"値 {key}: {items} (個数: {len(items)})")

# 実践例:学生の成績データのグループ化
students = [
    ('Alice', 'A'),
    ('Bob', 'B'),
    ('Charlie', 'A'),
    ('David', 'B'),
    ('Eve', 'A'),
    ('Frank', 'C')
]

# 成績でソートしてからグループ化
students_sorted = sorted(students, key=itemgetter(1))
print("\n成績別のグループ化:")
for grade, group in groupby(students_sorted, key=itemgetter(1)):
    students_in_grade = list(group)
    names = [student[0] for student in students_in_grade]
    print(f"成績 {grade}: {names}")

# 文字列の連続文字のグループ化
text = "aaabbccccaaa"
print("\n連続する文字のグループ化:")
for char, group in groupby(text):
    count = len(list(group))
    print(f"'{char}' が {count} 個連続")

# 実践例:ログデータの日付別グループ化
import datetime

log_data = [
    ('2024-01-01', 'INFO', 'System started'),
    ('2024-01-01', 'ERROR', 'Connection failed'),
    ('2024-01-01', 'INFO', 'Connection restored'),
    ('2024-01-02', 'INFO', 'Daily backup'),
    ('2024-01-02', 'WARNING', 'Low disk space'),
    ('2024-01-03', 'INFO', 'System updated')
]

print("\n日付別ログのグループ化:")
for date, group in groupby(log_data, key=itemgetter(0)):
    logs = list(group)
    print(f"\n{date} ({len(logs)}件):")
    for log in logs:
        print(f"  {log[1]}: {log[2]}")

出力結果:

連続する同じ値のグループ化:
値 1: [1, 1] (個数: 2)
値 2: [2, 2, 2] (個数: 3)
値 3: [3] (個数: 1)
値 1: [1, 1] (個数: 2)

成績別のグループ化:
成績 A: ['Alice', 'Charlie', 'Eve']
成績 B: ['Bob', 'David']
成績 C: ['Frank']

連続する文字のグループ化:
'a' が 3 個連続
'b' が 2 個連続
'c' が 4 個連続
'a' が 3 個連続

日付別ログのグループ化:

2024-01-01 (3件):
  INFO: System started
  ERROR: Connection failed
  INFO: Connection restored

2024-01-02 (2件):
  INFO: Daily backup
  WARNING: Low disk space

2024-01-03 (1件):
  INFO: System updated

フィルタ・選択関数群

takewhile()とdropwhile():条件付き要素の取得

takewhile(): 条件が真の間だけ要素を取得 dropwhile(): 条件が偽になるまで要素をスキップ

実用例:

from itertools import takewhile, dropwhile

# 基本的な使用例
numbers = [1, 3, 5, 8, 9, 11, 12, 15]

# 10未満の間だけ取得
print("takewhile - 10未満の間だけ取得:")
result1 = list(takewhile(lambda x: x < 10, numbers))
print(f"元のデータ: {numbers}")
print(f"結果: {result1}")

# 10未満の間はスキップ
print("\ndropwhile - 10未満の間はスキップ:")
result2 = list(dropwhile(lambda x: x < 10, numbers))
print(f"結果: {result2}")

# 実践例:ログファイルから特定期間のデータを抽出
log_entries = [
    "2024-01-01 09:00 System start",
    "2024-01-01 09:15 User login",
    "2024-01-01 10:00 Error occurred",
    "2024-01-01 10:30 Error resolved",
    "2024-01-01 11:00 Normal operation",
    "2024-01-01 11:30 User logout"
]

# 10時以降のログを取得
print("\n10時以降のログエントリ:")
morning_logs = list(dropwhile(
    lambda log: "10:" not in log, 
    log_entries
))
for log in morning_logs:
    print(f"  {log}")

# エラーが発生するまでのログを取得
print("\nエラー発生前のログ:")
pre_error_logs = list(takewhile(
    lambda log: "Error" not in log,
    log_entries
))
for log in pre_error_logs:
    print(f"  {log}")

出力結果:

takewhile - 10未満の間だけ取得:
元のデータ: [1, 3, 5, 8, 9, 11, 12, 15]
結果: [1, 3, 5, 8, 9]

dropwhile - 10未満の間はスキップ:
結果: [11, 12, 15]

10時以降のログエントリ:
  2024-01-01 10:00 Error occurred
  2024-01-01 10:30 Error resolved
  2024-01-01 11:00 Normal operation
  2024-01-01 11:30 User logout

エラー発生前のログ:
  2024-01-01 09:00 System start
  2024-01-01 09:15 User login

filterfalse():条件に合わない要素のフィルタ

通常のfilter()とは逆で、条件に合わない要素を取得します。

実用例:

from itertools import filterfalse

# 基本的な使用例
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 偶数ではない数値(奇数)を取得
odd_numbers = list(filterfalse(lambda x: x % 2 == 0, numbers))
print(f"奇数: {odd_numbers}")

# 実践例:不正なデータを除外
user_data = [
    {"name": "Alice", "age": 25},
    {"name": "", "age": 30},      # 名前が空
    {"name": "Bob", "age": -5},   # 年齢が負数
    {"name": "Charlie", "age": 35},
    {"name": "David", "age": 0}   # 年齢が0
]

def is_valid_user(user):
    return user["name"] and user["age"] > 0

# 無効なユーザーデータを取得
invalid_users = list(filterfalse(is_valid_user, user_data))
print(f"\n無効なユーザーデータ:")
for user in invalid_users:
    print(f"  {user}")

# 有効なユーザーデータ(比較用)
valid_users = list(filter(is_valid_user, user_data))
print(f"\n有効なユーザーデータ:")
for user in valid_users:
    print(f"  {user}")

出力結果:

奇数: [1, 3, 5, 7, 9]

無効なユーザーデータ:
  {'name': '', 'age': 30}
  {'name': 'Bob', 'age': -5}
  {'name': 'David', 'age': 0}

有効なユーザーデータ:
  {'name': 'Alice', 'age': 25}
  {'name': 'Charlie', 'age': 35}

実践的な応用例

データ処理での活用

CSVデータの効率的な処理:

from itertools import groupby, accumulate
import csv
from io import StringIO

# サンプルCSVデータ
csv_data = """date,product,sales
2024-01-01,A,100
2024-01-01,B,150
2024-01-01,A,80
2024-01-02,A,120
2024-01-02,B,200
2024-01-02,C,90"""

# CSVデータの読み込みと処理
reader = csv.DictReader(StringIO(csv_data))
data = list(reader)

# 日付でソートしてグループ化
data_sorted = sorted(data, key=lambda x: x['date'])
print("日付別売上サマリー:")

for date, group in groupby(data_sorted, key=lambda x: x['date']):
    daily_sales = [int(item['sales']) for item in group]
    total_sales = sum(daily_sales)
    products = len(daily_sales)
    print(f"{date}: {products}商品, 合計 {total_sales}")

# 累積売上の計算
all_sales = [int(item['sales']) for item in data]
cumulative_sales = list(accumulate(all_sales))
print(f"\n売上推移: {all_sales}")
print(f"累積売上: {cumulative_sales}")

Webスクレイピングでの活用

URLの組み合わせ生成:

from itertools import product

# 複数のページを効率的にスクレイピング
base_url = "https://example.com"
categories = ['electronics', 'books', 'clothing']
pages = range(1, 4)  # 1-3ページ

print("スクレイピング対象URL:")
for category, page in product(categories, pages):
    url = f"{base_url}/{category}?page={page}"
    print(f"  {url}")

# 検索パラメータの組み合わせ
search_terms = ['python', 'programming']
sort_options = ['date', 'popularity', 'price']
filters = ['new', 'used']

print(f"\n検索パラメータの組み合わせ(全{len(search_terms) * len(sort_options) * len(filters)}通り):")
for i, (term, sort, filter_type) in enumerate(product(search_terms, sort_options, filters)):
    if i >= 6:  # 最初の6つのみ表示
        break
    params = f"q={term}&sort={sort}&filter={filter_type}"
    print(f"  {params}")

ゲーム開発での活用

カードゲームのデッキ生成:

from itertools import product, combinations, cycle

# トランプのデッキ生成
suits = ['♠', '♥', '♦', '♣']
ranks = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']

# 全てのカードを生成
deck = list(product(suits, ranks))
print(f"デッキ総数: {len(deck)}枚")
print("最初の10枚:")
for i, (suit, rank) in enumerate(deck[:10]):
    print(f"  {suit}{rank}")

# ポーカーハンドの組み合わせ(5枚)
print(f"\nポーカーハンドの総組み合わせ数: {len(list(combinations(deck, 5)))} 通り")

# プレイヤーのターン管理
players = ['Player1', 'Player2', 'Player3', 'Player4']
turn_cycle = cycle(players)

print(f"\nターン順(10ターン分):")
for turn in range(10):
    current_player = next(turn_cycle)
    print(f"  ターン {turn + 1}: {current_player}")

アルゴリズム問題の解決

最適化問題の例:

from itertools import permutations, combinations

# 巡回セールスマン問題の簡単な例
cities = ['A', 'B', 'C', 'D']
distances = {
    ('A', 'B'): 10, ('A', 'C'): 15, ('A', 'D'): 20,
    ('B', 'A'): 10, ('B', 'C'): 35, ('B', 'D'): 25,
    ('C', 'A'): 15, ('C', 'B'): 35, ('C', 'D'): 30,
    ('D', 'A'): 20, ('D', 'B'): 25, ('D', 'C'): 30
}

def calculate_route_distance(route):
    """ルートの総距離を計算"""
    total = 0
    for i in range(len(route)):
        from_city = route[i]
        to_city = route[(i + 1) % len(route)]  # 最後は出発点に戻る
        total += distances.get((from_city, to_city), float('inf'))
    return total

# 全ての可能なルートを検証
print("巡回セールスマン問題の最適解:")
best_route = None
best_distance = float('inf')

for route in permutations(cities):
    distance = calculate_route_distance(route)
    if distance < best_distance:
        best_distance = distance
        best_route = route

print(f"最適ルート: {' → '.join(best_route)} → {best_route[0]}")
print(f"総距離: {best_distance}")

# ナップサック問題の例(組み合わせ最適化)
items = [
    ('本', 2, 3),      # (名前, 重さ, 価値)
    ('水', 3, 1),
    ('食料', 1, 4),
    ('服', 4, 2),
    ('工具', 5, 5)
]
capacity = 8

print(f"\nナップサック問題(容量: {capacity}):")
best_value = 0
best_combination = []

# 全ての組み合わせを試す
for r in range(1, len(items) + 1):
    for combo in combinations(items, r):
        total_weight = sum(item[1] for item in combo)
        total_value = sum(item[2] for item in combo)
        
        if total_weight <= capacity and total_value > best_value:
            best_value = total_value
            best_combination = combo

print(f"最適な組み合わせ:")
for item in best_combination:
    print(f"  {item[0]} (重さ: {item[1]}, 価値: {item[2]})")
print(f"総重量: {sum(item[1] for item in best_combination)}")
print(f"総価値: {best_value}")

パフォーマンス最適化とメモリ効率

メモリ使用量の比較

import sys
from itertools import count, repeat

def memory_comparison():
    """リストとイテレータのメモリ使用量比較"""
    
    # 大きなリスト
    large_list = list(range(100000))
    list_memory = sys.getsizeof(large_list)
    
    # イテレータ
    large_iterator = count()
    iterator_memory = sys.getsizeof(large_iterator)
    
    print("メモリ使用量の比較:")
    print(f"リスト(100,000要素): {list_memory:,} bytes")
    print(f"イテレータ: {iterator_memory:,} bytes")
    print(f"メモリ節約率: {((list_memory - iterator_memory) / list_memory * 100):.1f}%")

memory_comparison()

# 実際の処理時間比較
import time

def timing_comparison():
    """処理時間の比較"""
    
    # リスト内包表記
    start_time = time.time()
    result1 = [x**2 for x in range(100000)]
    list_time = time.time() - start_time
    
    # ジェネレータ式
    start_time = time.time()
    result2 = (x**2 for x in range(100000))
    # 実際に使用する際の時間(最初の10個のみ)
    first_10 = [next(result2) for _ in range(10)]
    generator_time = time.time() - start_time
    
    print(f"\n処理時間の比較:")
    print(f"リスト内包表記: {list_time:.4f}秒")
    print(f"ジェネレータ式: {generator_time:.4f}秒")

timing_comparison()

大量データの効率的な処理

from itertools import islice, chain

def process_large_file_efficiently(filename="large_data.txt"):
    """大量データファイルの効率的な処理例"""
    
    # サンプルデータの作成(実際のファイルがない場合)
    sample_data = [f"line_{i}: data_{i}\n" for i in range(1000)]
    
    def data_generator():
        """データを一行ずつ生成するジェネレータ"""
        for line in sample_data:
            yield line.strip()
    
    # バッチ処理の例
    def process_in_batches(data_gen, batch_size=100):
        """データをバッチごとに処理"""
        iterator = iter(data_gen())
        
        while True:
            batch = list(islice(iterator, batch_size))
            if not batch:
                break
            
            # バッチ処理の実行
            processed_count = len([line for line in batch if 'data' in line])
            print(f"バッチ処理完了: {len(batch)}行中 {processed_count}行を処理")
    
    print("大量データの効率的な処理:")
    process_in_batches(data_generator)

process_large_file_efficiently()

# 複数データソースの結合
def combine_data_sources():
    """複数のデータソースを効率的に結合"""
    
    # 複数のデータソース
    source1 = range(1, 6)        # [1, 2, 3, 4, 5]
    source2 = range(10, 16)      # [10, 11, 12, 13, 14, 15]
    source3 = range(20, 26)      # [20, 21, 22, 23, 24, 25]
    
    # chain()を使用した効率的な結合
    combined = chain(source1, source2, source3)
    
    print(f"\n複数データソースの結合:")
    print(f"結合結果: {list(combined)}")
    
    # 実際の使用例:ログファイルの結合処理
    def log_generator(prefix, count):
        for i in range(count):
            yield f"{prefix}_log_{i}"
    
    # 複数のログファイルを順次処理
    all_logs = chain(
        log_generator("system", 3),
        log_generator("error", 2),
        log_generator("access", 4)
    )
    
    print(f"ログファイル結合:")
    for log in all_logs:
        print(f"  {log}")

combine_data_sources()

よくあるパターンとベストプラクティス

エラーハンドリングと例外処理

from itertools import count, takewhile
import random

def safe_iterator_usage():
    """安全なイテレータ使用のパターン"""
    
    # 無限イテレータの安全な使用
    def safe_infinite_loop():
        counter = count(1)
        max_iterations = 1000  # 安全装置
        
        for i, value in enumerate(counter):
            if i >= max_iterations:
                print("安全装置作動: 最大反復回数に達しました")
                break
            
            # ランダムな条件で終了
            if random.random() < 0.01:  # 1%の確率で終了
                print(f"条件達成で終了: {value}")
                break
        else:
            print("正常終了")
    
    print("安全な無限ループの例:")
    safe_infinite_loop()
    
    # エラー処理を含むイテレータ使用
    def process_with_error_handling(data):
        """エラーハンドリング付きの処理"""
        for item in data:
            try:
                # 何らかの処理(エラーが発生する可能性)
                if item == 'error':
                    raise ValueError(f"エラーアイテム: {item}")
                result = item.upper()
                yield result
            except ValueError as e:
                print(f"警告: {e} - スキップします")
                continue
            except Exception as e:
                print(f"予期しないエラー: {e} - 処理を中断")
                break
    
    test_data = ['hello', 'world', 'error', 'python', 'itertools']
    print(f"\nエラーハンドリング付き処理:")
    processed = list(process_with_error_handling(test_data))
    print(f"処理結果: {processed}")

safe_iterator_usage()

デバッグとプロファイリング

from itertools import count, accumulate
import time

def debug_itertools():
    """itertoolsのデバッグテクニック"""
    
    def debug_wrapper(iterator, name="Iterator"):
        """イテレータをラップしてデバッグ情報を出力"""
        count = 0
        for item in iterator:
            count += 1
            print(f"[{name}] {count}: {item}")
            yield item
    
    # デバッグ情報付きのイテレータ使用
    print("デバッグ情報付きイテレータ:")
    numbers = range(1, 6)
    debugged_accumulate = debug_wrapper(
        accumulate(numbers), 
        "Accumulate"
    )
    
    result = list(debugged_accumulate)
    print(f"最終結果: {result}")
    
    # パフォーマンス測定
    def measure_performance(func, name, *args):
        """関数の実行時間を測定"""
        start_time = time.time()
        result = func(*args)
        end_time = time.time()
        
        # イテレータの場合は実際に消費する
        if hasattr(result, '__iter__') and not isinstance(result, (list, tuple, str)):
            result = list(result)
        
        execution_time = end_time - start_time
        print(f"{name}: {execution_time:.6f}秒")
        return result
    
    print(f"\nパフォーマンス測定:")
    data = range(10000)
    
    # 異なる方法での累積和計算
    def list_comprehension_cumsum(data):
        result = []
        total = 0
        for x in data:
            total += x
            result.append(total)
        return result
    
    def itertools_cumsum(data):
        return accumulate(data)
    
    result1 = measure_performance(list_comprehension_cumsum, "手動累積計算", data)
    result2 = measure_performance(itertools_cumsum, "itertools.accumulate", data)
    
    print(f"結果一致: {list(result1) == list(result2)}")

debug_itertools()

他ライブラリとの連携

NumPyとの連携

try:
    import numpy as np
    from itertools import product, combinations
    
    def numpy_itertools_integration():
        """NumPyとitertoolsの連携例"""
        
        # グリッド座標の生成
        x_coords = range(3)
        y_coords = range(3)
        
        # itertoolsで座標生成
        coordinates = list(product(x_coords, y_coords))
        print("グリッド座標:")
        for coord in coordinates:
            print(f"  {coord}")
        
        # NumPy配列として利用
        coord_array = np.array(coordinates)
        print(f"\nNumPy配列形状: {coord_array.shape}")
        print(f"座標配列:\n{coord_array}")
        
        # 距離計算の例
        def calculate_distances(coords):
            """各座標間の距離を計算"""
            distances = []
            for coord1, coord2 in combinations(coords, 2):
                dist = np.linalg.norm(np.array(coord1) - np.array(coord2))
                distances.append(dist)
            return distances
        
        distances = calculate_distances(coordinates)
        print(f"\n座標間距離(最初の5つ): {distances[:5]}")
    
    numpy_itertools_integration()
    
except ImportError:
    print("NumPyがインストールされていません")

pandasとの連携

try:
    import pandas as pd
    from itertools import product, groupby
    
    def pandas_itertools_integration():
        """pandasとitertoolsの連携例"""
        
        # 実験データの生成
        conditions = ['A', 'B']
        replicates = range(1, 4)
        measurements = [10, 15, 12, 18, 14, 16]
        
        # 全組み合わせのデータフレーム作成
        experiment_design = list(product(conditions, replicates))
        
        df = pd.DataFrame({
            'condition': [x[0] for x in experiment_design],
            'replicate': [x[1] for x in experiment_design],
            'measurement': measurements
        })
        
        print("実験データ:")
        print(df)
        
        # itertoolsでグループ化してからpandasで集計
        df_sorted = df.sort_values(['condition', 'replicate'])
        
        print(f"\n条件別統計:")
        for condition, group_df in df.groupby('condition'):
            measurements = group_df['measurement'].tolist()
            print(f"条件 {condition}: 平均 {np.mean(measurements):.1f}, 標準偏差 {np.std(measurements):.1f}")
    
    pandas_itertools_integration()
    
except ImportError:
    print("pandasがインストールされていません")

まとめz

itertoolsは、Pythonで繰り返し処理をスマートに書きたいときにとても強力なモジュールです。

覚えると「わざわざループを書かなくても一発でできる」ことが増え、コードの読みやすさ・速度・効率すべてがアップします。

この記事をブックマークして、ぜひプロジェクトや日々のツール作成に活用してみてください。

重要なポイント:
メモリ効率:大量データも少ないメモリで処理
高速処理:C言語レベルで最適化された実装
簡潔な記述:複雑なループを1行で表現
組み合わせ:他のライブラリとの強力な連携

コメント

タイトルとURLをコピーしました