「ヘッダーが1行とは限らない」という発想を持つ
現場のExcelって、きれいに1行だけ列名が並んでいるとは限らないですよね。
例えばこんな表、見覚えありませんか?
| 2025年1月 | 2025年1月 | 2025年2月 | 2025年2月 | ||
|---|---|---|---|---|---|
| 店舗 | 商品 | 数量 | 金額 | 数量 | 金額 |
| 東京 | りんご | 10 | 1000 | 8 | 900 |
| 東京 | みかん | 5 | 400 | 6 | 500 |
上2行が「ヘッダー」で、3行目からがデータ。
こういうときに使うのが header=[0, 1] です。
「0行目と1行目をセットで列名にして、MultiIndex(階層化された列)として読み込む」という指定になります。
基本形:header=[0,1] で複数行ヘッダーを読む
最小コードと挙動のイメージ
上のような表が sales_multi_header.xlsx に入っているとします。
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1]
)
print(df.columns)
print(df.head())
Pythonheader=[0, 1] とすると、0行目と1行目が「列ラベル」として使われ、
列名は「階層構造(MultiIndex)」になります。
例えば、df.columns はこんな感じになります(イメージ):
('店舗', '')('商品', '')('2025年1月', '数量')('2025年1月', '金額')('2025年2月', '数量')('2025年2月', '金額')
「上の段」と「下の段」がペアになって、1つの列ラベルになっているイメージです。
MultiIndex 列をどう扱うか
列を指定して取り出す
MultiIndex 列は「タプル」で指定します。
qty_jan = df[("2025年1月", "数量")]
amt_feb = df[("2025年2月", "金額")]
print(qty_jan.head())
print(amt_feb.head())
Python「上段」「下段」をセットで指定する感じです。
最初は少し違和感があるかもしれませんが、「2段のラベルを1組として扱う」と思えばOKです。
列名をフラットにしてしまう(実務でよくやる技)
MultiIndex のままでも扱えますが、「コードをシンプルにしたい」「列名を1段にしたい」ことも多いです。
その場合は、読み込んだあとで列名を結合してしまいます。
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1]
)
df.columns = [
f"{str(top)}_{str(bottom)}".strip("_")
for top, bottom in df.columns
]
print(df.columns)
print(df.head())
Python例えば、こんな感じの列名になります(イメージ):
"店舗""商品""2025年1月_数量""2025年1月_金額""2025年2月_数量""2025年2月_金額"
こうしてしまえば、あとは普通の1段の列名として扱えます。
実務でよくある「複数ヘッダー」のパターン
パターン1:上段が「期間」、下段が「項目」
さっきの例のように、「2025年1月/2025年2月」が上段、「数量/金額」が下段、というパターンです。
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1]
)
df.columns = [
f"{top}_{bottom}".strip("_")
for top, bottom in df.columns
]
print(df.head())
Pythonこのあと、例えば「2025年1月の金額だけ」を使いたいなら、df["2025年1月_金額"] と書けます。
パターン2:上段が「カテゴリ」、下段が「サブ項目」
例えばこんな表:
| 売上 | 売上 | 原価 | 原価 | ||
|---|---|---|---|---|---|
| 店舗 | 商品 | 数量 | 金額 | 数量 | 金額 |
| 東京 | りんご | 10 | 1000 | 10 | 600 |
これも同じく header=[0, 1] で読みます。
df = pd.read_excel("sales_cost.xlsx", header=[0, 1])
print(df.columns)
Python列名は例えばこうなります:
('店舗', '')('商品', '')('売上', '数量')('売上', '金額')('原価', '数量')('原価', '金額')
これもフラットにしてしまうと扱いやすくなります。
df.columns = [
f"{top}_{bottom}".strip("_")
for top, bottom in df.columns
]
Pythonheader=[0,1] と他の引数の組み合わせ
index_col と組み合わせて「行ラベル」を決める
例えば、「店舗」と「商品」をインデックスにしたい場合。
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1],
index_col=[0, 1]
)
print(df.index)
print(df.columns)
Pythonここでは、
行インデックス:店舗×商品(MultiIndex)
列インデックス:期間×項目(MultiIndex)
という「行も列も階層構造」の表になります。
ピボットテーブル的な集計をそのまま読み込んで分析したいときに便利です。
usecols と組み合わせるときの注意
header=[0, 1] を使うときに usecols を列番号で指定すると、
「どの列がどのラベルになるか」が少しわかりにくくなります。
できれば列名(上段・下段)を見てから、
「どの列を残すか」を決める方が安全です。
一度全部読み込んで print(df.columns) を確認し、
そのあとでフラット化してから列を選ぶ、という流れが現実的です。
実践テンプレート
テンプレ1:複数ヘッダーを読み、列名をフラットにしてから集計
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1]
)
df.columns = [
f"{str(top)}_{str(bottom)}".strip("_")
for top, bottom in df.columns
]
df["2025年1月_金額"] = pd.to_numeric(df["2025年1月_金額"], errors="coerce")
df["2025年2月_金額"] = pd.to_numeric(df["2025年2月_金額"], errors="coerce")
df["合計_金額"] = df["2025年1月_金額"] + df["2025年2月_金額"]
print(df[["店舗", "商品", "合計_金額"]])
Python「複数ヘッダー → フラット化 → 普通の集計」という流れの典型形です。
テンプレ2:行も列も MultiIndex のまま、特定の部分だけ取り出す
import pandas as pd
df = pd.read_excel(
"sales_multi_header.xlsx",
header=[0, 1],
index_col=[0, 1]
)
print(df.loc[("東京", "りんご"), ("2025年1月", "金額")])
PythonMultiIndex に慣れてくると、
「行も列もタプルで指定してピンポイントに値を取る」ということもできるようになります。
つまずきやすいポイントと注意点
header の行番号は「0始まり」
header=[0, 1] は「1行目と2行目」ではなく、「0行目と1行目」です。
Excel上で見える行番号(1,2,3,…)とはずれるので、
「どの行をヘッダーにしたいか」を一度意識してから指定してください。
例えば、こんな構造なら:
1行目: タイトル
2行目: 期間(2025年1月, 2025年2月, …)
3行目: 項目(数量, 金額, …)
4行目以降: データ
この場合は header=[1, 2] になります(0始まりで数える)。
MultiIndex のままだと列指定が少し面倒
df[("2025年1月", "金額")] のようにタプルで指定する必要があるので、
慣れないうちは「列名をフラットにしてしまう」方が楽です。
小さな練習問題
練習1:2行ヘッダーの sales_multi_header.xlsx を読み、列名をフラットにする
条件は次の通りです。
header=[0, 1]で読み込むdf.columnsをf"{top}_{bottom}"形式でフラットにする- フラット化後の列名と先頭5行を表示する
練習2:行も列も MultiIndex にして、特定の店舗・商品の特定月の金額を取り出す
条件は次の通りです。
header=[0, 1],index_col=[0, 1]で読み込むdf.loc[("東京", "りんご"), ("2025年1月", "金額")]のようにして値を取り出す
最後に
header=[0,1] は、
「Excelの“2段ヘッダー”を、そのまま構造として持ち込む」ためのスイッチです。
そのまま MultiIndex として扱ってもいいし、
読み込んだあとでフラットな列名に変えてもいい。
大事なのは、「ヘッダーが1行とは限らない」という前提を受け入れて、
構造ごときれいに取り込む、という発想です。
あなたの手元の「上2行がヘッダーっぽい変なExcel」があるなら、
そのスクリーンショットを頭に思い浮かべながら、
「どの行を header にするか」「フラットにするか」を設計してみてください。
そこから、pandas と Excel の距離がぐっと縮まります。
