はじめに

(12月29日に後半部分を修正し、30日に追記しました)

Qita Rアドベントに政府統計ポータル(e-Stat)と R でサンタさんの12月の出費動向を調べてみたという良記事がありました。 とても参考になるのですが、ポータルからマウスで項目をクリックしていくのが大変です。特に最後の方のフェーズではページが強制的にフルスクリーンになってしまい、上記アドベントの操作を確認できなくなります。

そこでAPIを使ってみます。

その前に、e-Stat API を少し触ってみましょう。

公式説明PDFをパクる

恐れ多くも総務省統計局統計センター様謹製PDF の最後の事例をパクります。

RCurl でjson形式のデータを引っ張ってきます。次世代統計システムから取得した自分のアカウントが必要です。以下のコードで yourAPPID の部分を取得したIDと変えてください。さらに加えて、統計表IDや分類表IDが必要なんですが、後述します。

> library(RCurl)
> yourAPPID <- "yourAPPID"
> url <- paste("http://api.e-stat.go.jp/rest/1.0/app/json/getStatsData?appId=",
+               yourAPPID,
+               "&statsDataId=0003013276",
+               "&cdArea=09003,22004",
+               "&cdCat01=010920070",
+               "&cdTimeFrom=2012000101",
+               "&cdTimeTo=2013000303",sep = "")
> x <- getURI(url)

あまり使ったことなかったので、この機会に jsonlite を使ってjsonを解釈しますた。

> library(jsonlite)
> x2 <- fromJSON(x)

構造を確認してみましょう(一部、端折ります)。

> str(x2)
List of 1
 $ GET_STATS_DATA:List of 3
  ..$ RESULT          :List of 3
  ..$ PARAMETER       :List of 6
  ..$ STATISTICAL_DATA:List of 3
  .. ..$ TABLE_INF:List of 9
  .. .. ..$ @id            : chr "0003013276"
  .. .. ..$ STAT_NAME      :List of 2
  .. .. .. ..$ @code: chr "00200561"
  .. .. .. ..$ $    : chr "家計調査"
  .. .. ..$ GOV_ORG        :List of 2
  .. .. ..$ STATISTICS_NAME: chr "家計調査 家計収支編 二人以上の世帯"
  .. .. ..$ TITLE          :List of 2
  .. .. .. ..$ $  : chr "品目分類 品目分類(平成22年改定)(総数:金額)"
  .. ..$ CLASS_INF:List of 1
  .. .. ..$ CLASS_OBJ:'data.frame': 5 obs. of  3 variables:
  .. .. .. ..$ @id  : chr [1:5] "tab" "cat01" "cat02" "area" ...
  .. .. .. ..$ @name: chr [1:5] "表章項目" "品目分類(22年改定)" "世帯区分" "地域区分" ...
  .. .. .. ..$ CLASS:List of 5
  .. .. .. .. ..$ :List of 3
  .. .. .. .. ..$ :List of 4
  .. .. .. .. .. ..$ @code : chr "010920070"
  .. .. .. .. .. ..$ @name : chr "ぎょうざ"
  .. .. .. .. .. ..$ @level: chr "4"
  .. .. .. .. .. ..$ @unit : chr "円"
  .. .. .. .. ..$ :'data.frame':    4 obs. of  3 variables:
  .. .. .. .. .. ..$ @code : chr [1:4] "03" "04" "01" "02"
  .. .. .. .. .. ..$ @name : chr [1:4] "二人以上の世帯" "二人以上の世帯のうち勤労者世帯" "二人以上の世帯(農林漁家世帯を除く)" "二人以上の世帯のうち勤労者世帯(農林漁家世帯を除く)"
  .. .. .. .. ..$ :'data.frame':    2 obs. of  3 variables:
  .. .. .. .. .. ..$ @code : chr [1:2] "09003" "22004"
  .. .. .. .. .. ..$ @name : chr [1:2] "09201 宇都宮市" "22130 浜松市"
  .. .. .. .. ..$ :'data.frame':    15 obs. of  3 variables:
  .. .. .. .. .. ..$ @code : chr [1:15] "2013000303" "2013000202" "2013000101" "2012001212" ...
  .. .. .. .. .. ..$ @name : chr [1:15] "2013年3月" "2013年2月" "2013年1月" "2012年12月" ...
  .. ..$ DATA_INF :List of 2
  .. .. ..$ NOTE :'data.frame': 3 obs. of  2 variables:
  .. .. ..$ VALUE:'data.frame': 30 obs. of  7 variables:
  .. .. .. ..$ @tab  : chr [1:30] "01" "01" "01" "01" ...
  .. .. .. ..$ @cat01: chr [1:30] "010920070" "010920070" "010920070" "010920070" ...
  .. .. .. ..$ @cat02: chr [1:30] "03" "03" "03" "03" ...
  .. .. .. ..$ @area : chr [1:30] "09003" "09003" "09003" "09003" ...
  .. .. .. ..$ @time : chr [1:30] "2013000303" "2013000202" "2013000101" "2012001212" ...
  .. .. .. ..$ @unit : chr [1:30] "円" "円" "円" "円" ...
  .. .. .. ..$ $     : chr [1:30] "358" "310" "294" "464" ...

