概要(lambdaは「その場で小さな関数」を作り、データ処理を短く書くための道具)
lambda(無名関数)は、名前を付けずに1行で小さな関数を作る仕組みです。短い変換・条件分岐・並べ替えのキー指定などを“その場で”書けるので、前処理や集計のコードを簡潔にできます。コツは「短く・単機能・副作用なし」に徹すること。複雑になりそうならdefで関数名を付けて可読性を優先します。
基本(lambdaの構文と「使いどころ」を押さえる)
構文と最小例
lambdaの形は「lambda 引数: 式」。式の評価結果が“戻り値”になります。
double = lambda x: x * 2
print(double(5)) # 10
# 引数が複数でもOK
add = lambda a, b: a + b
print(add(3, 4)) # 7
# 条件分岐も1行で書ける
label = lambda s: "pass" if s >= 60 else "fail"
print(label(70)) # pass
Python使いどころの指針
- 一時的な小さな処理: 並び替えのキー、軽い変換、簡潔な条件分岐。
- 関数名が不要な場面: その場限りで読み捨てる処理。
- 複雑になりそうならdefへ退避: 2行以上の処理、複雑な分岐、例外処理が必要な場合は関数化して名前を付ける。
組み合わせ(組み込み関数・pandasと合わせて実用に落とす)
組み込み関数との組み合わせ(sorted/map/filter/reduce)
# 並び替え(sortedのkey)
users = [{"name": "Taro", "age": 25}, {"name": "Hanako", "age": 20}]
by_age = sorted(users, key=lambda u: u["age"])
by_name_lower = sorted(users, key=lambda u: u["name"].lower())
# 要素変換(map)
nums = [1, 2, 3]
squared = list(map(lambda x: x**2, nums)) # [1, 4, 9]
# 条件抽出(filter)
adults = list(filter(lambda u: u["age"] >= 21, users)) # [{'name': 'Taro', 'age': 25}]
# 集約(reduce)
from functools import reduce
total = reduce(lambda acc, x: acc + x, nums, 0) # 6
Pythonpandasとの組み合わせ(Series.map/apply、DataFrame.apply/assign)
import pandas as pd
df = pd.DataFrame({"name": [" Taro", "HANAKO "], "score": [85, 92]})
# 1列の要素変換(Series.map/Series.apply)
df["name_clean"] = df["name"].map(lambda s: s.strip().title())
# 行ごとの複合ロジック(DataFrame.apply axis=1)
df["label"] = df.apply(lambda r: f"{r['name_clean']}({r['score']})", axis=1)
# 連鎖的な列追加(assignで読みやすく)
out = (
df.assign(
name_clean=lambda d: d["name"].map(lambda s: s.strip().title()),
passed=lambda d: d["score"].map(lambda x: x >= 60),
)
)
Pythongroupbyと組み合わせ(グループ内変換・指標作成)
gdf = pd.DataFrame({"team": ["A","A","B"], "x": [10, 12, 8]})
# グループ内で標準化(transformを優先)
gdf["z"] = gdf.groupby("team")["x"].transform(lambda s: (s - s.mean()) / s.std(ddof=0))
Pythonグループ集計・変換はapplyよりagg/transformの方が表現力と速度で有利です。lambdaは“中の小さな式”として添える感覚で使います。
実践例(前処理・キー指定・新列作成・安全な条件分岐)
前処理を短くまとめる(空白・大小・None対策)
df = pd.DataFrame({"email": [" Taro@Example.com ", None]})
df["email_norm"] = df["email"].map(lambda s: (s or "").strip().lower())
Python並び替えキーで自然順(辞書やタプルもOK)
products = pd.DataFrame({"name": ["A-2", "A-10", "A-1"]})
# 末尾数字でソート(キーを抽出するlambda)
out = products.sort_values("name", key=lambda s: s.str.extract(r"(\d+)", expand=False).astype(int))
Python複数列を組み合わせたラベル生成
df = pd.DataFrame({"first": ["taro", None], "last": ["yamada", "suzuki"]})
df["full"] = df.apply(lambda r: ("{} {}".format((r["first"] or "").strip().title(),
(r["last"] or "").strip().title())).strip() or "Unknown", axis=1)
Python安全な条件分岐(ゼロ割・欠損への防御)
df = pd.DataFrame({"new": [30, 0], "active": [100, 0]})
df["rate"] = df.apply(lambda r: (r["new"] / r["active"]) if r["active"] else 0.0, axis=1)
Python重要ポイントの深掘り(可読性・性能・引数と外部状態・例外)
可読性を最優先にする
lambdaは短いからこそ強い。1行で収まらない、複数ステップの処理、分岐が長いときはdefで関数名を付けて意味を明示します。レビューや保守が圧倒的に楽になります。
ベクトル化が可能ならそちらを選ぶ
pandasの列演算やstrアクセサ、NumPyの関数はlambda+applyより高速です。まず「列演算で書けるか」を検討し、どうしても行単位の複雑な組み立てが必要な箇所にだけlambdaを使います。
外部変数の取り込み(クロージャ)を理解して使う
threshold = 70
df["flag"] = df["score"].map(lambda x: x >= threshold) # 外のthresholdを捕まえる
Python関数の外にある値を中で使えます。動的に変えたい場合は、引数で渡す関数を定義(def)しておくと意図が明確です。
例外と欠損の扱いを先に決める
lambdaはtry/exceptを書きづらいので、前処理で欠損を埋める(fillna)、型を揃える(astype)、分母ゼロの防御を入れるなど“安全策”を事前に施します。必要ならdefで例外処理を含めた関数にします。
つまずき対策(NaNでdtypeが落ちる・チェーン代入・過度なlambda)
NaNが混じるとdtypeがobjectになりがち
数値演算は、先にfillna(0)やastypeで型を固定してからlambdaを適用します。日時はto_datetimeで早めに変換しておくと安全です。
チェーン代入は避け、locで一発指定
# 悪い例(コピーへ代入の恐れ)
# df[df["score"] >= 60]["grade"] = "A"
# 良い例
df.loc[df["score"] >= 60, "grade"] = "A"
Pythonlambdaで作ったSeriesを代入するときも、必ずlocで対象行・列を一発指定します。
“なんでもlambda”は禁物
並び替えのkey、map、短いapply以外で乱用すると読みにくくなります。関数に名前を付ける(def)・列演算へ置き換える・groupbyのagg/transformへ寄せる、を常に検討しましょう。
まとめ(lambdaは“短い一時関数”。pandasではmap/applyと組み合わせ、可読性と性能を両立)
lambdaは、その場で小さな関数を作るための道具です。組み込み関数(sorted/map/filter/reduce)やpandasのSeries.map/DataFrame.apply/assign/transformと組み合わせると、前処理・並び替え・新列作成を短く書けます。可読性を最優先にし、ベクトル化できる箇所は列演算へ寄せる。欠損・型・ゼロ割などの安全策を事前に施し、複雑化したら迷わずdefへ。これを徹底すれば、初心者でも“短く、正確に、速く”、気持ちのいいデータ処理が書けるようになります。
