ツール

統計データを扱う記事の数字を、人手のコピペではなくe-Stat(政府統計の総合窓口)APIから直接取る仕組みをPHPで作りました。実装自体は素直なのですが、「どの統計表のどの指標を指定すればいいか」を探すのが最初の壁で、最後にPHPの配列キーの仕様で1つバグを踏みました。この記事では、アプリケーションIDの取得から都道府県ランキングの組み立てまでの実装と、踏んだ罠を実コード・実データで残します。

なお、APIキー不要で使える「統計ダッシュボードAPI」で足りる用途もあります。そちらは政府統計APIとヘッドレスChromeで統計グラフ画像を自動生成するに書きました。今回はキーが必要な本家e-Stat APIで、より細かい統計表を扱います。

アプリケーションIDの取得

e-Stat APIは無料ですが、利用登録して「アプリケーションID」を発行する必要があります。e-Stat API機能からアカウントを作り、マイページでURLを登録するとIDが発行されます(審査待ちはなく即時でした)。以降のリクエスト全部に appId パラメータとして付けます。コードに直書きせず、定数や環境変数に置きます。

統計表IDの探し方(getStatsList)

最初の壁はここです。e-Statには数十万の統計表があり、目当ての数字が「どの統計表」に入っているかをまず特定する必要があります。統計表の検索が getStatsList です。

curl "https://api.e-stat.go.jp/rest/3.0/app/json/getStatsList?appId=<ID>&statsCode=00200502&searchWord=共働き&limit=10"

ポイントは statsCode=00200502(社会・人口統計体系)で絞ることです。都道府県別の比較をしたいとき、この統計体系の「都道府県データ」には基礎データ(実数)と社会生活統計指標(割合などの加工済み指標)が揃っていて、記事に使うには後者が便利です。上の検索では「共働き世帯割合」を含む統計表として 0000010206(社会生活統計指標 F労働)が返ってきました。

指標コードの探し方(getMetaInfo)

統計表IDが分かったら、表の中のどの指標かを getMetaInfo で調べます。返ってくるメタ情報の cat01 分類に指標の一覧が入っています。

curl "https://api.e-stat.go.jp/rest/3.0/app/json/getMetaInfo?appId=<ID>&statsDataId=0000010206"

この応答から「共働き世帯割合」の指標コードが #F01503 だと分かります。time 分類には収録年度(この表では1975〜2024年度)も入っているので、あとで使います。

都道府県ランキングを組み立てる(getStatsData)

データ本体は getStatsData です。統計表IDと指標コードを指定して取得し、47都道府県のランキングに組み立てます。WordPressプラグイン内で動かしているので wp_remote_get を使っていますが、HTTPクライアントは何でも構いません。

$url = 'https://api.e-stat.go.jp/rest/3.0/app/json/getStatsData?' . http_build_query( array(
	'appId'       => ESTAT_APP_ID,
	'statsDataId' => '0000010206',
	'cdCat01'     => '#F01503', // 共働き世帯割合
	'metaGetFlg'  => 'Y',       // 地域コード→名称などのメタも一緒に受け取る
) );
$body = json_decode( wp_remote_retrieve_body( wp_remote_get( $url, array( 'timeout' => 30 ) ) ), true );
$data = $body['GET_STATS_DATA']['STATISTICAL_DATA'];

// 値を年度ごとに集める。都道府県は '01000'〜'47000'(全国 '00000' は除外)。
$by_time = array();
foreach ( $data['DATA_INF']['VALUE'] as $v ) {
	$area = $v['@area'];
	if ( '00000' === $area || '000' !== substr( $area, -3 ) ) {
		continue; // 全国と市区町村を除外
	}
	if ( ! is_numeric( $v['$'] ) ) {
		continue; // 欠測は '-' や '***' で入ってくる
	}
	$by_time[ $v['@time'] ][ $area ] = (float) $v['$'];
}

// 47都道府県が揃っている最新年度を採用する。
// 5年周期の調査に基づく指標は、調査年以外の年度が欠測になるため
// 「単純に最新の年度」を取ると数件しか値のない年に当たることがある。
krsort( $by_time );
foreach ( $by_time as $time_code => $values ) {
	if ( count( $values ) >= 47 ) {
		break;
	}
}
arsort( $values ); // 値の降順 = ランキング

実行結果(共働き世帯割合、2020年度)はこうなりました。

1位 福井県 34.69
2位 山形県 34.4
3位 富山県 32.83
4位 長野県 31.4
5位 島根県 31.22

踏んだ罠 ― PHPは数値文字列の配列キーを勝手にintにする

組み上がったランキングから「福井県のコード '18000' の行を厳密比較で探す」処理を足したところ、ランキングには確かに福井が1位でいるのに「見つからない」という不可解な状態になりました。デバッグ出力がこれです。

var_dump( $row['code'], '18000', $row['code'] === '18000' );
// int(18000)
// string(5) "18000"
// bool(false)

原因はPHPの仕様です。整数として妥当な数値文字列を配列のキーに使うと、PHPは黙って整数キーに変換します$by_time[$time][$area] と組んだ時点で、'18000'int(18000) になっていました。あとで foreach でキーを取り出すと整数が出てくるので、文字列 '18000' との === は偽になります。

この罠が気づきにくいのは、先頭にゼロが付くコードは変換されないことです。'06000'(山形)は整数として妥当な表記ではないので文字列のまま残ります。つまり同じ配列の中に整数キーと文字列キーが混在し、緩い比較や添字アクセスでは何も起きず、厳密比較と in_array(..., true) だけが壊れます。都道府県コードのように「5桁ゼロ埋めの数字」を扱うときの定番の落とし穴だと思います。

対処はキーから取り出した値の正規化です。

// PHPは '18000' のような数値文字列を配列キーにすると整数へ勝手にキャストする
// ('06000' は先頭ゼロのため文字列のまま)。型が混在すると厳密比較が壊れるので
// 5桁の文字列表現に統一する。
$code = str_pad( (string) $code, 5, '0', STR_PAD_LEFT );

連想配列のキーに「コード番号」を使う設計自体を避けて、値の配列にコードをフィールドとして持たせるのも根本対策になります。今回はAPIの応答構造に沿ってキー集計が素直だったため、正規化で対応しました。