とりあずデータを取り出すには GET_STATS_DATA$STATISTICAL_DATA$DATA_INF$VALUE だけ抜けば良いことが分かりました。 抜きます。

> df <- x2$GET_STATS_DATA$STATISTICAL_DATA$DATA_INF$VALUE

列は増えていますが、最初に言及したPDFと多分同じですね。

> colnames(df)
[1] "@tab"   "@cat01" "@cat02" "@area"  "@time"  "@unit"  "$"     
> colnames(df)[7] <- "@yen"
> head(df)
  @tab    @cat01 @cat02 @area      @time @unit @yen
1   01 010920070     03 09003 2013000303    円  358
2   01 010920070     03 09003 2013000202    円  310
3   01 010920070     03 09003 2013000101    円  294
4   01 010920070     03 09003 2012001212    円  464
5   01 010920070     03 09003 2012001111    円  377
6   01 010920070     03 09003 2012001010    円  324

クラスIDを確認する方法

ちなみに政府統計APIを利用するには、各種カテゴリのクラスIDを知る必要があります。 上で抽出したJSONフォーマットでも CLASS_INF$CLASS_OBJ$CLASS に保存されています。

> # 以下にIDが確認できます
> x2$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ[1:2]
    @id                @name
1   tab             表章項目
2 cat01 品目分類(22年改定)
3 cat02             世帯区分
4  area             地域区分
5  time       時間軸(月次)
> # ついでなので、もっと詳しくみてみましょうか
> codes <- x2$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ$CLASS
> length(codes)
[1] 5
> codes
[[1]]
[[1]]$`@code`
[1] "01"
[[1]]$`@name`
[1] "金額"
[[1]]$`@level`
[1] ""

[[2]]
[[2]]$`@code`
[1] "010920070"
[[2]]$`@name`
[1] "ぎょうざ"
[[2]]$`@level`
[1] "4"
[[2]]$`@unit`
[1] "円"

[[3]]
  @code                                                @name @level
1    03                                       二人以上の世帯      1
2    04                       二人以上の世帯のうち勤労者世帯      1
3    01                 二人以上の世帯(農林漁家世帯を除く)      1
4    02 二人以上の世帯のうち勤労者世帯(農林漁家世帯を除く)      1

[[4]]
  @code          @name @level
1 09003 09201 宇都宮市      1
2 22004   22130 浜松市      1

[[5]]
        @code      @name @level
1  2013000303  2013年3月      1
2  2013000202  2013年2月      1
3  2013000101  2013年1月      1
4  2012001212 2012年12月      1
5  2012001111 2012年11月      1

政府統計の統計表IDを確認する方法

さて、APIを利用して統計表を抜くには、そのIDが必要なので、取り出します。これはXMLなので XML パッケージを使います。とても時間がかかります。

> library(XML)
> url <- paste("http://statdb.nstac.go.jp/rest/1.0/app/getStatsList?appId=", yourAPPID, sep = "")
> estat <- xmlParse(url)

Rでやるのが辛いなら、端末で抜きましょう。

$ wget http://statdb.nstac.go.jp/rest/1.0/app/getStatsList?appId=yourAPPID --output-document=estat.xml

いちど抜いたら保存しておきましょう。

家計調査 家計収支編 二人以上の世帯」を引っこ抜く

Qita Rアドベント記事 政府統計ポータル(e-Stat)と R でサンタさんの12月の出費動向を調べてみた ではマウス操作でデータをCSVファイルとして抽出しています。 これをAPIでやってみましょう。

そのためには統計表 ID やリストIDが必要です。上で統計表ID一覧を取得済みですから、ここを検索します。

