Pandas Melt:ワイドデータをロング形式に整形する(完全ガイド)
Updated on
各月が別々の列になっているスプレッドシートを想像してください。1月、2月、3月……12月まで。売上の数値が12列に並んでいて、これを時系列としてプロットしたり、groupby をかけたり、機械学習モデルに渡したりしたい。しかし、こうした処理はワイド形式のデータとは相性がよくありません。必要なのは「月名の列が1つ」と「売上値の列が1つ」です。この変換——列を行に変える——を行うのが、まさに pandas melt です。
問題はすぐに深刻化します。列が数十〜数百に及ぶワイドなデータセットは、アンケートデータ、センサー読み取り、財務レポート、Excel や SQL からピボットされたエクスポートなどでよく見られます。手作業で再構成するのは面倒でミスも起きやすい。unpivot したい列が増えるたびに、適切なツールを使わない限りボイラープレートコードが増えていきます。
pd.melt() はこれを1回の呼び出しで解決します。ワイドな DataFrame を受け取り、選択した列を行に変換してロング形式へ「melt」しつつ、識別子となる列はそのまま保持します。このガイドでは、完全な構文、すべてのパラメータ、実務シナリオに基づく例、そして pivot、stack、wide_to_long のような関連する整形関数との比較まで解説します。
pd.melt() が行うこと
pd.melt() は DataFrame をワイド形式からロング形式へ unpivot(縦持ち化)します。ワイド形式では変数ごとに列が分かれています。ロング形式(「tidy」形式とも呼ばれる)では、「変数名」の列が1つと「値」の列が1つになります。
概念的な変換は次のとおりです。
ワイド形式(melt 前):
| student | math | science | english |
|---|---|---|---|
| Alice | 90 | 85 | 88 |
| Bob | 78 | 92 | 80 |
ロング形式(melt 後):
| student | subject | score |
|---|---|---|
| Alice | math | 90 |
| Alice | science | 85 |
| Alice | english | 88 |
| Bob | math | 78 |
| Bob | science | 92 |
| Bob | english | 80 |
student 列は識別子として保持され、3つの科目列は2列(科目名を入れる列とスコアを入れる列)に melt されます。
pd.melt() の構文とパラメータ
pd.melt(frame, id_vars=None, value_vars=None, var_name=None,
value_name='value', col_level=None, ignore_index=True)DataFrame のメソッドとして呼ぶこともできます:
df.melt(id_vars=None, value_vars=None, var_name=None,
value_name='value', col_level=None, ignore_index=True)パラメータ一覧
| Parameter | Description | Default |
|---|---|---|
frame | melt する DataFrame(df.melt() を使う場合は不要) | Required |
id_vars | 識別子変数として保持する列(melt しない) | None |
value_vars | 行に展開(unpivot)する列。省略すると id_vars 以外のすべての列が対象 | None |
var_name | 元の列名を入れる新しい列の名前 | 'variable' |
value_name | 値を入れる新しい列の名前 | 'value' |
col_level | 列が MultiIndex の場合、melt するレベル | None |
ignore_index | True なら新しい整数 index を採用。False なら元の index を保持 | True |
基本例:学生の成績
先ほどの成績例から始めます。
import pandas as pd
grades = pd.DataFrame({
'student': ['Alice', 'Bob', 'Charlie'],
'math': [90, 78, 85],
'science': [85, 92, 88],
'english': [88, 80, 91]
})
long = pd.melt(grades, id_vars=['student'], value_vars=['math', 'science', 'english'],
var_name='subject', value_name='score')
print(long)出力:
student subject score
0 Alice math 90
1 Bob math 78
2 Charlie math 85
3 Alice science 85
4 Bob science 92
5 Charlie science 88
6 Alice english 88
7 Bob english 80
8 Charlie english 91これで各行が「学生×科目」の組み合わせを表すようになります。元の 3x4 DataFrame(3行、4列)が、9x3 DataFrame(9行、3列)になります。
value_vars を省略する
value_vars を省略すると、pandas は id_vars に含まれない列をすべて melt します。
long = grades.melt(id_vars=['student'], var_name='subject', value_name='score')
print(long)これは先ほどの例と同じ結果になります。value_vars の省略は、識別子以外をすべて melt したいときに便利です。
id_vars なしで melt する
識別子列を指定せずに melt することもできます。この場合、すべての列が variable/value のペアになります。
temperatures = pd.DataFrame({
'Jan': [30, 28],
'Feb': [32, 31],
'Mar': [45, 42]
})
long = temperatures.melt(var_name='month', value_name='temp_f')
print(long)出力:
month temp_f
0 Jan 30
1 Jan 28
2 Feb 32
3 Feb 31
4 Mar 45
5 Mar 42すべての列が測定値で、保持すべき識別子がない場合に有用です。
複数の識別子列
実データでは識別子が1つとは限りません。id_vars に列名のリストを渡せます。
sales = pd.DataFrame({
'region': ['North', 'South', 'North', 'South'],
'product': ['Widget', 'Widget', 'Gadget', 'Gadget'],
'q1_revenue': [1200, 1500, 800, 950],
'q2_revenue': [1400, 1600, 900, 1100],
'q3_revenue': [1100, 1450, 850, 1000],
'q4_revenue': [1500, 1700, 1000, 1200]
})
long_sales = sales.melt(
id_vars=['region', 'product'],
var_name='quarter',
value_name='revenue'
)
print(long_sales)出力:
region product quarter revenue
0 North Widget q1_revenue 1200
1 South Widget q1_revenue 1500
2 North Gadget q1_revenue 800
3 South Gadget q1_revenue 950
4 North Widget q2_revenue 1400
5 South Widget q2_revenue 1600
6 North Gadget q2_revenue 900
7 South Gadget q2_revenue 1100
8 North Widget q3_revenue 1100
9 South Widget q3_revenue 1450
10 North Gadget q3_revenue 850
11 South Gadget q3_revenue 1000
12 North Widget q4_revenue 1500
13 South Widget q4_revenue 1700
14 North Gadget q4_revenue 1000
15 South Gadget q4_revenue 1200region と product の両方が各行で保持され、四半期の4列が2列へ圧縮されます。
melt 後の後処理(クリーンアップ)
melt の後、variable 列には整形したい文字列が入っていることがよくあります。上の例では q1_revenue のような値が入っていますが、Q1 だけにしたいかもしれません。文字列操作で整形できます。
long_sales['quarter'] = long_sales['quarter'].str.replace('_revenue', '').str.upper()
print(long_sales.head())出力:
region product quarter revenue
0 North Widget Q1 1200
1 South Widget Q1 1500
2 North Gadget Q1 800
3 South Gadget Q1 950
4 North Widget Q2 1400特定の列だけ melt する
列の一部だけ melt したいこともあります。その場合は value_vars で明示します。
survey = pd.DataFrame({
'respondent': ['R1', 'R2', 'R3'],
'age': [25, 34, 42],
'q1_satisfaction': [4, 5, 3],
'q2_satisfaction': [3, 4, 5],
'q3_satisfaction': [5, 3, 4],
'income': [50000, 75000, 60000]
})
# 満足度の列だけ melt し、age と income は識別子として保持
long_survey = survey.melt(
id_vars=['respondent', 'age', 'income'],
value_vars=['q1_satisfaction', 'q2_satisfaction', 'q3_satisfaction'],
var_name='question',
value_name='rating'
)
print(long_survey)出力:
respondent age income question rating
0 R1 25 50000 q1_satisfaction 4
1 R2 34 75000 q1_satisfaction 5
2 R3 42 60000 q1_satisfaction 3
3 R1 25 50000 q2_satisfaction 3
4 R2 34 75000 q2_satisfaction 4
5 R3 42 60000 q2_satisfaction 5
6 R1 25 50000 q3_satisfaction 5
7 R2 34 75000 q3_satisfaction 3
8 R3 42 60000 q3_satisfaction 4income 列は melt 対象ではありませんが、識別子として保持されます。
実例:時系列データ
金融・経済データは、日付(年など)が列ヘッダになったワイド形式で届くことがよくあります。melt でプロットしやすい時系列へ変換できます。
import pandas as pd
gdp = pd.DataFrame({
'country': ['USA', 'UK', 'Germany'],
'2020': [20.94, 2.71, 3.89],
'2021': [23.00, 3.12, 4.26],
'2022': [25.46, 3.07, 4.07],
'2023': [27.36, 3.33, 4.46]
})
gdp_long = gdp.melt(id_vars=['country'], var_name='year', value_name='gdp_trillion_usd')
gdp_long['year'] = gdp_long['year'].astype(int)
gdp_long = gdp_long.sort_values(['country', 'year']).reset_index(drop=True)
print(gdp_long)出力:
country year gdp_trillion_usd
0 Germany 2020 3.89
1 Germany 2021 4.26
2 Germany 2022 4.07
3 Germany 2023 4.46
4 UK 2020 2.71
5 UK 2021 3.12
6 UK 2022 3.07
7 UK 2023 3.33
8 USA 2020 20.94
9 USA 2021 23.00
10 USA 2022 25.46
11 USA 2023 27.36これで GDP の推移を簡単にプロットしたり、国別に groupby したり、前年比成長率を計算したりできます。
MultiIndex 列で melt する
DataFrame の列ヘッダが複数レベル(MultiIndex)の場合、col_level でどのレベルを melt するか指定します。
arrays = [['score', 'score', 'attendance', 'attendance'],
['midterm', 'final', 'midterm', 'final']]
columns = pd.MultiIndex.from_arrays(arrays, names=['metric', 'exam'])
data = pd.DataFrame([[85, 90, 95, 100], [78, 82, 90, 88]],
index=['Alice', 'Bob'], columns=columns)
# 上位レベルを melt
melted = data.melt(col_level=0, var_name='metric', value_name='value', ignore_index=False)
print(melted)より複雑な多段階のケースでは、melt の前に droplevel() で列レベルを落としたり、レベル同士をアンダースコアで結合して列をフラット化してから melt したりする必要がある場合があります。
Melt vs Pivot:逆操作
melt() と pivot() は逆操作です。melt は wide → long、pivot は long → wide に変換します。
import pandas as pd
# ワイドから開始
wide = pd.DataFrame({
'name': ['Alice', 'Bob'],
'math': [90, 78],
'science': [85, 92]
})
# Melt: wide -> long
long = wide.melt(id_vars='name', var_name='subject', value_name='score')
print("Long format:")
print(long)
# Pivot: long -> wide(往復)
back_to_wide = long.pivot(index='name', columns='subject', values='score').reset_index()
back_to_wide.columns.name = None
print("\nBack to wide format:")
print(back_to_wide)出力:
Long format:
name subject score
0 Alice math 90
1 Bob math 78
2 Alice science 85
3 Bob science 92
Back to wide format:
name math science
0 Alice 90 85
1 Bob 78 92重要な違い: pivot() は index と columns の組み合わせが一意であることを要求します。ロング形式データに重複がある場合は、代わりに pivot_table() と集計関数を使います。
Melt vs Stack vs wide_to_long
pandas には整形(reshape)のための関数がいくつかあります。使い分けの目安は次のとおりです。
| Function | Direction | Best For | Key Difference |
|---|---|---|---|
melt() | wide to long | 特定の列を行に unpivot する | 列ベースで直感的。初心者に分かりやすい |
stack() | wide to long | 列レベルを index レベルへ畳み込む | index ベース。MultiIndex と相性が良い |
wide_to_long() | wide to long | 共通の prefix と数値 suffix を持つ列(例: score1, score2) | stub 名を自動解析する |
pivot() | long to wide | 値を列へ展開(キーが一意) | melt() の逆 |
unstack() | long to wide | index レベルを列へ展開 | stack() の逆 |
stack() を使うべき場合
stack() は列 index を操作し、それを行 index 側へ押し込みます。MultiIndex をすでに扱っていて、index レベルで整形したいときに有用です。
wide = pd.DataFrame({
'math': [90, 78],
'science': [85, 92]
}, index=['Alice', 'Bob'])
stacked = wide.stack()
print(stacked)出力:
Alice math 90
science 85
Bob math 78
science 92
dtype: int64結果は Series(MultiIndex 付き)になり、melt() のように列名付きのきれいな DataFrame にはなりません。明示的な列名を持つフラットな DataFrame が欲しい場合は melt() を使います。
wide_to_long() を使うべき場合
wide_to_long() は score1, score2, score3 のような命名パターンに沿った列向けです。
df = pd.DataFrame({
'student': ['Alice', 'Bob'],
'score1': [90, 78],
'score2': [85, 92],
'score3': [88, 80]
})
long = pd.wide_to_long(df, stubnames='score', i='student', j='exam_num')
print(long.reset_index())出力:
student exam_num score
0 Alice 1 90
1 Alice 2 85
2 Alice 3 88
3 Bob 1 78
4 Bob 2 92
5 Bob 3 80列名が一貫した prefix-suffix パターンに従っているなら wide_to_long() が便利です。そうでなければ melt() の方が柔軟です。
パフォーマンス上の考慮点
多くのデータセット(数百万行未満)では、melt() は十分高速です。100,000 行・50 列の DataFrame を melt するベンチマーク例は次のとおりです。
| Operation | Approximate Time |
|---|---|
melt() with 50 value columns | ~15 ms |
stack() equivalent | ~10 ms |
Manual loop with concat() | ~500 ms |
パフォーマンス改善のヒント:
value_varsを明示する — 必要な列だけ melt する方が、全列を melt するより速いです。ignore_index=Trueを使う(デフォルト)— 元の index を保持するとオーバーヘッドが増えます。- melt してすぐに再 pivot するのを避ける — 別のワイド形式が必要なら、往復するより
pivot_table()やrename()を直接検討してください。 - 非常に大規模な DataFrame(melt 後に 1億行+ など)では、lazy evaluation と並列処理を提供する polars や Dask の利用も検討してください。
# 実際に必要な列だけ melt する
long = df.melt(
id_vars=['id'],
value_vars=['col_a', 'col_b', 'col_c'], # 50列すべてではない
var_name='metric',
value_name='reading'
)よくあるエラーと対処法
1. KeyError: 列が見つからない
id_vars や value_vars に指定した列名が DataFrame に存在しないと発生します。
# 誤り: 列名にタイプミス
long = df.melt(id_vars=['stduent']) # KeyError
# 対処: 先に列名を確認する
print(df.columns.tolist())2. 想定外の重複行が出る
melt 自体は重複を「作る」のではなく、id 変数の組み合わせごとに行を生成します。重複に見える場合、元データ側に識別子の重複行があることが原因です。
# id 列に重複があるか確認
print(df.duplicated(subset=['student']).sum())3. value 列の dtype が混在する
異なる dtype の列を melt(例: int64 と float64)すると、pandas は value 列をより一般的な型へ昇格(upcast)します。数値列と文字列列を混ぜて melt すると value 列は object dtype になります。
df = pd.DataFrame({
'id': [1, 2],
'score': [90, 85],
'grade': ['A', 'B']
})
long = df.melt(id_vars='id')
print(long.dtypes)
# variable object
# value object <-- score と grade が両方 object になるこれを避けるには、数値列と文字列列を分けて melt してください。
PyGWalker で melt 後のデータを可視化する
データをワイド形式からロング形式へ整形したら、次は視覚的に探索するのが自然な流れです。分布の確認、グループ比較、外れ値の発見などに役立ちます。PyGWalker (opens in a new tab) は、任意の pandas DataFrame を Jupyter Notebook 内で Tableau のように操作できる、オープンソースの Python ライブラリです。
import pandas as pd
import pygwalker as pyg
# ワイドなデータをロング形式に変換
grades = pd.DataFrame({
'student': ['Alice', 'Bob', 'Charlie', 'Diana'],
'math': [90, 78, 85, 92],
'science': [85, 92, 88, 79],
'english': [88, 80, 91, 84]
})
long = grades.melt(id_vars='student', var_name='subject', value_name='score')
# インタラクティブ可視化を起動
walker = pyg.walk(long)PyGWalker では、subject を x-axis に、score を y-axis にドラッグし、student で色分けすれば、科目ごとのパフォーマンス比較を即座に行えます。チャートコードは不要です。棒グラフ、散布図、箱ひげ図などをドラッグ&ドロップで作れます。
Google Colab (opens in a new tab) や Kaggle (opens in a new tab) で PyGWalker を試すか、
pip install pygwalkerでインストールできます。
FAQ
pandas melt は何をしますか?
pandas の melt() は DataFrame をワイド形式からロング形式へ整形します。列を行に変換し、2つの新しい列(元の列名を入れる列=variable、値を入れる列=value)を作ります。これは「unpivot」とも呼ばれます。
pandas の melt と pivot の違いは何ですか?
melt() はワイド形式をロング形式に変換します(列が行になる)。pivot() はその逆で、ロング形式をワイド形式に変換します(行が列になる)。両者は逆操作です。同じパラメータで DataFrame を melt してから pivot すると、元の DataFrame に戻せます。
pandas では melt と stack をいつ使い分けるべきですか?
明示的な列名を持つきれいな DataFrame を得たい、かつ unpivot する列を制御したい場合は melt() を使います。MultiIndex 列を扱っていて、列レベルを行 index に押し込みたい場合は stack() を使います。melt() は初心者に直感的で、stack() は階層構造の整形により強力です。
pandas で複数列を melt するには?
value_vars に列名のリストを渡します: df.melt(id_vars=['id'], value_vars=['col_a', 'col_b', 'col_c'])。リストに含めた列がすべて行に展開されます。value_vars を省略すると、id_vars に含まれないすべての列が melt されます。
重複した列名を持つ DataFrame を melt できますか?
pandas は melt できますが、結果の variable 列に重複値が入るため分かりにくくなる可能性があります。曖昧さを避けるため、df.columns = [...] や df.rename() で先に列名を変更してください。
pandas で melt を元に戻すには?
pivot() または pivot_table() を使って、melt 済み(ロング形式)のデータをワイド形式へ戻せます: long.pivot(index='id', columns='variable', values='value')。index と columns の組み合わせが重複して集約が必要なら pivot_table() を使います。
まとめ
pandas の melt() は、Python でワイド形式の DataFrame をロング(tidy)形式へ変換する標準的な方法です。要点は次のとおりです。
id_varsを使う — 識別子として保持する列を指定します。value_varsを使う — melt する列を制御します。省略すると識別子以外がすべて対象になります。var_nameとvalue_nameを使う — 出力列に意味のある名前を付けます。- melt と pivot は逆操作 — wide-to-long は
melt()、long-to-wide はpivot()。 stack()よりmelt()を選ぶ — MultiIndex Series ではなく、フラットな DataFrame と明示的な列名が欲しい場合。- melt 後の後処理を行う — variable 列に対して文字列メソッドで prefix/suffix の除去や値の整形をします。
整形後のデータは、PyGWalker (opens in a new tab) のようなツールでチャートコードなしに視覚探索でき、分析ワークフローをより速く直感的にできます。