概要(groupby は「連続する同じキーでまとまりを作る」ための基本ツール)
itertools.groupby は、並んだデータを「キーが同じものが連続している区間」ごとにグループ化するイテレータです。重要なのは「連続している要素だけが同じグループになる」こと。つまり、同じキーが散らばっていると一つにまとまらず、通常は「グループ化したいキーであらかじめソートしておく」必要があります。結果は (key, group_iterator) のペアを順に返し、group_iterator は一度きりの消費型(必要なら list(…) で取り出す)です。
from itertools import groupby
data = [1, 1, 2, 3, 3, 3, 2]
for key, grp in groupby(data):
print(key, list(grp))
# 1 [1, 1]
# 2 [2]
# 3 [3, 3, 3]
# 2 [2] ← “バラバラ”に現れる 2 は別グループとして分かれる
Python基本の使い方(ここが重要)
まず「グループ化したいキー」でソートしてから groupby する
groupby は“連続する同じキー”を束ねるので、グループ化の前に並べ替えるのが定石です。辞書やオブジェクトのフィールドでグループ化するなら、同じキー関数で sort と groupby を揃えます。
from itertools import groupby
from operator import itemgetter
rows = [
{"team": "B", "user": "taro"},
{"team": "A", "user": "hanako"},
{"team": "A", "user": "mika"},
{"team": "B", "user": "jiro"},
]
rows.sort(key=itemgetter("team")) # 重要:同じキー関数でソート
for team, grp in groupby(rows, key=itemgetter("team")):
users = [r["user"] for r in grp] # grp は一度きりのイテレータ
print(team, users)
# A ['hanako', 'mika']
# B ['taro', 'jiro']
Pythonkey 引数で「何を基準にまとめるか」を決める
key=None(省略)なら要素自体で連続区間をまとめます。複雑な構造なら key=lambda r: r[“col”] のように関数を渡します。
from itertools import groupby
words = ["apple", "apricot", "banana", "blueberry", "cherry"]
words.sort(key=lambda w: w[0]) # 先頭文字で並べる
for initial, grp in groupby(words, key=lambda w: w[0]):
print(initial, list(grp))
# a ['apple', 'apricot']
# b ['banana', 'blueberry']
# c ['cherry']
Pythongroup は「一度きりの消費型」。必要分だけ取り出す
groupby が返す grp はイテレータなので、反復すると消費されます。後で再利用したいなら list(grp) で素材化、またはすぐ集計・出力に使います。
重要ポイントの深掘り(区間グループ化の本質・ソートと整合・落とし穴)
groupby は「連続区間」を作る。散在データの“集合”化ではない
同じキーが散在している生データにそのまま groupby をかけると、同じキーでも複数グループに分かれます。全てを一つに“集合”化したいなら、defaultdict(list) などでキー別に集めるアプローチが適切です(groupby は「順序付きの区間処理」に強い)。
# 集合化したいなら defaultdict を使う
from collections import defaultdict
rows = [{"team": "B", "user": "taro"}, {"team": "A", "user": "hanako"}, {"team": "B", "user": "jiro"}]
box = defaultdict(list)
for r in rows:
box[r["team"]].append(r["user"])
print(box) # {'B': ['taro', 'jiro'], 'A': ['hanako']}
Pythonsort の key と groupby の key を“必ず一致”させる
この整合が崩れると、意図しない分割になります。itemgetter や同じ lambda を使い回し、並べ替えとグループ化の基準を揃えましょう。
大量データは「流しながら処理」が基本
groupby は遅延生成で、1グループずつ手元に来ます。グループごとに集計・出力を行えば、全体をメモリに載せずに処理できます。ログや時系列の「状態が続く区間」を検出する用途に特に強いです。
実務での使いどころ(区間検出・グループ出力・ファイル分割)
連続状態の区間抽出(状態が続いた塊を得る)
例えば、連続する同じエラーレベルの区間を抽出します。出力側で区間の開始・終了を扱うと、継続時間の分析に直結します。
from itertools import groupby
logs = [
("INFO", 1), ("INFO", 2),
("WARN", 3),
("ERROR", 4), ("ERROR", 5), ("ERROR", 6),
("INFO", 7)
]
# すでに時系列順に並んでいる前提で、連続区間をまとめる
for level, grp in groupby(logs, key=lambda x: x[0]):
times = [t for _, t in grp]
print(level, (times[0], times[-1])) # 開始・終了
# INFO (1, 2)
# WARN (3, 3)
# ERROR (4, 6)
# INFO (7, 7)
Pythonソート済みデータを「キー別ファイル」に安全に分割
グループごとにまとめて書き出すことで、バッファの切り替えや最後の残りの処理を簡潔にできます。
from itertools import groupby
from operator import itemgetter
rows = [
{"dept": 10, "name": "scott"},
{"dept": 10, "name": "john"},
{"dept": 20, "name": "bob"},
{"dept": 20, "name": "maria"},
]
rows.sort(key=itemgetter("dept"))
for dept, grp in groupby(rows, key=itemgetter("dept")):
with open(f"dept-{dept}.txt", "w", encoding="utf-8", newline="\n") as f:
for r in grp:
f.write(r["name"] + "\n")
Python見出しで区切られたレポートを「セクションごと」に処理
連続する同じヘッダ種別でグループ化して、セクション単位の集計やレンダリングに使えます。
例題で身につける(定番から一歩先まで)
例題1:先頭文字ごとに単語をまとめて表示(要ソート)
from itertools import groupby
words = ["blueberry", "banana", "apple", "apricot", "cherry"]
words.sort(key=lambda w: w[0])
for initial, grp in groupby(words, key=lambda w: w[0]):
print(initial, list(grp))
Python例題2:CSV の列でグループ化して、各グループの合計を出す
import csv
from itertools import groupby
from operator import itemgetter
def sum_by_col(path: str, key_col: int, val_col: int):
with open(path, "r", encoding="utf-8", newline="") as f:
rows = list(csv.reader(f))
rows.sort(key=itemgetter(key_col)) # グループ化列でソート
for key, grp in groupby(rows, key=itemgetter(key_col)):
total = sum(int(r[val_col]) for r in grp)
print(key, total)
Python例題3:時系列の「同じ状態が続く区間」の長さを測る
from itertools import groupby
series = [("idle", 0), ("idle", 1), ("busy", 2), ("busy", 3), ("idle", 4)]
for state, grp in groupby(series, key=lambda x: x[0]):
ts = [t for _, t in grp]
duration = ts[-1] - ts[0] + 1
print(state, duration)
Python例題4:グループごとの先頭・末尾だけを効率よく取得
from itertools import groupby
nums = [1, 1, 1, 2, 2, 3, 1]
for key, grp in groupby(nums):
g = list(grp)
print(key, g[0], g[-1]) # 各区間の先頭・末尾
Pythonまとめ
groupby は「並んだデータの連続区間をキーごとに束ねる」ためのイテレータです。散在する同キーを“1か所に集める”機能ではないため、グループ化前のソートと、sort の key と groupby の key を一致させる設計が最重要ポイント。返されるグループは消費型イテレータなので、その場で集計・出力するか、必要に応じて list へ素材化します。ログの区間検出、ソート済みデータのキー別出力、時系列の状態区間の分析など、区間を意識した処理で強力に機能します。まずは「ソート→groupby→グループ単位で処理」という基本線を体に覚えさせると、現場のコードが短く、読みやすく、堅牢になります。
