読者です 読者をやめる 読者になる 読者になる

StatsFragments

Python, R, Rust, 統計, 機械学習とか

Python pandas で e-Stat のデータを取得したい

e-Stat とは

"「政府統計の総合窓口(e-Stat)」は、各府省が公表する統計データを一つにまとめ、統計データの検索をはじめとした、さまざまな機能を備えた政府統計のポータルサイト" だそうだ。このデータを pandas で読めるとうれしい...ということで対応した。

github.com

インストール

$ pip install japandas

パッケージのインポート

import numpy as np
np.__version__
# '1.10.2'

import pandas as pd
pd.__version__
# u'0.17.1'

import japandas as jpd
jpd.__version__
# '0.2.0'

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

e-Stat を利用するには 利用登録とアプリケーション ID の取得が必要。利用ガイドに沿って登録する。

データの取得

japandas を利用してデータを取得する。データの取得は以下 2 ステップで行う

  1. "政府統計コード" を利用して、統計調査に含まれる統計表 ( 実データ ) の一覧とその ID ( 統計表 ID ) を取得する。
  2. 取得した統計表 ID を利用して、実データを取得する

1. 統計表一覧の取得

e-Stat 提供データ一覧 に含まれる統計調査のうち、今回は 00200564 全国消費実態調査 を利用する。

jpd.DataReader で ID "00200564" のデータを取得すると、以下のような DataFrame が返ってくる。各カラムの詳細は e-Stat API 仕様 に記載されている。

key = "Your application ID"
dlist = jpd.DataReader("00200564", 'estat', appid=key)
dlist

f:id:sinhrks:20151231180421p:plain

一つの "統計表題名及び表番号" は "調査年月" が異なる複数のデータを持つことがある。値をユニークにした方が中身を確認しやすい。

tables = dlist[u'統計表題名及び表番号'].value_counts().to_frame()
tables

f:id:sinhrks:20151231180429p:plain

ここでは "平成26年全国消費実態調査 > 全国 > 品目及び購入先・購入地域に関する結果 > 単身世帯" の "男女,年齢階級,購入形態,品目別1世帯当たり1か月間の支出" のデータを取得したい。

この時点では 正確な "統計表題名及び表番号" がわからないため、まずはそれらしい文字列でレコードを抽出する。

indexer = tables.index.str.contains(u'男女,年齢階級,購入形態,品目別1世帯当たり1か月間の支出')
indexer
# array([False, False, False, ..., False, False, False], dtype=bool)

tables[indexer]

f:id:sinhrks:20151231180441p:plain

上の結果から 正確な "統計表題名及び表番号" が得られるため、元データから対象のレコードが抽出できる。

table = tables[indexer].index[0]
table
# [単身世帯]フロー編第149表 男女,年齢階級,購入形態,品目別1世帯当たり1か月間の支出

target = dlist[dlist[u'統計表題名及び表番号'] == table]
target

f:id:sinhrks:20151231180453p:plain

2. 実データの取得

  1. で調べた "統計表 ID" ( "0003109612" ) を jpd.DataReader に渡せばよい。
df = jpd.DataReader("0003109612", 'estat', appid=key)
# 略

が、いちいち文字列を抽出したり再入力するのは面倒だ。そんな時は、上の結果 ( 取得対象の "統計表 ID" を含む DataFrame) をそのまま渡してもよい。複数のレコードがある場合は全データを連結して返す。

df = jpd.DataReader(target, 'estat', appid=key)
df

f:id:sinhrks:20151231180558p:plain

出典:「平成26年全国消費実態調査調査結果」(総務省統計局)

取得したデータを集計してみる。各属性に対応する値は "value" カラムに含まれている。まず、カラム名を簡潔なものに変更する。

df.columns =[u'value', u'世帯区分', u'品目分類表', u'地域', u'表章項目', u'男女', u'年齢階級', u'購入形態']

また、dtypes を見ると全ての列が object 型になっている。通常、"value" には数値が入るため、jpd.DataReader は "value" が数値に変換できる場合は自動で変換するのだが、このデータでは何らかの理由で失敗しているようだ。

df.dtypes
# value    object
# 世帯区分     object
# 品目分類表    object
# 地域       object
# 表章項目     object
# 男女       object
# 年齢階級     object
# 購入形態     object
# dtype: object

pd.to_numeric で数値に変換しようとすると ValueError が発生する。データを見ると "value" にハイフン "-" がいくつか使われているようだ (やめてくれ...)。

文字列処理してもよいが、今回は特に何もしなくても以降の処理で解決されるため、とりあえずそのままにしておく。

1/9 補足 v0.2.1 では "-" を欠損として扱うように修正済み。

pd.to_numeric(df['value'])
# ValueError: Unable to parse string

