Pandas apply / map / applymap
apply、map、applymap は Pandas でデータ変換に使用される3大関数であり、DataFrame や Series に対して柔軟な要素ごとの操作や一括操作を行うことができます。
Series.map
mapSeries のメソッドで、Series の各要素を変換するために使用されます。
基本的な使い方
実例
import pandas as pd
# Series を作成
s = pd.Series([1, 2, 3, 4, 5])
print("元のデータ:")
print(s)
print()
# 関数を使用
print("各要素 * 2:")
print(s.map(lambda x: x * 2))
print()
# 辞書マッピングを使用
mapping = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E"}
print("辞書マッピングを使用:")
print(s.map(mapping))
print()
# Series マッピングを使用
mapping_series = pd.Series(["A", "B", "C", "D", "E"], index=[1, 2, 3, 4, 5])
print("Series マッピングを使用:")
print(s.map(mapping_series))
# Series を作成
s = pd.Series([1, 2, 3, 4, 5])
print("元のデータ:")
print(s)
print()
# 関数を使用
print("各要素 * 2:")
print(s.map(lambda x: x * 2))
print()
# 辞書マッピングを使用
mapping = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E"}
print("辞書マッピングを使用:")
print(s.map(mapping))
print()
# Series マッピングを使用
mapping_series = pd.Series(["A", "B", "C", "D", "E"], index=[1, 2, 3, 4, 5])
print("Series マッピングを使用:")
print(s.map(mapping_series))
欠損値の処理
実例
import pandas as pd
import numpy as np
s = pd.Series([1, 2, np.nan, 4, 5])
print("NaN を含む:")
print(s)
print()
# map はデフォルトで NaN をスキップ
print("map 処理(NaN をスキップ):")
print(s.map(lambda x: x * 2 if pd.notna(x) else -1))
import numpy as np
s = pd.Series([1, 2, np.nan, 4, 5])
print("NaN を含む:")
print(s)
print()
# map はデフォルトで NaN をスキップ
print("map 処理(NaN をスキップ):")
print(s.map(lambda x: x * 2 if pd.notna(x) else -1))
DataFrame.applymap
applymapは DataFrame のメソッドで、各要素ごとに関数を適用します(注意:Pandas 2.0+ では、DataFrame.mapmap で代替することを推奨)。
実例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [4, 5, 6],
"C": [7, 8, 9]
})
print("元のデータ:")
print(df)
print()
# 各要素に2を掛ける
print("各要素 * 2:")
print(df.applymap(lambda x: x * 2))
print()
# 小数第2位まで保持
print("小数第2位まで保持:")
print(df.applymap(lambda x: round(x, 2)))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [4, 5, 6],
"C": [7, 8, 9]
})
print("元のデータ:")
print(df)
print()
# 各要素に2を掛ける
print("各要素 * 2:")
print(df.applymap(lambda x: x * 2))
print()
# 小数第2位まで保持
print("小数第2位まで保持:")
print(df.applymap(lambda x: round(x, 2)))
applymap は要素ごとの操作のため、大規模データでは遅くなる可能性があります。数値列のみを操作する場合は、ベクトル化操作または apply と axis パラメータの併用を検討してください。
DataFrame.apply
applyは最も柔軟なメソッドで、軸に沿って関数を適用できます。
列ごとに適用
実例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3, 4, 5],
"B": [10, 20, 30, 40, 50],
"C": [100, 200, 300, 400, 500]
})
print("元のデータ:")
print(df)
print()
# デフォルトは axis=0、列ごとに適用
print("各列の合計:")
print(df.apply(sum))
print()
print("各列の最大値:")
print(df.apply(max))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3, 4, 5],
"B": [10, 20, 30, 40, 50],
"C": [100, 200, 300, 400, 500]
})
print("元のデータ:")
print(df)
print()
# デフォルトは axis=0、列ごとに適用
print("各列の合計:")
print(df.apply(sum))
print()
print("各列の最大値:")
print(df.apply(max))
行ごとに適用
実例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [10, 20, 30],
"C": [100, 200, 300]
})
print("元のデータ:")
print(df)
print()
# axis=1、行ごとに適用
print("各行の合計:")
print(df.apply(sum, axis=1))
print()
# 各行の最大値 - 最小値
print("各行のレンジ:")
print(df.apply(lambda x: x.max() - x.min(), axis=1))
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [10, 20, 30],
"C": [100, 200, 300]
})
print("元のデータ:")
print(df)
print()
# axis=1、行ごとに適用
print("各行の合計:")
print(df.apply(sum, axis=1))
print()
# 各行の最大値 - 最小値
print("各行のレンジ:")
print(df.apply(lambda x: x.max() - x.min(), axis=1))
aggfunc を使用した集約
実例
import pandas as pd
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [10, 20, 30]
})
# 複数の関数を同時に適用
print("合計と平均を同時に計算:")
print(df.apply([sum, np.mean]))
print()
# 複数の値を返す
result = df.apply(lambda x: pd.Series({
"sum": x.sum(),
"mean": x.mean(),
"max": x.max()
}, index=["sum", "mean", "max"]))
print("複数の値を返す:")
print(result)
import numpy as np
df = pd.DataFrame({
"A": [1, 2, 3],
"B": [10, 20, 30]
})
# 複数の関数を同時に適用
print("合計と平均を同時に計算:")
print(df.apply([sum, np.mean]))
print()
# 複数の値を返す
result = df.apply(lambda x: pd.Series({
"sum": x.sum(),
"mean": x.mean(),
"max": x.max()
}, index=["sum", "mean", "max"]))
print("複数の値を返す:")
print(result)
Series.apply
Series でも apply を使用できます。機能は map に似ていますが、より柔軟です。
実例
import pandas as pd
import numpy as np
s = pd.Series([1, 4, 9, 16, 25])
print("元のデータ:")
print(s)
print()
# 平方根を計算
print("平方根:")
print(s.apply(np.sqrt))
print()
# 条件による返却
print("条件判断:")
print(s.apply(lambda x: "大" if x > 10 else "小"))
import numpy as np
s = pd.Series([1, 4, 9, 16, 25])
print("元のデータ:")
print(s)
print()
# 平方根を計算
print("平方根:")
print(s.apply(np.sqrt))
print()
# 条件による返却
print("条件判断:")
print(s.apply(lambda x: "大" if x > 10 else "小"))
性能比較
実例
import pandas as pd
import numpy as np
import time
# 大規模データを作成
n = 100000
s = pd.Series(np.random.randn(n))
# map と apply をテスト
func = lambda x: x * 2 + 1
start = time.time()
result1 = s.map(func)
map_time = time.time() - start
start = time.time()
result2 = s.apply(func)
apply_time = time.time() - start
# ベクトル化(最速)
start = time.time()
result3 = s * 2 + 1
vec_time = time.time() - start
print(f"map 所要時間: {map_time:.4f}s")
print(f"apply 所要時間: {apply_time:.4f}s")
print(f"ベクトル化所要時間: {vec_time:.4f}s")
print("\n結論:ベクトル化操作を優先的に使用し、性能が最良です")
import numpy as np
import time
# 大規模データを作成
n = 100000
s = pd.Series(np.random.randn(n))
# map と apply をテスト
func = lambda x: x * 2 + 1
start = time.time()
result1 = s.map(func)
map_time = time.time() - start
start = time.time()
result2 = s.apply(func)
apply_time = time.time() - start
# ベクトル化(最速)
start = time.time()
result3 = s * 2 + 1
vec_time = time.time() - start
print(f"map 所要時間: {map_time:.4f}s")
print(f"apply 所要時間: {apply_time:.4f}s")
print(f"ベクトル化所要時間: {vec_time:.4f}s")
print("\n結論:ベクトル化操作を優先的に使用し、性能が最良です")
実践:データ変換
実例
import pandas as pd
import numpy as np
# サンプル DataFrame を作成
df = pd.DataFrame({
"氏名": ["張三", "李四", "王五", "趙六"],
"年齢": [25, 30, 28, 35],
"給与": [12000, 15000, 11000, 18000],
"部門": ["技術", "営業", "技術", "運営"]
})
print("元のデータ:")
print(df)
print()
# apply を使用して行レベルの計算を行う
def calculate(row):
"""年収と税引き後給与を計算します"""
annual = row["給与"] * 12
tax = annual * 0.1 if annual > 120000 else annual * 0.05
after_tax = annual - tax
return pd.Series({
"年収": annual,
"税金": tax,
"税引き後": after_tax
})
result = df.apply(calculate, axis=1)
df_result = pd.concat([df, result], axis=1)
print("計算結果:")
print(df_result)
import numpy as np
# サンプル DataFrame を作成
df = pd.DataFrame({
"氏名": ["張三", "李四", "王五", "趙六"],
"年齢": [25, 30, 28, 35],
"給与": [12000, 15000, 11000, 18000],
"部門": ["技術", "営業", "技術", "運営"]
})
print("元のデータ:")
print(df)
print()
# apply を使用して行レベルの計算を行う
def calculate(row):
"""年収と税引き後給与を計算します"""
annual = row["給与"] * 12
tax = annual * 0.1 if annual > 120000 else annual * 0.05
after_tax = annual - tax
return pd.Series({
"年収": annual,
"税金": tax,
"税引き後": after_tax
})
result = df.apply(calculate, axis=1)
df_result = pd.concat([df, result], axis=1)
print("計算結果:")
print(df_result)
3つの関数の使い分け
| 方法 | 適用対象 | 使用シーン | 性能 |
|---|---|---|---|
map |
Series | 要素レベルの変換、辞書マッピング | 快 |
applymap |
DataFrame | 要素レベルの変換(非数値列) | 慢 |
apply |
Series/DataFrame | 行・列の集約、カスタム関数 | 中 |
その他の拡張ベクトル化操作(演算子を直接使用)が可能な場合は apply/map を使用せず、map を使用できる場合は apply を使用しないでください。