公的機関が配布する統計の表は、人が印刷して読むために作られています。見やすさを優先しているので、機械が読む形にはなっていません。ダウンロードして読み込むと、想像とはかなり違うものが出てきます。
配布の形式は のこともあれば表計算ソフトの形式のこともあります。典型的なのは、、多段の見出し、注記の記号、合計行の混在の4つです。どれも人が読むぶんには自然ですが、そのまま集計に回すと壊れます。
この回では、それぞれが何を引き起こすかと、へどう直すかを扱います。地味な作業ですが、ここを飛ばすと後の分析が全部おかしくなります。
セル結合は空欄になる
セルを結合すると、画面では1つのマスに見えますが、データとしては最初のマスにだけ値が入り、残りは空になります。都道府県名が縦に結合されている表を読み込むと、1行目だけに県名があり、以降は空欄という状態になります。
対処は、上の値を下へ埋めることです。ただし機械的に埋めると、本来空であるべき場所まで埋まります。埋める対象の列を明示的に指定してください。
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つの列名にするのが基本です。「男_実数」「男_割合」のようにすれば、扱いやすい形になります。上の段は結合されていることが多いので、先に埋めてから連結します。
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つの関数で変換できるので、読み込んだ直後にこの形へ揃えておくのが定石です。
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 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。