df['value'].str.isnumeric()
# 時間軸(年次)
# 2014-01-01     True
# 2014-01-01     True
# 2014-01-01     True
# 2014-01-01    False
#               ...  
# 2014-01-01    False
# 2014-01-01    False
# 2014-01-01    False
# 2014-01-01    False
# Name: value, dtype: bool

"品目分類表" の値をみるとかなり細かい項目に分けられていることがわかる。

統計調査に利用された調査票 を参照すると、調査形式は家計簿のようなフォーマットに任意の品名を書き込むもののようだ。品目分類を被調査者が記入する欄はなく、集計時に家計簿を品目分類表に従って分類しているのだろう。

df[u'品目分類表'].unique()
# array([u'集計世帯数',
#        u'世帯数分布(抽出率調整)',
#        u'世帯数分布(抽出率調整)(1万分比)',
#        ..., u'仕送り金',
#        u'国内遊学仕送り金',
#        u'他の仕送り金'], dtype=object)

データから "すし" を含むレコードを抽出してみる。...弁当とは? この調査票には詳細が記載されていないようだが、他の調査を見ると スーパーで買うパックの寿司を指しているものと思う (おにぎりは別項目)。

filtered = df[df[u'品目分類表'].str.contains(u'すし')]
filtered

f:id:sinhrks:20151231180613p:plain

抽出されたレコードの "value" には 数値として不正な文字列は含まれないため、pd.to_numeric で数値に変換できる。

filtered['value'] = pd.to_numeric(filtered['value'])
filtered.dtypes
# value     int64
# 世帯区分     object
# 品目分類表    object
# 地域       object
# 表章項目     object
# 男女       object
# 年齢階級     object
# 購入形態     object
# dtype: object

Series.nunique で各列に含まれるユニークな値の数を調べる。"男女", "年齢階級", "購入形態" でレコードが分かれているため、それら 3 つをキーにして集計してやればよい。

filtered.apply(lambda x: x.nunique())
# value    76
# 世帯区分      1
# 品目分類表     1
# 地域        1
# 表章項目      1
# 男女        3
# 年齢階級      8
# 購入形態      4
# dtype: int64

sushi = pd.pivot_table(filtered, index=[u'男女', u'年齢階級'], columns=u'購入形態', values='value', aggfunc='sum')
sushi

f:id:sinhrks:20151231180626p:plain

"購入形態" (支払い方法) には興味がないので、"合計" の値だけを抽出してプロットする。

60 代 男性 単身者 は "すし(弁当)" への消費金額が比較的多いようだ。金額的に月 1 〜 2 回買っている感じだろうか。また女性も一部男性と比べ高い。

sushi = sushi[[u'合計']]
sushi.plot.bar(ylim=(0, 1000))

f:id:sinhrks:20151231221734p:plain

追加データでの確認

同じ統計調査に "二人以上の世帯" のデータも含まれているので、同項目をみてみる。先ほどと同じように、まず 対象の統計表 ID を調べる。世帯別の集計になるため、男女/年齢といった区分はないが、品目別の支出がわかるデータを探す。

indexer2 = tables.index.str.contains(u'品目別1世帯当たり1か月間の支出')
tables[indexer2]

f:id:sinhrks:20151231182910p:plain

table2 = tables[indexer2].index[3]
target2 = dlist[dlist[u'統計表題名及び表番号'] == table2]
target2

f:id:sinhrks:20151231182922p:plain

見つけた 統計表 ID から実データを取得する。

df2 = jpd.DataReader(target2, 'estat', appid=key)
df2

f:id:sinhrks:20151231183005p:plain

カラム名を変更し、"すし" かつ 地域が "全国" のデータのみを抽出する。

df2.columns = [u'value', u'世帯区分', u'品目分類表', u'地域', u'表章項目']
sushi2 = df2[df2[u'品目分類表'].str.contains(u'すし') & (df2[u'地域'] == u'全国')]
sushi2[u'value'] = pd.to_numeric(sushi2[u'value'])
sushi2

f:id:sinhrks:20151231183012p:plain

この数値が二人以上世帯での消費金額の平均になる。先のグラフに重ねてプロットする。

ax = sushi.sum(axis=1).plot.bar(ylim=(0, 1000))
ax.axhline(y=sushi2.iloc[1, 0], color='red')

f:id:sinhrks:20151231221723p:plain

単身者で 二人以上世帯の消費金額とほぼ同じ金額を使っていれば消費が多いと言ってよさそうだ。単純な見方をすると 料理は面倒だし外食は気疲れする...と感じる頻度が高い層が買っているのだろうか。被調査者によってかなり偏りがあると考えられるので、ここまで細項目を取るなら分布が見てみたい。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理