> library(XML)
> # 保存したファイルから読み込むなら  estat <- xmlParse("estat.xml")
> # ちなみにこの操作は、オブジェクトをコピーするのではなく、もとのデータへの参照になっています
> est1 <- getNodeSet(estat, "//GET_STATS_LIST//DATALIST_INF//STATISTICS_NAME")
>
> # リスト名を取り出します(これも時間がかかります)
> tmp <- xmlSApply(est1, function(x) xmlSApply(x, xmlValue))
>
> # tmpオブジェクトも保存しておくといいでしょう
> # save(tmp, file = "estatIdNAME.rda") ; load ("estatIdNAME.rda") # or # write.csv(tmp, file = "estatIdNAME.csv")
> # 検索して、該当するリストの添え字を取ります
> num <- grep("家計調査 家計収支編 二人以上の世帯", tmp)
> nodes <- getNodeSet(estat, "//GET_STATS_LIST//DATALIST_INF//LIST_INF")
> nodes[num]
[[1]]
<LIST_INF id="0002070001">
  <STAT_NAME code="00200561">家計調査</STAT_NAME>
  <GOV_ORG code="00200">総務省</GOV_ORG>
  <STATISTICS_NAME>家計調査 家計収支編 二人以上の世帯</STATISTICS_NAME>
  <TITLE no="001">用途分類 用途分類(総数)</TITLE>
  <CYCLE>月次</CYCLE>
  <SURVEY_DATE>0</SURVEY_DATE>
  <OPEN_DATE>2014-12-26</OPEN_DATE>
  <SMALL_AREA>0</SMALL_AREA>
</LIST_INF> 

[[2]]
<LIST_INF id="0002070002">
  <STAT_NAME code="00200561">家計調査</STAT_NAME>
  <GOV_ORG code="00200">総務省</GOV_ORG>
  <STATISTICS_NAME>家計調査 家計収支編 二人以上の世帯</STATISTICS_NAME>
  <TITLE no="001">用途分類 用途分類(総数)</TITLE>
  <CYCLE>四半期</CYCLE>
  <SURVEY_DATE>0</SURVEY_DATE>
  <OPEN_DATE>2014-11-14</OPEN_DATE>
  <SMALL_AREA>0</SMALL_AREA>
</LIST_INF> 

以下、あと少し続くけど略

まあ、さらに検索しても良いのですが、疲れたので、目視で以下を確認しました。

[[22]]
<LIST_INF id="0003015188">
  <STAT_NAME code="00200561">家計調査</STAT_NAME>
  <GOV_ORG code="00200">総務省</GOV_ORG>
  <STATISTICS_NAME>家計調査 家計収支編 二人以上の世帯</STATISTICS_NAME>
  <TITLE no="010">品目分類 品目分類(平成22年改定)(総数:金額)</TITLE>
  <CYCLE>四半期</CYCLE>
  <SURVEY_DATE>0</SURVEY_DATE>
  <OPEN_DATE>2014-11-14</OPEN_DATE>
  <SMALL_AREA>0</SMALL_AREA>
</LIST_INF>

これをすっこ抜きます。ここから再び json をい使いましょう(XML で取得する方法を後述します)。これまた時間かかります。

> url <- paste("http://api.e-stat.go.jp/rest/1.0/app/json/getStatsData?appId=",
+             yourAPPID,
+             "&statsDataId=0003015188",  sep = "")
> x <- getURI(url)

構造を確認します。

> x2 <- fromJSON(x)
> x2$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ[1:2]
    @id                @name
1   tab             表章項目
2 cat01 品目分類(22年改定)
3 cat02             世帯区分
4  area             地域区分
5  time     時間軸(四半期)

ゲーム関係のIDを探します。

> num <- grep("ゲーム|がん具",x2$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ$CLASS[[2]][,2])
> # IDを確認しましょう
> x2$GET_STATS_DATA$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ$CLASS[[2]][num,]
        @code                @name @level @unit
521 090203010       テレビゲーム機      3    円
522 090203150       ゲームソフト等      3    円
523 090203020           他のがん具      3    円
559 090442000 入場・観覧・ゲーム代      4    円
567 090442060   他の入場・ゲーム代      5    円
# > grep("ゲーム|がん具",x2[[1]][3]$STATISTICAL_DATA$CLASS_INF$CLASS_OBJ$CLASS, value = TRUE, perl = TRUE)
# [1] "list(`@code` = c(\"090203010\", \"090203150\", \"090203020\"), `@name` = c(\"テレビゲーム機\", \"ゲームソフト等\", \"他のがん具\"), `@level` = c(\"3\", \"3\, \"3\"), `@unit` = c(\"円\", \"円\, \"円\"))"

これらのIDをカテゴリ01 (cdCat01) に指定して、もう一回検索してみましょうか。

> url <- paste("http://api.e-stat.go.jp/rest/1.0/app/json/getStatsData?appId=",
+             yourAPPID,
+             "&statsDataId=0003015188",
+             "&cdCat01=090203010,090203150,090203020", sep = "")
> x <- getURI(url)
> x2 <- fromJSON(x)
> df <- x2$GET_STATS_DATA$STATISTICAL_DATA$DATA_INF$VALUE
> head(df)
    @tab    @cat01 @cat02 @area      @time @unit    $
