404 Not Found

404 Not Found


nginx

【ピボット・melt】データの変形とピボット操作

同じデータでも、人間が読みやすい「ワイドテーブル」として保存する場合と、プログラムによる分析に適した「ロングテーブル」として保存する場合があります。Pandasは、これらの形式を相互に変換するための4つの必須ツール、pivot / melt / stack / unstack を提供しています。本レッスンでは、Mermaid図を使って各変形操作の方向性を視覚的に示し、「melt(溶かす)」や「pivot(軸で回転させる)」が実際に何を意味するのかを直感的に理解できるようにします。これは、競合する多くのチュートリアルにはない可視化です。

⚠️ 注意: 以下のコードを実行するには、ローカルのPython環境が必要です。

1. 学べること



2. アリスの売上レポート変形

(1) 困りどころ:レポート形式 ≠ 分析形式

アリスの月次売上レポートはワイド形式(月が列方向に並ぶ)ですが、分析にはロング形式(月を1つの列として持つ)が必要です:

PYTHON
import pandas as pd

# ワイド形式 — 読みやすいが、分析しにくい
wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano'],
    'Jan': [320, 280, 200],
    'Feb': [350, 300, 220],
    'Mar': [380, 310, 240]
})
print(wide)
#     product  Jan  Feb  Mar
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) 解決策:meltでロング形式へ

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:meltでワイドからロングへ(難易度 ⭐)

PYTHON
import pandas as pd

wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano'],
    'Jan': [320, 280, 200],
    'Feb': [350, 300, 220],
    'Mar': [380, 310, 240]
})

# Melt:月の列が行になる
long = wide.melt(id_vars='product', var_name='month', value_name='sales')
print(long)
#     product month  sales
# 0     Latte   Jan    320
# 1     Mocha   Jan    280
# 2 Americano   Jan    200
# 3     Latte   Feb    350
# ...
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。


3. ワイドテーブルとロングテーブル

(1) Mermaid図:ワイド ⇄ ロング

100%
graph LR
    subgraph Wide["ワイドテーブル — 人間向き"]
        W["product | Jan | Feb | Mar<br>Latte   | 320 | 350 | 380<br>Mocha  | 280 | 300 | 310"]
    end
    subgraph Long["ロングテーブル — 機械分析向き"]
        L["product | month | sales<br>Latte   | Jan   | 320<br>Latte   | Feb   | 350<br>Latte   | Mar   | 380<br>Mocha  | Jan   | 280<br>..."]
    end
    Wide -->|"melt()"| Long
    Long -->|"pivot()"| Wide
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) ワイドとロングの比較

特徴 ワイドテーブル ロングテーブル
値の格納場所 列名の中 行の中
向いている用途 人間が読む/レポート 機械による分析/ggplot
新しい月の追加 列を追加(構造が変わる) 行を追加(構造は不変)
groupby 不便 自然にサポート
典型的な用途 Excelレポート データベース/Tidy Data


4. pivot — 基本的なピボット

(1) 基本的なピボット

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:pivotでロングからワイドへ(難易度 ⭐)

PYTHON
import pandas as pd

long = pd.DataFrame({
    'product': ['Latte', 'Latte', 'Latte', 'Mocha', 'Mocha', 'Mocha'],
    'month': ['Jan', 'Feb', 'Mar', 'Jan', 'Feb', 'Mar'],
    'sales': [320, 350, 380, 280, 300, 310]
})

# ピボット:一意な行=product、一意な列=month、値=sales
wide = long.pivot(index='product', columns='month', values='sales')
print(wide)
# month     Feb   Jan   Mar
# product
# Americano  220   200   240
# Latte      350   320   380
# Mocha      300   280   310

# インデックスをリセットしてproductを通常の列にする
wide = wide.reset_index()
print(wide.columns)  # MultiIndex → フラット化
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。
⚠️ 注意: pivotは、indexとcolumnsの組み合わせが一意であることを要求します。重複がある場合(例:同じ月の同じ商品に複数のレコードがある場合)、エラーが発生します。その場合は、pivot_table(自動的に集計してくれる)を使用してください。



5. pivot_table — 集計を伴うピボット

(1) 集計ピボット

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:pivot_tableによる集計(難易度 ⭐⭐)

PYTHON
import pandas as pd

# product+monthごとに複数のエントリ(集計が必要)
df = pd.DataFrame({
    'product': ['Latte', 'Latte', 'Latte', 'Latte', 'Mocha', 'Mocha'],
    'month': ['Jan', 'Jan', 'Feb', 'Feb', 'Jan', 'Feb'],
    'sales': [150, 170, 180, 170, 140, 160],
    'quantity': [30, 34, 36, 34, 28, 32]
})

# 集計関数を指定したpivot_table
result = pd.pivot_table(
    df,
    values='sales',
    index='product',
    columns='month',
    aggfunc='sum'   # デフォルトは'mean'
)
print(result)
# month     Feb   Jan
# product
# Latte     350   320
# Mocha     160   140

