pandasのpivot()の使い方|縦長データを横長の表にする引数と重複エラーの直し方、Rのpivot_widerとの対応
pandasのpivot()は、縦に並んだデータ(ロング形式)を、行・列・値の3つを指定して横長の表(ワイド形式)に組み替えるメソッドです。書き方は df.pivot(index=”行にする列”, columns=”列にする列”, values=”値の列”) で、集計はしないので、行と列の組み合わせが重複しているとValueErrorになります。
この記事では、pivot()の引数、valuesを省略したときの多段の列、結果を普通の表に戻す方法、重複エラーの直し方、Rのpivot_wider()との対応までを実行例つきでまとめます。コードはpandas 3.0.6(Python 3.12.15)で実行しました(2026年10月15日確認)。集計もしたい場合は「pandasのpivot_tableの使い方」、stack・unstack・meltとの違いは「pandasのstack・unstack・pivotの違いと使い分け」で詳しく扱っています。
pivot()の書き方と3つの引数
公式リファレンスでのシグネチャは DataFrame.pivot(*, columns, index=<no_default>, values=<no_default>) です(pandas.DataFrame.pivot — pandas documentation、2026年10月15日確認)。先頭の * は、すべての引数をキーワードで渡す必要があるという意味です。
| 引数 | 役割 | 省略したとき |
|---|---|---|
index |
新しい表の行(見出し)にする列 | 今のインデックスをそのまま使う |
columns |
新しい表の列名にする列(必須) | 省略できない |
values |
セルに入れる値の列 | 残りの列すべてを使い、列が2段(MultiIndex)になる |
どの引数にも列名のリストを渡せます。リストにすると、行や列の見出しが2段以上になります。
基本の使い方:日付×店舗の売上表を作る
1行に「日付・店舗・売上・客数」が1件ずつ並んだ表を、日付を行、店舗を列にした売上表へ組み替えます。
import pandas as pd
df = pd.DataFrame({
"日付": ["10/1", "10/1", "10/1", "10/2", "10/2", "10/2"],
"店舗": ["渋谷", "新宿", "池袋", "渋谷", "新宿", "池袋"],
"売上": [120, 200, 90, 150, 180, 60],
"客数": [12, 20, 9, 15, 18, 6],
})
print(df)
wide = df.pivot(index="日付", columns="店舗", values="売上")
print(wide)
print(wide.columns.name, wide.index.name)
日付 店舗 売上 客数
0 10/1 渋谷 120 12
1 10/1 新宿 200 20
2 10/1 池袋 90 9
3 10/2 渋谷 150 15
4 10/2 新宿 180 18
5 10/2 池袋 60 6
店舗 新宿 池袋 渋谷
日付
10/1 200 90 120
10/2 180 60 150
店舗 日付
見るポイントは3つです。
- 「店舗」の値(渋谷・新宿・池袋)がそのまま列名になり、「日付」の値が行の見出しになります。
- 列は並べ替えられます。日本語は文字コード順になるので、元データの順(渋谷・新宿・池袋)にはなりません。
- 表の左上の「店舗」は列の名前(
columns.name)、その下の「日付」は行の名前(index.name)です。データの列ではありません。
列の順番を決めたいときは、組み替えた後にリストで並べ直します。
w = wide[["渋谷", "新宿", "池袋"]]
print(w)
店舗 渋谷 新宿 池袋
日付
10/1 120 200 90
10/2 150 180 60
valuesを省略すると列が2段になる
values を指定しないと、index・columns以外の列(ここでは売上と客数)がすべて値になり、列の見出しが「値の列名」と「店舗」の2段になります。
w = df.pivot(index="日付", columns="店舗")
print(w)
print(w["客数"])
売上 客数
店舗 新宿 池袋 渋谷 新宿 池袋 渋谷
日付
10/1 200 90 120 20 9 12
10/2 180 60 150 18 6 15
店舗 新宿 池袋 渋谷
日付
10/1 20 9 12
10/2 18 6 15
上の段の名前で w["客数"] のように取り出せば、1段の表になります。values=["売上"] のようにリストで1列だけ渡した場合も、列は ("売上", "新宿") のような2段のままになる点に注意してください。
結果を普通の表(1段の列)に戻す
pivot()の結果は、行の見出しにindexが入った形です。CSVに書き出したり、別の表と結合したりするときは、reset_index() で「日付」を普通の列に戻し、列の名前を消しておくと扱いやすくなります。
w = df.pivot(index="日付", columns="店舗", values="売上")
flat = w.reset_index()
flat.columns.name = None
print(flat)
# 列が2段のときは「売上_新宿」のように1段へつなげる
w3 = df.pivot(index="日付", columns="店舗", values=["売上"])
w3.columns = [f"{a}_{b}" for a, b in w3.columns]
print(w3.reset_index())
日付 新宿 池袋 渋谷
0 10/1 200 90 120
1 10/2 180 60 150
日付 売上_新宿 売上_池袋 売上_渋谷
0 10/1 200 90 120
1 10/2 180 60 150
行や列を複数の列で組み立てる
indexやcolumnsに列名のリストを渡すと、見出しが2段になります。支店と月の組み合わせを行にして、商品を列に並べる例です。
df = pd.DataFrame({
"支店": ["東京", "東京", "東京", "東京", "大阪", "大阪"],
"月": ["4月", "4月", "5月", "5月", "4月", "5月"],
"商品": ["A", "B", "A", "B", "A", "A"],
"売上": [100, 80, 120, 90, 70, 75],
})
print(df.pivot(index=["支店", "月"], columns="商品", values="売上"))
print(df.pivot(index="支店", columns=["月", "商品"], values="売上"))
商品 A B
支店 月
大阪 4月 70.0 NaN
5月 75.0 NaN
東京 4月 100.0 80.0
5月 120.0 90.0
月 4月 5月
商品 A B A B
支店
大阪 70.0 NaN 75.0 NaN
東京 100.0 80.0 120.0 90.0
大阪には商品Bのデータが無いので、そのセルは NaN になりました。2段の見出しの扱い方は「pandasのMultiIndexの使い方」で詳しく説明しています。
データが無いセルはNaNになり、整数が小数になる
元データに無い組み合わせは NaN で埋まります。NaNは小数(float)の値なので、整数だった列も 200.0 のような表示に変わります。pivot()には fill_value 引数が無いため、組み替えた後に fillna() で埋めます。
df = pd.DataFrame({
"日付": ["10/1", "10/1", "10/2"],
"店舗": ["渋谷", "新宿", "渋谷"],
"売上": [120, 200, 150],
})
w = df.pivot(index="日付", columns="店舗", values="売上")
print(w)
print(w.dtypes.to_dict())
print(w.fillna(0).astype(int))
店舗 新宿 渋谷
日付
10/1 200.0 120.0
10/2 NaN 150.0
{'新宿': dtype('float64'), '渋谷': dtype('float64')}
店舗 新宿 渋谷
日付
10/1 200 120
10/2 0 150
「データが無い」と「売上が0」は意味が違います。0で埋めてよいかは、データの意味で判断してください。
エラー「Index contains duplicate entries, cannot reshape」の直し方
pivot()は集計をしないので、同じ行×列の組み合わせに値が2つ以上あると、どちらを入れるか決められずにValueErrorになります。公式リファレンスにも、組み合わせに複数の値があるとValueErrorになり、集計が必要ならpivot_tableを使うよう書かれています。
df = pd.DataFrame({
"日付": ["10/1", "10/1", "10/2"],
"店舗": ["渋谷", "渋谷", "渋谷"],
"売上": [120, 30, 150],
})
try:
df.pivot(index="日付", columns="店舗", values="売上")
except ValueError as e:
print("ValueError:", e)
# 重複している行を確かめる
print(df[df.duplicated(["日付", "店舗"], keep=False)])
ValueError: Index contains duplicate entries, cannot reshape
日付 店舗 売上
0 10/1 渋谷 120
1 10/1 渋谷 30
まず duplicated(..., keep=False) で重複している行を見て、原因を確かめます。同じデータの二重登録なら drop_duplicates() で消し、1日に複数回の売上が正しく記録されているなら、合計などで集計します。
print(df.pivot_table(index="日付", columns="店舗", values="売上", aggfunc="sum"))
print(df.groupby(["日付", "店舗"])["売上"].sum().unstack())
店舗 渋谷
日付
10/1 150
10/2 150
店舗 渋谷
日付
10/1 150
10/2 150
どちらも同じ結果です。pivot_table は1行で書け、groupby+unstack は途中の集計結果を確かめながら進められます。
エラー「takes 1 positional argument but 4 were given」
古い記事にある df.pivot("日付", "店舗", "売上") のような書き方は、pandas 2.0以降はTypeErrorになります(2.0で引数がキーワード専用になりました。What’s new in 2.0.0の「Disallow passing non-keyword arguments to DataFrame.pivot()」)。
df = pd.DataFrame({"日付": ["10/1", "10/1"], "店舗": ["渋谷", "新宿"], "売上": [120, 200]})
try:
df.pivot("日付", "店舗", "売上")
except TypeError as e:
print("TypeError:", e)
TypeError: DataFrame.pivot() takes 1 positional argument but 4 were given
index=・columns=・values= を付けて書けば動きます。
ワイド形式からロング形式へ戻す(melt)
pivot()の逆の操作は melt() です。reset_index() で日付を列に戻してから、店舗の列を縦に並べ直します。
df = pd.DataFrame({
"日付": ["10/1", "10/1", "10/1", "10/2", "10/2", "10/2"],
"店舗": ["渋谷", "新宿", "池袋", "渋谷", "新宿", "池袋"],
"売上": [120, 200, 90, 150, 180, 60],
})
w = df.pivot(index="日付", columns="店舗", values="売上")[["渋谷", "新宿", "池袋"]]
long = w.reset_index().melt(id_vars="日付", var_name="店舗", value_name="売上")
print(long)
# pd.pivot(df, ...) という関数の形でも同じ
print(pd.pivot(df, index="日付", columns="店舗", values="売上")
.equals(df.pivot(index="日付", columns="店舗", values="売上")))
日付 店舗 売上
0 10/1 渋谷 120
1 10/2 渋谷 150
2 10/1 新宿 200
3 10/2 新宿 180
4 10/1 池袋 90
5 10/2 池袋 60
True
melt()の引数は「pandasのmelt()の使い方」で詳しく扱っています。
Rのpivot_wider()との対応
Rのtidyrで pivot_wider() を使っていた人向けに、引数の対応をまとめます(Pivot data from long to wide — pivot_wider • tidyr、2026年10月15日確認)。
| tidyrのpivot_wider() | pandas | メモ |
|---|---|---|
id_cols |
index |
pandasでは行の見出しになる |
names_from |
columns |
値が新しい列名になる |
values_from |
values |
複数指定で列が2段になる |
values_fill |
pivot()の後に fillna() |
pivot_tableなら fill_value |
values_fn(重複を集計) |
pivot_table(aggfunc=...) |
pivot()は重複でエラー |
names_sep(列名をつなぐ) |
列名を自分で f"{a}_{b}" とつなぐ |
上の「普通の表に戻す」を参照 |
逆向きの pivot_longer() にあたるのが melt() です。
pivot・pivot_table・unstackの使い分け
| やりたいこと | 使うもの |
|---|---|
| 重複の無いデータを横長にするだけ | pivot() |
| 合計・平均などで集計しながら横長にする、総計も付けたい | pivot_table() |
| 行の見出し(MultiIndex)の一部を列に回す | unstack() |
| 横長を縦長に戻す | melt()(または stack()) |
組み替えの全体像は公式ユーザーガイドの「Reshaping and pivot tables」にまとまっています。表の組み替えは、データ分析や機械学習の前処理で毎回のように出てくる操作で、テックジムのAIエンジニア養成コース(16章で機械学習とディープラーニングを学ぶコース)でも第0章「データ分析入門とデータ可視化」から扱います。
よくある質問
Q. pivotとpivot_tableの違いは何ですか?
pivotは並べ替えるだけで集計をしないため、行と列の組み合わせが重複するとエラーになります。pivot_tableは重複を合計や平均で集計でき、総計(margins)や空白を埋めるfill_valueも指定できます。
Q. 「Index contains duplicate entries, cannot reshape」と出たらどうすればいいですか?
indexとcolumnsの同じ組み合わせが2行以上あるという意味です。duplicatedで重複行を確認し、二重登録ならdrop_duplicatesで消し、正しいデータならpivot_tableかgroupbyで集計してください。
Q. df.pivot(“A”, “B”, “C”)と書くとエラーになるのはなぜですか?
pandas 2.0以降、pivotの引数はキーワード専用になったためです。df.pivot(index=”A”, columns=”B”, values=”C”)のように引数名を付けて書けば動きます。
Q. pivotの結果を普通の列に戻すにはどうしますか?
reset_index()で行の見出しを列に戻し、columns.nameをNoneにします。列が2段になっている場合は、列名を「売上_新宿」のように1段へつなげてから戻します。
Q. Rのpivot_widerに当たるのはどれですか?
pandasではpivotが当たります。id_colsがindex、names_fromがcolumns、values_fromがvaluesに対応し、重複を集計するvalues_fnを使う場合はpivot_tableを使います。
pandas|集計・グループ化の関連記事
groupbyや各種集計関数で、データを要約する操作の記事です。
- pivot_table()で複雑なクロス集計も自由自在!カテゴリごとの統計量を算出
- quantileとは?分位点・四分位数の求め方をpandas・NumPyで解説
- Pandas & SciPyでトリム平均を算出!外れ値に強い統計量を使いこなす
- stack(), unstack(), pivot()で縦横無尽にデータを変換
- pandas transformとは?使い方をわかりやすく解説
- Pythonでデータを正規化・標準化!リスト、NumPy、Pandasでの実装を徹底解説
pandas全体の使い方は「pandasの使い方を初心者向けに解説」でまとめています。




