【Pandas】applyとlambdaで列・行に対する処理をまとめる

Pandas

こんにちは、かつコーチです。
「標準のメソッドだけでは実現できない、独自の変換処理をしたい」というときに活躍するのがapplylambdaです。
少しとっつきにくく感じる方も多い組み合わせですが、パターンを覚えてしまえば実務で頻繁に使うことになります。

applyとlambdaとは?

applyの定義:各要素・各行に関数を適用する

applyとは、Series(列)の各要素、またはDataFrameの各行・各列に対して、指定した関数を適用するためのメソッドです。

import pandas as pd

df = pd.DataFrame({
    "商品名": ["りんご", "みかん", "ぶどう"],
    "価格": [150, 100, 300]
})

def add_tax(price):
    return int(price * 1.1)

df["税込価格"] = df["価格"].apply(add_tax)
print(df)
   商品名   価格  税込価格
0  りんご  150    165
1  みかん  100    110
2  ぶどう  300    330

lambdaの定義:名前をつけない一時的な関数

lambda(ラムダ)とは、defを使わずにその場限りの簡単な関数を定義できる書き方です。

df["税込価格"] = df["価格"].apply(lambda price: int(price * 1.1))

先ほどのadd_tax関数と同じ処理を、1行で書けています。

なぜapply×lambdaが重要なのか

pandasにはsummeanのような標準の集計・変換メソッドが数多く用意されていますが、「独自のルールで新しい列を作りたい」といった場面では、標準メソッドだけでは対応できません。
applylambdaを組み合わせることで、どんな複雑な条件・変換処理も列・行単位で柔軟に実装できるようになります。

基本の書き方:apply・lambdaの使い方

手順1:Seriesに対するapply(1列の各値を変換)

df["価格帯"] = df["価格"].apply(lambda p: "高価格" if p >= 200 else "通常価格")
print(df)
   商品名   価格  価格帯
0  りんご  150  通常価格
1  みかん  100  通常価格
2  ぶどう  300  高価格

手順2:DataFrameに対するapply(行ごとに複数列を使う処理)

複数の列を組み合わせて判定したい場合は、axis=1を指定して行単位で処理します。

df2 = pd.DataFrame({
    "商品名": ["りんご", "みかん", "ぶどう"],
    "価格": [150, 100, 300],
    "在庫": [5, 0, 10]
})

def check_status(row):
    if row["在庫"] == 0:
        return "在庫切れ"
    elif row["価格"] >= 200:
        return "高価格"
    else:
        return "通常"

df2["ステータス"] = df2.apply(check_status, axis=1)
print(df2)
   商品名   価格  在庫  ステータス
0  りんご  150   5     通常
1  みかん  100   0   在庫切れ
2  ぶどう  300  10    高価格

複数の条件を組み合わせるような処理は、lambdaよりもdefで関数を定義した方が読みやすくなります。

手順3:applyとlambdaの使い分け

# シンプルな1行の処理 → lambdaが便利
df["税込価格"] = df["価格"].apply(lambda p: int(p * 1.1))

# 複数の条件分岐がある処理 → defで関数を定義した方が読みやすい
def classify(row):
    if row["在庫"] == 0:
        return "在庫切れ"
    elif row["価格"] >= 200:
        return "高価格"
    return "通常"

df2["ステータス"] = df2.apply(classify, axis=1)

つまずきやすい設定・注意点

  • axis=0(デフォルト)は列単位、axis=1は行単位で処理するという意味です。指定を間違えると意図しない結果になります
  • applyは便利ですが、内部的にはPythonのループに近い動きをするため、大量データでは処理が遅くなりがちです

よくあるつまずきポイント・エラー対処

applyが遅くて処理が終わらない

❌Before:数十万行のデータに対して、単純な四則演算までapplyで書いてしまう

df["税込価格"] = df["価格"].apply(lambda p: p * 1.1)

私が10万行を超えるデータでこの書き方をしたとき、処理に想定以上の時間がかかり、「applyは何にでも使える便利な機能」という認識を改めることになりました。
applyは柔軟な反面、内部で1行ずつPythonの関数を呼び出すため、単純な計算にはオーバーヘッドが大きいのです。

✅After:単純な四則演算はapplyを使わず、pandasのベクトル演算(列全体に対して直接計算する書き方)を使う

df["税込価格"] = df["価格"] * 1.1

実行時間を計測したところ、10万行のデータでapplyが数秒かかっていた処理が、ベクトル演算では一瞬で終わりました。
「単純な計算はベクトル演算、複雑な条件分岐はapply」という使い分けを意識すると、処理速度を大きく改善できます。

応用・一歩先の使い方

mapとの違いを理解する

似た機能にmapがあります。mapはSeriesの各値を、辞書や関数で置き換える処理に特化しています。

category_map = {"りんご": "果物", "みかん": "果物", "キャベツ": "野菜"}
df["カテゴリ"] = df["商品名"].map(category_map)

「対応表(辞書)に基づいて単純に置き換えたいだけ」ならmap、「条件分岐や計算を伴う」ならapply、という使い分けが基本です。

applymapの代わりにmapを使う(pandas 2.x系の変更点)

DataFrame全体の各要素に処理をかけたい場合、以前はapplymapが使われていましたが、pandas 2.1以降では非推奨となり、代わりにDataFrame.mapを使うことが推奨されています。

# pandas 2.1以降で推奨される書き方
df_numeric = df[["価格", "在庫"]]
result = df_numeric.map(lambda x: x * 2)

古い記事ではapplymapが紹介されていることが多いので、新しくコードを書く際はmapを使うようにしましょう。

まとめ

この記事のポイント

  • applyは列・行に対して任意の関数を適用できる柔軟な機能
  • lambdaは名前のない一時的な関数を1行で定義する書き方
  • 単純な計算にはベクトル演算、複雑な条件分岐にはapplyを使い分ける
  • 対応表による単純な置き換えにはmapが適している
  • pandas 2.x系ではapplymapが非推奨になり、DataFrame.mapが推奨されている

次に読むべき記事

ここまででpandasの基本〜実践的な処理は一通り学べました。
最後は、Excelとpandas、それぞれの得意分野を整理した「Excelでの集計とpandasでの集計、業務で使い分けるポイント」で、実務での使い分けを確認しておきましょう。

タグ: Pandas, 中級者向け, 実践Tips

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