# 複数の集計関数
result2 = pd.pivot_table(
    df,
    values=['sales', 'quantity'],
    index='product',
    columns='month',
    aggfunc={'sales': 'sum', 'quantity': 'mean'}
)

# Margins — 行/列の合計を追加
result3 = pd.pivot_table(
    df, values='sales', index='product', columns='month',
    aggfunc='sum', margins=True, margins_name='Total'
)
print(result3)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) pivotとpivot_tableの比較

特徴 pivot pivot_table
重複する値 エラー発生 集計する
集計関数 なし sum/mean/countなど
margins 非対応 ✅ 行/列の合計
複数の値列 単一列 複数列
向いている用途 重複のないクリーンなデータ 重複があり集計が必要なデータ


6. melt — 詳細解説

(1) meltのパラメータ

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:meltのパラメータ解説(難易度 ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'product': ['Latte', 'Mocha'],
    'region': ['North', 'South'],
    'Jan_sales': [320, 280],
    'Feb_sales': [350, 300],
    'Jan_quantity': [64, 56],
    'Feb_quantity': [70, 60]
})

# 基本的なmelt — id以外のすべての列が行になる
result1 = df.melt(id_vars=['product', 'region'])
print(result1)

# value_varsの指定 — 特定の列だけをmeltする
result2 = df.melt(
    id_vars=['product', 'region'],
    value_vars=['Jan_sales', 'Feb_sales'],
    var_name='month',
    value_name='sales'
)
print(result2)

# 複数のid_vars
result3 = df.melt(
    id_vars=['product'],
    value_vars=['Jan_sales', 'Feb_sales'],
    var_name='month_sales',
    value_name='amount'
)
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。


7. stack / unstack

(1) stack — 列を行へ

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:MultiIndexでのstack/unstack(難易度 ⭐⭐)

PYTHON
import pandas as pd

df = pd.DataFrame({
    'Jan': [320, 280],
    'Feb': [350, 300],
    'Mar': [380, 310]
}, index=['Latte', 'Mocha'])
df.index.name = 'product'

# stack:列 → 行レベル(ワイド → ロング)
stacked = df.stack()
print(stacked)
# product  month
# Latte    Jan      320
#          Feb      350
#          Mar      380
# Mocha    Jan      280
# ...

# unstack:行レベル → 列(ロング → ワイド)
unstacked = stacked.unstack()
print(unstacked)
#          Jan   Feb   Mar
# product
# Latte    320   350   380
# Mocha    280   300   310

# MultiIndexの場合:どのレベルをunstackするかを選択
multi = pd.DataFrame({
    'sales': [320, 350, 280, 300],
    'region': ['North', 'North', 'South', 'South'],
    'month': ['Jan', 'Feb', 'Jan', 'Feb']
})
pivot = multi.set_index(['region', 'month'])['sales']
print(pivot.unstack(level='month'))
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

(2) 4つの変形メソッドの比較

メソッド 方向 入力 → 出力 向いている用途
pivot ロング → ワイド index/columns/valuesを指定 重複のないロングテーブル
pivot_table ロング → ワイド + aggfuncで集計 重複のあるロングテーブル
melt ワイド → ロング id_varsを指定 列名を行の値にする
stack 列 → 行 columns → インデックスレベル MultiIndex操作
unstack 行 → 列 インデックスレベル → columns stackの逆操作


8. 総合例:ワイド⇄ロング変換の完全パイプライン

▶ サンプル

TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

:ワイド⇄ロング変換の完全パイプライン(難易度 ⭐⭐⭐)

PYTHON
import pandas as pd
import numpy as np

# ============================================
# 総合例:ワイド ⇄ ロング
# 売上データの変形パイプライン
# ============================================

# 1. ワイド形式から始める(レポート形式)
np.random.seed(42)
wide = pd.DataFrame({
    'product': ['Latte', 'Mocha', 'Americano', 'Espresso', 'Cappuccino'],
    'region': ['North', 'South', 'East', 'West', 'North'],
    'Jan': np.random.randint(200, 500, 5),
    'Feb': np.random.randint(220, 520, 5),
    'Mar': np.random.randint(240, 540, 5),
    'Apr': np.random.randint(250, 550, 5)
})

print("=== ワイド形式(5商品 × 4か月) ===")
print(wide)

# 2. meltでロング形式へ
long = wide.melt(
    id_vars=['product', 'region'],
    var_name='month',
    value_name='sales'
)
print(f"\n=== ロング形式:{len(long)}行 ===")
print(long.head(8))

# 3. ロング形式で分析(groupbyが自然)
monthly_total = long.groupby('month')['sales'].sum()
print(f"\n=== 月別合計 ===\n{monthly_total}")

