この記事でわかること

  • 月ごとに列が並んだ横持ちの表を、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で外し、行の数と売上の合計が元の表と合うかを確かめます
  • 並べ直した表は新しいファイルとして保存し、横持ちの元の表はそのまま残します