1      01 090203010     03 00000 2014000709    円  191
2      01 090203010     03 00000 2014000406    円  144
3      01 090203010     03 00000 2014000103    円  206

多分、これで良いんでしょうか。

XML として抽出するなら

ちなみに XML を使う場合は、http://api.e-stat.go.jp/rest/1.0/app/json/getStatsData? から json の部分を削ります。

> url <- paste ("http://api.e-stat.go.jp/rest/1.0/app/getStatsData?appId=", yourAPPID, 
+              "&statsDataId=0003015188",
+              sep = "")
> library (XML)
> estatX <- xmlParse(url)
> estatTable <- getNodeSet(estatX, "//GET_STATS_DATA//STATISTICAL_DATA//CLASS_INF//CLASS_OBJ")
> # xmlRoot () xmlChildren () で辿っていってもいいです
> # estatTableの二つ目のリストは 品目分類 `cat01` ですが、何でしょうか?
> estatTable [[2]]
<CLASS_OBJ id="cat01" name="品目分類(22年改定)">
  <CLASS code="000100000" name="世帯数分布(抽出率調整)" level="1" unit="一万分比"/>
  <CLASS code="000200000" name="集計世帯数" level="1" unit="世帯"/>
  <CLASS code="000300000" name="世帯人員" level="1" unit="人"/>
  <CLASS code="000400000" name="18歳未満人員" level="2" unit="人"/>
  <CLASS code="000500000" name="65歳以上人員" level="2" unit="人"/>
  <CLASS code="000600000" name="65歳以上無職者人員" level="3" unit="人"/>
  <CLASS code="000700000" name="有業人員" level="1" unit="人"/>
  <CLASS code="000800000" name="世帯主の年齢" level="1" unit="歳"/>
  <CLASS code="000900000" name="持家率" level="1" unit="%"/>
  <CLASS code="001000000" name="家賃・地代を支払っている世帯の割合" level="1" unit="%"/>
  <CLASS code="001100000" name="消費支出" level="1" unit="円"/>
  <CLASS code="010000000" name="食料" level="1" unit="円"/>
  <CLASS code="010100000" name="穀類" level="2" unit="円"/>
  <CLASS code="010110001" name="米" level="3" unit="円"/>
         中略
> # 長いので簡略化します
> xmlAttrs(estatTable[[2]],"id")
                    id                   name 
               "cat01" "品目分類(22年改定)" 
> # "cat01" は品目なので、ゲームと玩具のIDを確認します
> estatClass <- getNodeSet(estatX, "//GET_STATS_DATA//STATISTICAL_DATA//CLASS_INF//CLASS_OBJ//CLASS")
> 
> tmp1 <- sapply(estatClass,xmlGetAttr, "name")
> 
> num <- grep("ゲーム|がん具", tmp1)
> 
> # 検索時にゲームなどの品目を利用したい時はこのIDを使います
> # tmp2 <- sapply(estatClass,xmlGetAttr, "code")
> (games <- data.frame(code = tmp2[num], name = tmp1 [num]))
       code                 name
1 090203010       テレビゲーム機
2 090203150       ゲームソフト等
3 090203020           他のがん具
4 090442000 入場・観覧・ゲーム代
5 090442060   他の入場・ゲーム代

> ## IDが確認できたので、これを使って検索しなおしてみましょう
> url2 <- paste ("http://api.e-stat.go.jp/rest/1.0/app/getStatsData?appId=", yourAPPID, 
+              "&statsDataId=0003015188&cdCat01=090203010,090203150,090203020",
+              sep = "")
>
> estatX2 <- xmlParse(url2)
> estatData2 <- getNodeSet(estatX2, "//GET_STATS_DATA//STATISTICAL_DATA//DATA_INF//VALUE")
> df <- do.call ("rbind",  lapply (estatData2, xmlAttrs))
> value <-  lapply(estatData2,xmlValue)
> df <- cbind(df,value)
> df <- as.data.frame(df)
> head(df)
  tab     cat01 cat02  area       time unit value
1  01 090203010    03 00000 2014000709   円   191
2  01 090203010    03 00000 2014000406   円   144
3  01 090203010    03 00000 2014000103   円   206
4  01 090203010    03 00000 2013001012   円   526
5  01 090203010    03 00000 2013000709   円   172
6  01 090203010    03 00000 2013000406   円   164

補足

Rでe-Statを利用する方法としてと Rで統計API というサイトがあります。以上の操作はベタなので、みなさん、このRで統計API で公開されている関数を利用しましょうね。

また、政府統計の総合窓口(e-Stat)ーAPI機能テストフォームなんかも使いましょう。