PythonでExcelの横持ちの表を縦持ちに並べ直す手順|pandasのmeltで月ごとの列を1列にまとめる
月ごとに列が並んだ横持ちのExcelの表を、Pythonのpandasのmeltで店舗・月・売上の縦持ちの表に並べ直す手順を、公式の資料をもとに解説します。id_varsの付け忘れ、空欄の行、合計の列が混ざるなどのつまずきも紹介します。
本記事はプロモーションを含みます。紹介する情報は、実際の成果や効果を保証するものではありません。
目次
この記事でわかること
- 月ごとに列が並んだ横持ちの表を、pandasのmeltで縦持ちの表に並べ直す方法
- 残す列(id_vars)と、新しい列の名前(var_name・value_name)の決め方
- 空欄のセルの行を外して、別のExcelのファイルに保存する手順
- id_varsの付け忘れや、合計の列が混ざるなど、つまずきやすいところ
店舗ごとの売上を、9月・10月・11月のように月ごとの列で横に並べた表は、目で見るには便利です。ところが、月をまたいで集計したり、ほかの表とつなげたりするときは、「店舗・月・売上」の3列に1か月分ずつ入った縦に長い表のほうが扱いやすくなります。ここでは、Excelの横持ちの表をPythonのpandasで縦持ちに組み替え、結果を新しいファイルとして残すまでを順に説明します。この記事のために作った架空の表で、実際に動かして出た結果も載せています。
melt・dropnaなどの働きは、pandasの公式の資料(APIリファレンスとユーザーガイド)を2026年10月25日に読んで確かめました。試すのに使った表は架空のもので、実在の会社や店舗のデータは使っていません。
先に結論: meltで店舗の列を残し、月と売上の2列にまとめる
先に結論を書くと、横持ちの表を縦持ちにするときは、meltで残す列をid_varsに指定し、並べ直してできる2つの列の名前をvar_nameとvalue_nameで付けます。 meltで使う指定を、表にまとめました。
| 指定 | 役割 |
|---|---|
| id_vars | そのまま残す列 |
| value_vars | 並べ直す列 |
| var_name | 元の列名が入る列 |
| value_name | 値が入る列 |
| ignore_index | 行番号を振り直す |
meltで横持ちの表を縦持ちに並べ直す
pandasのmeltは、横に広い表(wide)を縦に長い表(long)に並べ直す関数です。DataFrameのmeltも同じ働きをします。公式の資料によると、meltは識別のための列(id_vars)を残し、ほかの列を行の向きに並べ直して、元の列名と値の2つの列にするとしています。
並べ直す列はvalue_varsで選べます。value_varsを付けないときは、id_vars以外のすべての列が並べ直されます。そのため、残したい列をid_varsに入れ忘れると、その列まで月の列と同じように並べ直されます。
新しい列の名前と行の番号
meltで新しくできる2つの列は、初期の設定では「variable」と「value」という名前です。var_nameとvalue_nameを付けると、「月」と「売上」のように、表を受け取る人が読める名前にできます。ただし、value_nameには、元の表にすでにある列名は使えません。
並べ直した後の行の番号は、初期の設定(ignore_index=True)では0から振り直されます。ignore_index=Falseにすると元の行の番号が残りますが、同じ番号が何度も出てきます。
手順: 月ごとの列を並べ直して別のファイルに保存する
次のコードは、架空の売上の表 sales_wide.xlsx(店舗の列と、2026-09・2026-10・2026-11の月の列)を読み込んで、店舗の列を残したまま月ごとの列を「月」と「売上」の2列に組み替えます。売上が空欄の行を外したうえで、新しいファイル(sales_long.xlsx)として保存します。元のsales_wide.xlsxには何も書き込みません。自分の表で試すときは、ファイル名と列名をその表に合わせ、始める前に元のファイルをコピーしておくと安心です。
import pandas as pd
# 元の表を読む(このファイルには書き込まない)
src_file = "sales_wide.xlsx"
wide = pd.read_excel(src_file)
# 店舗の列は残し、月ごとの列を「月」と「売上」の2列に並べ直す
long = wide.melt(id_vars="店舗", var_name="月", value_name="売上")
# 売上が空欄の行を外し、小数になった売上を整数に戻す
long = long.dropna(subset=["売上"])
long["売上"] = long["売上"].astype(int)
print(long)
# 左端の行番号は要らないので、index=False で書き出す
out_file = "sales_long.xlsx"
long.to_excel(out_file, index=False)
このコードを動かすときは、pandasに加えて、xlsxの読み書きに使うopenpyxlも入れておきます。dropnaのsubsetには、空欄かどうかを見る列を指定します。ここでは売上の列だけを見ています。空欄があった列は、読み込んだ時点で小数の形になるため、空欄の行を外した後にastype(int)で整数に戻しています。縦持ちの表では行番号に意味が無いので、書き出すto_excelにはindex=Falseを付けています。
実行した結果
4店舗×3か月の架空の表(売上のセル12か所のうち、空欄が3か所)で動かすと、結果は次のとおりでした。
- 店舗・月・売上の3列で、空欄の3か所を外した9行の表になりました
- 行は、2026-09の店舗(東京・大阪・名古屋・福岡の順)、2026-10、2026-11の順に並びました
- 表示の左端の行番号は、空欄の行を外したところ(3・6・9)が飛んでいました。書き出したファイルには行番号を入れていません
- 売上を足すと97000になり、横持ちの表の売上をすべて足した値と一致しました
- 書き出したsales_long.xlsxには3列の見出しと9行が入りました。元のsales_wide.xlsxは変わりませんでした
試した環境は、Mac の作業PCの Python 3.12.15(pandas 3.0.6・openpyxl 3.1.5)です。
自分で直すか、相談するかの判断
うまく並べ直せたかは、「並べ直した後の行の数と売上の合計が、元の表と合うか」で見分けます。行の数は、店舗の数×月の列の数から空欄のセルの数を引いた数になります。架空の表では4×3−3で9行でした。合わないときは、月の列のほかに合計やメモの列が無いか、店舗の名前が空欄の行が無いかを確かめます。並べ直す前に空欄の場所を確かめる方法は、PythonでExcelの表の空欄を見つけて一覧にする手順で紹介しています。
空欄のセルを「その月の売上は0」と見るか「まだ入力されていない」と見るかは、表を作った人の決まりで変わります。この記事のコードでは空欄の行を外していますが、0として残したいときは、外す前に表を作った人に確かめます。
つまずきやすいところ
- id_varsを付け忘れる: 架空の表では、店舗の列まで並べ直され、月の列に「店舗」、売上の列に店舗の名前が入って、12行のはずが16行になりました
- 合計の列まで並べ直す: 元の表の右端に合計の列があると、「合計」も1つの月として並べ直されます。架空の表では、売上の合計が97000の2倍の194000になりました。value_varsで月の列だけを選ぶか、先に合計の列を外します
- value_nameに元の列名を使う: value_name="店舗"のように、元の表にある列名を付けると、架空の表ではValueErrorで止まりました
- 月の見出しが日付のセル: Excelの見出しが日付の形で入っていると、架空の表では月の列に「2026-09-01 00:00:00」のような日時が入りました。日付の扱いは、PythonでExcelの日付を正しく読み込む手順が参考になります
- dropnaを忘れる: 試した表では、空欄の3か所が売上がNaNの行として残り、12行になりました
- 並べ直した表を集計に使うとき: 縦持ちの表は、groupbyで店舗ごとや月ごとの合計を出しやすい形です。やり方はPythonでCSVを集計する手順にあります。横持ちに戻すときは、PythonでExcelのピボットテーブルのように集計する手順のpivot_tableを使います。架空の表でも元の値に戻りましたが、店舗の並びは名古屋・大阪・東京・福岡の順に変わりました
まとめ
- 横持ちの表を縦持ちにするときは、meltで残す列をid_varsに入れ、var_nameとvalue_nameで新しい列の名前を付けます
- 月の列のほかに合計などの列があるときは、value_varsで月の列だけを選びます
- 空欄の行はdropnaで外し、行の数と売上の合計が元の表と合うかを確かめます
- 並べ直した表は新しいファイルとして保存し、横持ちの元の表はそのまま残します
よくある質問
横持ちと縦持ちは、何が違いますか?
横持ちは、1つの店舗の売上を、月ごとの列に横へ並べた形です。縦持ちは、店舗・月・売上の3列にして、1か月分の売上を1行に入れた形です。月が増えたとき、横持ちは列が、縦持ちは行が増えます。
meltのあとに、店舗の名前が売上の列に入ってしまうのはなぜですか?
id_varsを付けていない可能性があります。pandasの資料では、value_varsを指定しないときは、id_vars以外のすべての列を並べ直すとしています。架空の表でも、id_varsを付けないと店舗の列まで並べ直され、12行のはずが16行になりました。
空欄のセルは、縦持ちにするとどうなりますか?
空欄のまま1行になり、売上の値はNaNになります。架空の表では、空欄の3か所がNaNの行として残りました。記事のコードでは、dropnaで売上が空欄の行を外しています。
縦持ちにした表を、横持ちに戻せますか?
戻せます。pivot_tableで店舗を行、月を列にして売上を合計すると、月ごとの列が並んだ形になります。架空の表でも、元の表と同じ値に戻りました。ただし、店舗の並びは元の表の順ではなく、名古屋・大阪・東京・福岡の順に変わりました。
並べ直すと、元のExcelのファイルも変わりますか?
変わりません。記事のコードは元のファイルを読むだけで、結果は別の名前のファイルに書き出します。架空の表で試したときも、元のsales_wide.xlsxは変わりませんでした。