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

Python
スポンサーリンク

Pythonで複数ヘッダー行のExcelを読み込む入門 — header=[0,1](MultiIndex)

複数行にまたがる見出し(上段が大分類、下段が小分類)の表は、pandas.read_excelheader=[...] でまとめて列名として読み込み、列の階層(MultiIndex)にできます。初心者向けに、動くコード・整形テンプレート・つまずき対策をわかりやすくまとめました。


基本の使い方

import pandas as pd

# 1行目と2行目をヘッダーに使って読み込み(0始まりで [0, 1])
df = pd.read_excel("multi_header.xlsx", header=[0, 1])

print(df.head())
print(df.columns)  # ← MultiIndex(階層化された列名)
Python
  • 効果: 列名が階層化された MultiIndex になります(例:(“売上”, “1月”), (“売上”, “2月”), (“数量”, “1月”) など)。
  • 前提: 指定した行に「列名」がきちんと並んでいること。余分なタイトル行がある場合は後述の skiprows を併用します。

よくあるレイアウト別テンプレート

タイトルや注記を飛ばしてから複数ヘッダーを取る

import pandas as pd

df = pd.read_excel(
    "report.xlsx",
    skiprows=3,     # 4行目から見出しが始まる
    header=[0, 1]   # 4行目・5行目がヘッダー
)
Python
  • 狙い: 帳票の先頭に会社ロゴや説明文がある場合に対応。

3段階のヘッダー(例:カテゴリ / 指標 / 月)

import pandas as pd

df = pd.read_excel("three_levels.xlsx", header=[0, 1, 2])
Python
  • 狙い: より複雑な多段見出しでも、その段数ぶんを配列で指定すればOK。

必要列だけ読む(usecols)+複数ヘッダー

import pandas as pd

df = pd.read_excel(
    "sales.xlsx",
    header=[0, 1],
    usecols="A:F"  # 表の列範囲に合わせて絞る
)
Python
  • 狙い: 巨大帳票では列を絞ってメモリと速度を確保。

読み込み後の扱い方(選択・集計・フラット化)

階層列の一部だけ選ぶ(タプルで指定)

# 例:上段 "売上" の配下だけ取り出す
sales = df["売上"]  # サブDataFrame(列の第2階層が残る)
print(sales.head())

# 例:("売上","1月") 列だけ
jan_sales = df[("売上", "1月")]
Python

列名をフラット化(結合して1階層に)

# 欠損ヘッダーは空文字で埋めてから連結
df.columns = ["_".join([str(x) for x in tup if x is not None and str(x) != "nan"]).strip()
              for tup in df.columns]
print(df.columns)  # 例:["売上_1月", "売上_2月", "数量_1月", ...]
Python
  • ポイント: フラット化しておくと、後続の加工・保存(CSVなど)がシンプルになります。

列名の正規化(空白除去・日本語→英語)

# 階層の各要素に処理を当てる
df.columns = pd.MultiIndex.from_tuples(
    [tuple(str(s).strip() if s is not None else "" for s in tup) for tup in df.columns]
)
Python

月次合計などの軽い集計

# MultiIndexのままでも選択して合計可能
total = df["売上"].sum(axis=0)  # 各月の合計(横合計)
print(total)
Python

実践テンプレート(よくある帳票に合わせる)

テンプレ1:顧客列+売上/数量の多段ヘッダーを読み、売上だけ抽出してCSV化

import pandas as pd

df = pd.read_excel("customer_sales.xlsx", skiprows=2, header=[0, 1])

# 顧客基本情報は単一列名(例:["顧客ID","顧客名"])、指標は多段(例:「売上」「数量」×「1月〜12月」)
base_cols = ["顧客ID", "顧客名"]
sales_df = df["売上"]  # 多段の「売上」ブロック

out = pd.concat([df[base_cols], sales_df], axis=1)
out.to_csv("sales_only.csv", index=False)
Python

テンプレ2:フラット化してから型変換・月次ランキング

import pandas as pd

df = pd.read_excel("multi_header.xlsx", header=[0, 1])

# フラット化
df.columns = ["_".join([str(x).strip() for x in tup if x]) for tup in df.columns]

# 金額列を一括数値化(カンマ対応)
for c in df.columns:
    if "売上_" in c:
        df[c] = pd.to_numeric(df[c].astype(str).str.replace(",", ""), errors="coerce")

# 例:3月の売上上位5件
top5 = df.nlargest(5, "売上_3月")
print(top5[["顧客ID", "顧客名", "売上_3月"]])
Python

テンプレ3:多段ヘッダーの部分選択→期間合計

import pandas as pd

df = pd.read_excel("sales.xlsx", header=[0, 1])

# "売上" の 1月〜3月だけ選んで合計列を追加
q1 = df["売上"][["1月", "2月", "3月"]].sum(axis=1)
df[("売上", "Q1合計")] = q1  # 新しい階層列として追加

print(df.head())
Python

つまずきやすいポイントと回避策

  • 上段ヘッダーに結合セルがある:
    • 対策: 結合セルが適切に展開されず欠損になることがあります。skiprowsheader 行の位置を調整。必要ならフラット化して欠損は空文字にして扱う。
  • ヘッダー行に空白や全角半角の揺れ:
    • 対策: 読み込み後にヘッダー要素へ strip() を適用して正規化。比較・選択が安定します。
  • 一部列にヘッダーが欠けている(空セル):
    • 対策: MultiIndex の要素が Nonenan になります。フラット化時に安全に連結して名前を補う(例:空要素は無視)。
  • 同名の組み合わせが重複する:
    • 対策: 後処理で列名を一意化(例:重複検出して “_dup” を付与)。または階層選択時に明示的なタプルで指定。
  • 保存時に多段ヘッダーが崩れる(CSV保存):
    • 対策: 保存前に列をフラット化。Excel保存なら to_excel でもMultiIndexは保存可能ですが、読み手のツールによっては見づらいことがあります。

ミニ例題(練習用)

  • 例題1: 二段ヘッダーを読み、”売上” ブロックだけ表示
import pandas as pd
df = pd.read_excel("multi.xlsx", header=[0, 1])
print(df["売上"].head())
Python
  • 例題2: フラット化して「売上_1月」を数値化して合計
import pandas as pd
df = pd.read_excel("multi.xlsx", header=[0, 1])
df.columns = ["_".join([str(x).strip() for x in tup if x]) for tup in df.columns]
df["売上_1月"] = pd.to_numeric(df["売上_1月"].astype(str).str.replace(",", ""), errors="coerce")
print("1月合計:", df["売上_1月"].sum())
Python
  • 例題3: 3段ヘッダーから一部階層だけ選択
import pandas as pd
df = pd.read_excel("three_levels.xlsx", header=[0, 1, 2])
# 例:("カテゴリA","売上",["1月","2月"]) を選択
subset = df[("カテゴリA", "売上")][["1月", "2月"]]
print(subset.head())
Python
タイトルとURLをコピーしました