ふくふくHukuhuku Inc.
EP.28Data Fetch 10分公開: 2026-09-02

人が読む表を、機械が読める形に直す ── セル結合と多段の見出し

公的統計の表は、印刷して読むために作られています。セル結合、多段の見出し、注記の記号、合計行。そのまま読み込むと、まともに使えません。

#前処理#公的統計#Python
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

公的機関が配布する統計の表は、人が印刷して読むために作られています。見やすさを優先しているので、機械が読む形にはなっていません。ダウンロードして読み込むと、想像とはかなり違うものが出てきます。

配布の形式は のこともあれば表計算ソフトの形式のこともあります。典型的なのは、多段の見出し注記の記号合計行の混在の4つです。どれも人が読むぶんには自然ですが、そのまま集計に回すと壊れます。

この回では、それぞれが何を引き起こすかと、へどう直すかを扱います。地味な作業ですが、ここを飛ばすと後の分析が全部おかしくなります。

セル結合は空欄になる

セルを結合すると、画面では1つのマスに見えますが、データとしては最初のマスにだけ値が入り、残りは空になります。都道府県名が縦に結合されている表を読み込むと、1行目だけに県名があり、以降は空欄という状態になります。

対処は、上の値を下へ埋めることです。ただし機械的に埋めると、本来空であるべき場所まで埋まります。埋める対象の列を明示的に指定してください。

結合セル由来の空欄だけを埋める。列を指定して機械的に埋めない
Python
import pandas as pd

def fill_merged(df: pd.DataFrame, cols: list[str]) -> pd.DataFrame:    """結合セル由来の空欄を上から埋める。対象列は必ず明示する。
    全列に対して ffill すると、本来欠損であるべき値まで埋まる。    """    missing = set(cols) - set(df.columns)    if missing:        raise KeyError(f"指定された列が無い: {missing}")
    out = df.copy()    out[cols] = out[cols].ffill()    return out

見出しが複数行にまたがる

「男」「女」の下にそれぞれ「実数」「割合」が並ぶような、2段・3段の見出しもよくあります。1行目だけを見出しとして読むと、2行目がデータの1行目として混ざります。

段を連結して1つの列名にするのが基本です。「男_実数」「男_割合」のようにすれば、扱いやすい形になります。上の段は結合されていることが多いので、先に埋めてから連結します。

多段の見出しを1段に潰す
Python
import pandas as pd

def flatten_header(df: pd.DataFrame, sep: str = "_") -> pd.DataFrame:    """複数段の見出しを連結して1段にする。    空の段は飛ばすので、片方しか無い列も自然な名前になる。"""    if not isinstance(df.columns, pd.MultiIndex):        return df
    cols = []    for tup in df.columns:        parts = [str(x).strip() for x in tup if str(x).strip() and not str(x).startswith("Unnamed")]        cols.append(sep.join(parts) if parts else "unnamed")
    out = df.copy()    out.columns = cols    return out

横持ちを縦持ちに直す

公的統計の表は、年や地域が列として横に並んでいることがよくあります。2020年、2021年、2022年…と列が伸びていく形です。人が眺めるには良いのですが、年が増えるたびに列が増えるので、処理の側から見ると扱いにくい。

これを縦に倒して、1行が1つの観測になる形へ直します。年が増えても列は増えず、行が増えるだけになります。なら1つの関数で変換できるので、読み込んだ直後にこの形へ揃えておくのが定石です。

横に並んだ年を、行へ倒す
Python
import pandas as pd

def to_long(df: pd.DataFrame, id_cols: list[str], var_name: str = "year") -> pd.DataFrame:    """id_cols 以外の列を、すべて縦に倒す。
    列が「2020」「2021」…と並んでいる表を、    (地域, 年, 値) の 3 列に直す。    """    value_cols = [c for c in df.columns if c not in id_cols]    if not value_cols:        raise ValueError("倒す対象の列が無い。id_cols の指定を確認")
    out = df.melt(id_vars=id_cols, value_vars=value_cols, var_name=var_name, value_name="value")    # 年の列は文字列で来る。数値に直せないものは残して後で確認する    out[var_name] = out[var_name].astype(str).str.extract(r"(d{4})")[0]    return out

注記の記号と合計行

数値の列に、「-」「…」「※」「x」 といった記号が混ざります。これらは「該当なし」「未公表」「秘匿」といった意味を持っており、それぞれ違う理由で数値が無いことを表しています。全部を0にすると、意味が失われます。

ここでも一緒に済ませておくと、後の工程が単純になります。秘匿されている値を0として集計すると、合計が実態より小さくなります。どの記号がどの意味かは、表の凡例に書いてあります。読み飛ばさず、記号ごとに扱いを決めてください。

公的統計でよく見る記号
記号よくある意味扱い
-該当する数値が無い0 と欠損のどちらかを凡例で確認
未公表・調査していない欠損。0 にしない
x秘匿(件数が少なく特定されうる)欠損。ただし0ではない
△ / ▲負の数マイナスとして数値化
*注記あり値としては使える。注記を読む

もう一つ厄介なのが、合計行が明細行と同じ表に混ざっていることです。「全国」の行と各都道府県の行が並んでいる表で、そのまま合計すると全国分が二重に数えられます。行の意味を判別して、明細だけを残す必要があります。

合計と明細が合うか確かめる

合計行を落とす前に、明細の合計が合計行と一致するかを確認してください。一致しなければ、読み込みの段階で行が欠けているか、注記の記号を落としてしまっています。この検算は、前処理が正しいことの証明になります

この種の作業は 前処理の現場 でより詳しく扱っています。この連載では、取得の直後にどこまでやっておくかという観点で触れました。次回からは、業務システムからの取得に移ります。

こうした整形は、一度書けば同じ配布元には使い回せます。表の作りは年度が変わっても大きくは変わらないので、最初に丁寧にやっておくと翌年からは読み込むだけで済みます。

整形の途中でどんな判断をしたかは、コードのコメントに残しておいてください。なぜこの行を落としたのか、なぜこの記号を欠損にしたのかは、半年後の自分でも思い出せません。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp