Python Excel操作 逆引き集 | Excelから複数のヘッダー行を持つ読み込み

Python Python
スポンサーリンク

「ヘッダーが1行とは限らない」という発想を持つ

現場のExcelって、きれいに1行だけ列名が並んでいるとは限らないですよね。
例えばこんな表、見覚えありませんか?

2025年1月2025年1月2025年2月2025年2月
店舗商品数量金額数量金額
東京りんご1010008900
東京みかん54006500

上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())
Python

header=[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:上段が「カテゴリ」、下段が「サブ項目」

例えばこんな表:

売上売上原価原価
店舗商品数量金額数量金額
東京りんご10100010600

これも同じく 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
]
Python

header=[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月", "金額")])
Python

MultiIndex に慣れてくると、
「行も列もタプルで指定してピンポイントに値を取る」ということもできるようになります。


つまずきやすいポイントと注意点

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 を読み、列名をフラットにする

条件は次の通りです。

  1. header=[0, 1] で読み込む
  2. df.columnsf"{top}_{bottom}" 形式でフラットにする
  3. フラット化後の列名と先頭5行を表示する

練習2:行も列も MultiIndex にして、特定の店舗・商品の特定月の金額を取り出す

条件は次の通りです。

  1. header=[0, 1], index_col=[0, 1] で読み込む
  2. df.loc[("東京", "りんご"), ("2025年1月", "金額")] のようにして値を取り出す

最後に

header=[0,1] は、
「Excelの“2段ヘッダー”を、そのまま構造として持ち込む」ためのスイッチです。

そのまま MultiIndex として扱ってもいいし、
読み込んだあとでフラットな列名に変えてもいい。
大事なのは、「ヘッダーが1行とは限らない」という前提を受け入れて、
構造ごときれいに取り込む、という発想です。

あなたの手元の「上2行がヘッダーっぽい変なExcel」があるなら、
そのスクリーンショットを頭に思い浮かべながら、
「どの行を header にするか」「フラットにするか」を設計してみてください。
そこから、pandas と Excel の距離がぐっと縮まります。

タイトルとURLをコピーしました