注目キーワード
  1. Python
  2. コンペ

【図解】pandas mapとapplyの違いと使い分け|Series・DataFrame・applymap対応の完全ガイド

  • 2024年2月23日
  • 2026年6月30日
  • Python
  • 2131回
  • 0件

Pandasでデータ処理を行う際、mapapplyのどちらを使うべきか迷った経験はありませんか?この記事では、両者の明確な違いと具体的な使い分け方法を、豊富なコード例とともに解説します。

mapとapplyの違い(一覧表で比較)

まず最初に、mapapplyの基本的な違いを表形式で確認しましょう。

項目 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を使う場合の書き方

mapapplyに関数を渡す際、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: 処理速度を向上させたい場合は?

以下の順序で検討してください:

  1. ベクトル化演算(最高速)
  2. map(辞書使用)
  3. apply
  4. 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全体の操作
  • ✅ カスタム関数の適用

実践的な判断フロー

  1. 辞書で値を変換したいmap
  2. 複数列を使う必要があるapply
  3. DataFrame全体を処理したいapply
  4. どちらでも可能だが速度重視map
  5. どちらでも可能だが可読性重視apply

この記事で紹介したテクニックを使いこなすことで、pandasでのデータ処理がより効率的になります。実際のプロジェクトでは、データの特性と処理の要件に応じて適切に使い分けることが重要です。

pandasのmap・applyを使いこなせるようになると、「次はどんなスキルを身につけるべきか」「独学で続けるべきか、スクールを検討すべきか」という疑問が出てくることがあります。AIが普及した今、その判断基準も変わってきています。

AI時代にプログラミングスクールが必要な人・独学で足りる人の分かれ目【診断付き】

最新情報をチェックしよう!