Pandasでデータ処理を行う際、mapとapplyのどちらを使うべきか迷った経験はありませんか?この記事では、両者の明確な違いと具体的な使い分け方法を、豊富なコード例とともに解説します。
mapとapplyの違い(一覧表で比較)
まず最初に、mapとapplyの基本的な違いを表形式で確認しましょう。
| 項目 | map | apply |
|---|---|---|
| 対象 | Seriesのみ | Series・DataFrame両方 |
| 戻り値 | Series(元と同じ型) | Series・DataFrame・スカラー値 |
| 引数 | 辞書・Series・関数 | 関数のみ |
| 典型的な用途 | 値の置換・マッピング | 複雑な計算・集約処理 |
| 速度 | 辞書使用時は高速 | 関数適用時は柔軟だが重い |
| NA値の処理 | NAはNA(変換されない) | 関数内で制御可能 |
💡 使い分けのポイント
- 辞書での値の置換 →
mapを使用 - 複数列にまたがる計算 →
applyを使用 - DataFrame全体の処理 →
applyを使用
DataFrame.map(旧applymap)についても知っておこう
pandasにはSeries.map()とDataFrame.apply()のほかに、DataFrameの全要素に関数を適用するメソッドも存在します。バージョンによって名称が変わっているため、コードを書く際は注意が必要です。
- pandas 2.0以前:
DataFrame.applymap()という名称でした - pandas 2.1以降:
DataFrame.applymap()は非推奨(deprecated)となり、DataFrame.map()に統一されました
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
# pandas 2.1以降の書き方(推奨)
result = df.map(lambda x: x * 2)
print(result)
# 出力:
# A B
# 0 2 8
# 1 4 10
# 2 6 12
# pandas 2.0以前の書き方(非推奨・警告が出る)
# result = df.applymap(lambda x: x * 2)
| メソッド | 対象 | 推奨バージョン |
|---|---|---|
Series.map() | Seriesの各要素 | 全バージョン |
DataFrame.map() | DataFrameの全要素 | pandas 2.1以降(推奨) |
DataFrame.applymap() | DataFrameの全要素 | pandas 2.0以前(非推奨) |
DataFrame.apply() | 行または列単位 | 全バージョン |
よくあるエラー対処:
df.applymap()を使っているコードでFutureWarningが出る場合は、df.map()に書き換えることで解決します。既存コードの移行時はこの点を最初に確認してください。
pandas mapの基本的な使い方
mapは主にSeriesの各要素に対する値の変換・置換に特化した関数です。特に辞書を使った値のマッピングに威力を発揮します。
辞書を使った値の変換
import pandas as pd # DataFrameの作成 df = pd.DataFrame({ 'product': ['A001', 'B002', 'C003', 'A001', 'B002'], 'category': ['electronics', 'clothing', 'books', 'electronics', 'clothing'] }) # 商品コードを商品名に変換 product_map = { 'A001': 'スマートフォン', 'B002': 'Tシャツ', 'C003': 'プログラミング入門書' } df['product_name'] = df['product'].map(product_map) print(df)
product category product_name 0 A001 electronics スマートフォン 1 B002 clothing Tシャツ 2 C003 books プログラミング入門書 3 A001 electronics スマートフォン 4 B002 clothing Tシャツ
関数を使った変換
# 数値の変換 prices = pd.Series([100, 250, 380, 150]) # 税込価格(1.1倍)に変換 tax_included = prices.map(lambda x: int(x * 1.1)) print(tax_included)
0 110 1 275 2 418 3 165 dtype: int64
pandas applyの基本的な使い方
applyは関数をSeries・DataFrameに適用する汎用的な関数で、複雑な処理や複数列を扱う計算に適しています。
Seriesに対するapply
# 文字列処理の例 names = pd.Series(['田中太郎', '佐藤花子', '鈴木次郎']) # 姓のみを抽出 surnames = names.apply(lambda x: x[:2]) print(surnames)
0 田中 1 佐藤 2 鈴木 dtype: object
DataFrameの行に対するapply
# 複数列を使った計算 df_sales = pd.DataFrame({ 'price': [1000, 1500, 2000], 'quantity': [5, 3, 8], 'discount_rate': [0.1, 0.05, 0.15] }) # 割引適用後の売上を計算 def calculate_sales(row): base_amount = row['price'] * row['quantity'] discount = base_amount * row['discount_rate'] return base_amount - discount df_sales['final_sales'] = df_sales.apply(calculate_sales, axis=1) print(df_sales)
price quantity discount_rate final_sales 0 1000 5 0.10 4500.0 1 1500 3 0.05 4275.0 2 2000 8 0.15 13600.0
複数列・DataFrameでの使い分け
複数列を扱う場合の具体的な使い分け方法を説明します。
複数列の条件分岐処理
# 顧客データの作成 df_customers = pd.DataFrame({ 'age': [25, 45, 35, 60, 28], 'income': [400, 800, 600, 1200, 350], 'has_children': [False, True, True, False, True] }) # 複数条件での顧客セグメント分類 def classify_customer(row): if row['age'] < 30: if row['income'] > 500: return '若年高所得' else: return '若年一般' elif row['age'] < 50: if row['has_children']: return '中年ファミリー' else: return '中年単身' else: return 'シニア' df_customers['segment'] = df_customers.apply(classify_customer, axis=1) print(df_customers)
age income has_children segment 0 25 400 False 若年一般 1 45 800 True 中年ファミリー 2 35 600 True 中年ファミリー 3 60 1200 False シニア 4 28 350 True 若年一般
DataFrameの列に対するapply
# 各列の統計情報を計算 df_numeric = pd.DataFrame({ 'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50], 'C': [100, 200, 300, 400, 500] }) # 各列の変動係数(標準偏差/平均)を計算 cv = df_numeric.apply(lambda x: x.std() / x.mean(), axis=0) print("変動係数:") print(cv)
変動係数: A 0.527046 B 0.527046 C 0.527046 dtype: float64
applyが遅くなりやすいケースと代替手段
applyは柔軟性が高い反面、行・列単位でPythonのループを内部で実行するため、データ量が多いと処理速度が問題になります。以下のケースでは代替手段の検討が有効です。
ベクトル演算で代替できるケース
import pandas as pd
import numpy as np
df = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [10, 20, 30, 40, 50]})
# ❌ applyで書いた場合(行ごとにループ・遅い)
result_slow = df.apply(lambda row: row['A'] + row['B'], axis=1)
# ✅ ベクトル演算で書いた場合(pandas内部で一括処理・速い)
result_fast = df['A'] + df['B']
NumPy関数で代替できるケース
# ❌ applyで書いた場合(遅い)
result_slow = df['A'].apply(np.sqrt)
# ✅ NumPyを直接適用(速い)
result_fast = np.sqrt(df['A'])
判断の目安
| 処理の種類 | 推奨手段 |
|---|---|
| 四則演算・比較演算 | ベクトル演算(+, -, > 等) |
| NumPyの数学関数 | np.sqrt() 等を直接適用 |
| 辞書での値の置換 | Series.map(dict) |
| 複数列を参照する複雑な処理 | apply(axis=1)(代替が難しい場合) |
基本方針:
applyは「ベクトル演算やNumPy関数で書けない複雑な処理」に限定することが、パフォーマンス改善の第一歩です。
速度とパフォーマンスの違い
実際の処理速度を比較してみましょう。
辞書マッピングの速度比較
import time import numpy as np # 大きなデータセットで比較 large_series = pd.Series(np.random.choice(['A', 'B', 'C'], 100000)) mapping_dict = {'A': 1, 'B': 2, 'C': 3} # mapの実行時間 start_time = time.time() result_map = large_series.map(mapping_dict) map_time = time.time() - start_time # applyの実行時間 start_time = time.time() result_apply = large_series.apply(lambda x: mapping_dict[x]) apply_time = time.time() - start_time print(f"map実行時間: {map_time:.4f}秒") print(f"apply実行時間: {apply_time:.4f}秒") print(f"mapの方が{apply_time/map_time:.1f}倍高速")
⚡ パフォーマンスのポイント
- 辞書マッピング:
mapが圧倒的に高速 - 複雑な関数:
applyの方が柔軟で読みやすい - 大量データ: ベクトル化できる処理は
mapを優先
mapとapplyでlambdaを使う場合の書き方
map・applyに関数を渡す際、lambda(無名関数)を使うと1行で簡潔に記述できます。実際のコードで頻繁に登場するパターンを確認しておきましょう。
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'score': [85, 60, 92]
})
# mapとlambda:Seriesの各要素を変換
df['name_upper'] = df['name'].map(lambda x: x.upper())
# applyとlambda:複数列を参照した条件分岐
df['grade'] = df.apply(
lambda row: 'A' if row['score'] >= 80 else 'B',
axis=1
)
print(df)
# name score name_upper grade
# 0 Alice 85 ALICE A
# 1 Bob 60 BOB B
# 2 Charlie 92 CHARLIE A
lambdaの基本的な書き方・引数の扱い・条件分岐パターンについては以下の記事で詳しく解説しています。
→ Pythonのlambda(ラムダ)とは?基本の書き方・引数・複数行の扱いをわかりやすく解説
よくある疑問(mapとapplyのどちらを使うべき?)
Q1: 辞書での値の変換時、mapとapplyのどちらがいい?
A: mapを使用してください。辞書マッピングはmapの得意分野で、applyより高速です。
Q2: 複数列を参照する計算はどちらを使う?
A: apply一択です。mapはSeries専用のため、複数列を同時に参照できません。
Q3: NA値がある場合の挙動の違いは?
# NA値の処理比較 series_with_na = pd.Series(['A', 'B', np.nan, 'C']) mapping = {'A': 1, 'B': 2, 'C': 3} print("mapの結果:") print(series_with_na.map(mapping)) print("\napplyの結果:") print(series_with_na.apply(lambda x: mapping.get(x, 'NA') if pd.notna(x) else 'NA'))
Q4: 処理速度を向上させたい場合は?
以下の順序で検討してください:
- ベクトル化演算(最高速)
- map(辞書使用)
- apply
- swifter(並列処理ライブラリ)
# swifterを使った高速化例 # pip install swifter が必要 import swifter # 通常のapply # result = df.apply(complex_function, axis=1) # swifterを使った高速化 # result = df.swifter.apply(complex_function, axis=1)
まとめ(おすすめの使い分け)
🎯 結論:こう使い分けよう
mapを使うべき場面
- ✅ 辞書・Seriesを使った値の置換
- ✅ カテゴリ変数の変換
- ✅ 単純な1対1のマッピング
- ✅ 処理速度を重視する場合
applyを使うべき場面
- ✅ 複数列を参照する計算
- ✅ 複雑な条件分岐処理
- ✅ DataFrame全体の操作
- ✅ カスタム関数の適用
実践的な判断フロー
- 辞書で値を変換したい →
map - 複数列を使う必要がある →
apply - DataFrame全体を処理したい →
apply - どちらでも可能だが速度重視 →
map - どちらでも可能だが可読性重視 →
apply
この記事で紹介したテクニックを使いこなすことで、pandasでのデータ処理がより効率的になります。実際のプロジェクトでは、データの特性と処理の要件に応じて適切に使い分けることが重要です。
pandasのmap・applyを使いこなせるようになると、「次はどんなスキルを身につけるべきか」「独学で続けるべきか、スクールを検討すべきか」という疑問が出てくることがあります。AIが普及した今、その判断基準も変わってきています。