メインコンテンツへスキップ

3.3.6 データ変換

この節の位置づけ

ここまで学んだ新人の方は、このあたりで少し混乱しやすいです。

  • apply
  • map
  • replace
  • rank
  • cut

名前は知っていても、問題になると「どれを先に使えばいいのか」が分からなくなりがちです。

なので、この節で一番大事なのは、関数をもう一度覚えることではなく、まず次の判断基準を持つことです。

「値を変えたい」のか、「新しい列を作りたい」のか、「並び替えや順位付けをしたい」のか、それとも「連続値を区間に分けたい」のか。

学習目標

  • applymapapplymap の使い方と違いを理解する
  • 並び替え(sort_values)とランキング(rank)を学ぶ
  • データの置換とマッピングを身につける

まずは全体像をつかもう

データ変換は、「この列を最終的にどうしたいか」で考えると理解しやすいです。

Pandas データ変換パイプライン

この節で本当に解決したいのは、次のことです。

  • それぞれの変換操作が何を補ってくれるのか
  • いつ map を先に思いつくべきか、いつ apply を先に思いつくべきか

apply:行や列に関数を適用する

Pandas 変換メソッド選択図解

apply は Pandas の中でもとても柔軟な変換ツールです。任意の関数を、各行または各列に適用できます。

新人向けの分かりやすいイメージ

データ変換は、次のように考えると理解しやすいです。

  • 元データに「翻訳」「加工」「再ラベル付け」をする

たとえば、やりたいことは次のように分かれます。

  • コードを中文に変換したい
  • 1行の中の複数列から新しい結果を計算したい
  • 連続した数値を高・中・低の3段階に分けたい

見た目はどれも「変換」ですが、実際には別の種類の問題です。

Series に対して使う

import pandas as pd
import numpy as np

df = pd.DataFrame({
"氏名": ["张三", "李四", "王五", "赵六"],
"数学": [85, 92, 78, 95],
"英语": [90, 88, 72, 85]
})

# 単一列に組み込み関数を適用
print(df["数学"].apply(np.sqrt)) # 各点数の平方根を求める

# 単一列にカスタム関数を適用
def grade(score):
if score >= 90: return "優秀"
elif score >= 80: return "良好"
elif score >= 70: return "普通"
else: return "合格"

df["数学等级"] = df["数学"].apply(grade)
print(df)

# lambda を使うとより簡潔
df["英语等级"] = df["英语"].apply(lambda x: "合格" if x >= 60 else "不合格")

DataFrame に対して行単位で適用する

# axis=1 は各行に対して処理するという意味
df["总分"] = df[["数学", "英语"]].apply(np.sum, axis=1)

# カスタムの行処理
def student_info(row):
return f"{row['氏名']}の数学は{row['数学']}点です"

df["描述"] = df.apply(student_info, axis=1)
print(df[["氏名", "描述"]])

apply を初めて学ぶとき、一番大事なのは?

まず覚えるべきなのは、次の1点です。

apply は「既存の方法だけでは足りないときの、自作の計算」に向いている。

つまり、最初に思いつく道具というより、

  • ルールが少し複雑で、組み込み関数だけでは対応しにくいときに使う

というイメージです。


map:値の対応付け

map は Series に対して使い、古い値を新しい値へ対応付けします。

df = pd.DataFrame({
"氏名": ["张三", "李四", "王五"],
"性别": ["M", "F", "M"],
"部署代码": [1, 2, 1]
})

# 辞書で対応付け
df["性别中文"] = df["性别"].map({"M": "男", "F": "女"})

# 部門コードを対応付け
dept_map = {1: "技術部", 2: "市場部", 3: "管理部"}
df["部署名称"] = df["部署代码"].map(dept_map)

# 関数を使って対応付け
df["氏名长度"] = df["氏名"].map(len)

print(df)

いつ map を先に思いつくとよい?

頭の中で次のような対応関係を考えているときです。

  • A コード -> A 名称
  • M / F -> 男 / 女
  • 月の省略形 -> 中国語の月名

このような「1つの値を別の1つの値へ変換する」関係なら、
まず map を思い出すとよいです。

map と apply の違い

特性mapapply
対象Series のみSeries または DataFrame
辞書マッピング対応
関数対応
行単位の処理✅(axis=1)

replace:値を置き換える

df = pd.DataFrame({
"城市": ["BJ", "SH", "GZ", "SZ", "BJ"],
"等级": ["A", "B", "C", "A", "B"]
})

# 単一値の置換
df["城市"] = df["城市"].replace("BJ", "北京")

# 複数値の置換(辞書)
city_map = {"SH": "上海", "GZ": "广州", "SZ": "深圳"}
df["城市"] = df["城市"].replace(city_map)

print(df)

mapreplace はどこで混同しやすい?

まずは次のように覚えると分かりやすいです。

  • map は「対応付けして変換する」イメージ
  • replace は「特定の古い値をそのまま置き換える」イメージ

目的が、

  • 一連のコードを名前に変換する

なら map に近いです。
一方で、ただ

  • ある汚い値だけを別の値に直したい

なら replace に近いです。


並び替え

sort_values:値で並び替える