region_avg = long.groupby('region')['sales'].mean().round(0)
print(f"\n=== 地域別平均 ===\n{region_avg}")

# 4. ピボットでワイドに戻す(product × monthのサマリ)
summary_wide = pd.pivot_table(
    long, values='sales', index='product', columns='month',
    aggfunc='sum', margins=True, margins_name='Total'
)
print(f"\n=== ピボットテーブルのサマリ ===")
print(summary_wide)

# 5. MultiIndexでのstack/unstack
multi = long.set_index(['region', 'product', 'month'])['sales']
by_region = multi.unstack(level='month')
print(f"\n=== 月別unstack(地域×商品) ===")
print(by_region.head(8))
TEXT
> 出力: ローカルのPython環境(pandas 2.x)で実行してください。Pistonサーバにはpandasがプリインストールされていません。ローカルにインストール(`pip install pandas`)して、一緒に進めてください。実際の値はpandasのバージョンによって若干異なる場合があります。

❓ よくある質問

Q pivotとpivot_tableの違いは何ですか?
A pivotはindexとcolumnsの組み合わせが一意であることを要求し、重複があるとエラーになります。pivot_tableは重複を集計して処理し(デフォルトはmeanですが、sum/countなども指定可能)、margins(行/列の合計)もサポートします。重複のないシンプルなデータにはpivotを、重複がある場合や集計が必要な場合はpivot_tableを使用してください。実務では、日常のシナリオの80%においてpivot_tableの方が安全な選択肢です。
Q meltのid_varsとは何ですか?
A id_varsはmeltされない列、つまり行の識別子として残る列です。それ以外のすべての列は、その列名が変数列(var_name)に、その値が値列(value_name)に集められます。例えば、月の列Jan/Feb/Marをmeltすると、文字列'Jan'/'Feb'/'Mar'はmonth列の値になり、対応する数値はsales列の値になります。
Q stackとunstackは互いに逆操作ですか?
A はい — stackは列を行に押し込み(ワイド → ロング)、unstackは行を列に展開します(ロング → ワイド)。df.stack().unstack()は元のテーブルを復元します。これらはMultiIndexのレベルに対して動作し、stackは行レベルを追加し、unstackは列レベルを追加します。levelパラメータで、どのレベルを対象にするかを制御します。
Q ワイドテーブルとロングテーブルはいつ使い分けるべきですか?
A ワイドテーブルは人間が読むのに最適です(レポート、プレゼン、Excel)。ロングテーブルはプログラムによる分析に最適です(groupby、merge、可視化)。Tidy Dataの原則はロング形式を推奨しています:1列に1変数、1行に1観測。データはロング形式(安定した構造)で保存し、表示するときにワイド形式(pivotの出力)にしましょう。
Q pivotで重複値のエラーが出たらどうすればいいですか?
A これは、indexとcolumnsの組み合わせに重複する行があることを意味します(例:同じ月の同じ商品に複数のレコードがある場合)。選択肢は2つあります:① aggfunc='sum'/'mean'を指定したpivot_tableで自動的に集計する;② 先にgroupbyで集計してからpivotする。pivot_tableの方が簡潔で、一般的に好まれます。
Q melt操作を元に戻すにはどうすればいいですか?
A pivotまたはpivot_tableで元の形を復元します:long.pivot(index='product', columns='month', values='sales')。meltとpivotは逆操作です。なお、melt後にデータ型が変わる場合がある(数値列がobjectになる場合がある)ため、復元前にastypeでの変換が必要になることがあります。
Q Tidy Dataとは何ですか?
A Tidy Dataは、Hadley Wickhamが提唱したデータ整理の原則です:① 各変数は1つの列を占める;② 各観測は1つの行を占める;③ 各観測単位のタイプは1つのテーブルを占める。ロングテーブルは自然にTidy Dataを満たしますが、ワイドテーブルは満たしません(month変数が列名に隠れているため)。Pandasのmeltは、まさにワイドテーブルをTidy Data形式に「整える(tidy)」ために存在しています。

📖 まとめ


📝 練習問題

  1. 基礎(難易度 ⭐):ワイドテーブル(3商品 × 3か月)を作成し、meltでロング形式に変換してから、pivotでワイド形式に戻し、結果が一致することを確認してください。
  2. 中級(難易度 ⭐⭐):重複する値を含む売上データ(同じ月の同じ商品に複数のレコード)を作成し、pivot_table(aggfunc='sum') + margins=Trueを使って、行と列の合計を持つピボットテーブルを生成してください。
  3. チャレンジ(難易度 ⭐⭐⭐):3地域 × 4商品 × 3か月のロング形式データをシミュレートし、次を完成させてください:set_indexでMultiIndexを作成 → unstackで月別に展開 → stackで元に戻す → meltで別の変形 → pivot_tableでクロス集計。

← 前へ:結合と追加 · 次へ:文字列操作 →

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%