df = pd.DataFrame({
"氏名": ["张三", "李四", "王五", "赵六", "钱七"],
"年龄": [22, 28, 25, 35, 21],
"給与": [15000, 22000, 18000, 35000, 12000]
})

# 薪資の昇順
print(df.sort_values("給与"))

# 薪資の降順
print(df.sort_values("給与", ascending=False))

# 複数列で並び替え:まず年齢の昇順、年齢が同じなら薪資の降順
print(df.sort_values(["年龄", "給与"], ascending=[True, False]))

# 上位 3 件を取る(nlargest を使うのがおすすめ)
print(df.nlargest(3, "給与"))

# 下位 3 件を取る
print(df.nsmallest(3, "給与"))

sort_index:インデックスで並び替える

df_indexed = df.set_index("氏名")
print(df_indexed.sort_index()) # 名前順に並べる
print(df_indexed.sort_index(ascending=False))

rank:順位をつける

df = pd.DataFrame({
"氏名": ["张三", "李四", "王五", "赵六", "钱七"],
"成绩": [85, 92, 78, 92, 88]
})

# デフォルトの順位付け(同じ値は平均順位)
df["排名"] = df["成绩"].rank(ascending=False)
print(df)
# 氏名 成绩 排名
# 0 张三 85 4.0
# 1 李四 92 1.5 ← 同率1位なので (1+2)/2
# 2 王五 78 5.0
# 3 赵六 92 1.5
# 4 钱七 88 3.0

# いろいろな順位付けの方法
df["最小排名"] = df["成绩"].rank(ascending=False, method="min") # 同率は最小順位
df["最大排名"] = df["成绩"].rank(ascending=False, method="max") # 同率は最大順位
df["密集排名"] = df["成绩"].rank(ascending=False, method="dense") # 飛び番なし
print(df[["氏名", "成绩", "排名", "最小排名", "密集排名"]])
method同順位の扱い例(92,92)
average平均を取る1.5, 1.5
min最小順位を取る1, 1
max最大順位を取る2, 2
dense密な順位(飛び番なし)1, 1(次は 2)
first出現順で決める1, 2

初学者がまず覚えるとよい選択表

今やりたいことまず思い浮かべたい方法
コードを中文ラベルに変えたいmap
1行の複数列から新しい結果を計算したいapply(axis=1)
Top N を取りたい / 並び替えたいsort_values / nlargest
順位をつけたいrank
連続値を区間に分けたいcut / qcut

この表は、新人の方にとってとても役立ちます。
「変換方法はたくさんある」という状態を、よくある数個の問題に整理し直してくれるからです。


そのほかのよく使う変換

値の出現回数を数える

df = pd.DataFrame({
"部署": ["技術", "市場", "技術", "管理", "技術", "市場"]
})

# 各値の出現回数
print(df["部署"].value_counts())
# 技術 3
# 市場 2
# 管理 1

# 割合
print(df["部署"].value_counts(normalize=True))

ユニーク値

print(df["部署"].unique())     # ['技術' '市場' '管理']
print(df["部署"].nunique()) # 3(ユニーク値の個数)

ビン分割(cut / qcut)

ages = pd.Series([18, 22, 25, 30, 35, 42, 55, 68])

# 固定区間で分ける
bins = [0, 18, 30, 50, 100]
labels = ["少年", "青年", "中年", "老年"]
age_group = pd.cut(ages, bins=bins, labels=labels)
print(age_group)

# 分位数で分ける(各グループの人数をできるだけ近くする)
quartile_group = pd.qcut(ages, q=4, labels=["Q1", "Q2", "Q3", "Q4"])
print(quartile_group)

まとめ

操作方法よくある用途
自作の変換apply()複雑な行単位・列単位の計算
値の対応付けmap()辞書による対応付け、コード変換
値の置換replace()間違った値の修正
並び替えsort_values()Top N、ランキング
順位付けrank()成績順位
値のカウントvalue_counts()カテゴリ集計
区間分けcut() / qcut()年齢層、収入層

この節で一番持ち帰ってほしいこと

  • データ変換で大事なのは関数名を覚えることより、まず「データをどう変えたいか」をはっきりさせること
  • map は対応付け、apply は自作の加工、というイメージを持つと分かりやすい
  • 並び替え、順位付け、ビン分割は、どれもデータの見せ方を整理し直す操作

やってみよう

練習 1:データのマッピング

# 英語の月名の省略形を含むデータを作る
# 1. 月名の省略形を中国語に変換する
# 2. 月を四半期(Q1, Q2, Q3, Q4)に変換する

練習 2:順位付けの応用

# 20人の学生の3科目の成績 DataFrame を作る
# 1. 合計点を計算する
# 2. 合計点で順位をつける(密集順位)
# 3. 合計点で並び替えて、上位5名を取る
# 4. 各科目の点数にランクを付ける(優秀/良好/普通/合格/不合格)

練習 3:ビン分割の練習

# 100人のユーザーの消費金額データがある
# 1. cut を使って消費金額を "低消費/中消費/高消費" の3段階に分ける
# 2. qcut を使って5グループに均等分割する
# 3. 各グループの人数と平均消費額を集計する