政府統計の総合窓口(e-Stat)のAPIを使ってみよう [R編]

# はじめに


Qiita [Rアドベント](http://qiita.com/advent-calendar/2014/r-rstudio) に[政府統計ポータル（e-Stat）と R でサンタさんの12月の出費動向を調べてみた](http://akiyoko.hatenablog.jp/entry/2014/12/25/025309)という良記事がありました。
とても参考になるのですが、ポータルからマウスで項目をクリックしていくのが大変です。特に最後の方のフェーズではページが強制的にフルスクリーンになってしまい、上記アドベントの操作を確認できなくなります。

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

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

## 公式説明PDFをパクる


恐れおおくも総務省統計局統計センター様謹製[PDF](http://www.soumu.go.jp/main_content/000320558.pdf) の最後の事例をパクります。

`RCurl` でjson形式のデータを引っ張ってきます。[次世代統計システム](http://statdb.nstac.go.jp/)から取得した自分のアカウントが必要です。以下のコードで `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)
```

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

```{r}
> 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 "家計調査"
  .. .. ..$ STATISTICS_NAME: chr "家計調査 家計収支編 二人以上の世帯"
  .. .. ..$ TITLE          :List of 2
  .. .. .. ..$ @no: chr "010"
  .. .. .. ..$ $  : 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 浜松市"
  .. .. .. .. .. ..$ @level: chr [1:2] "1" "1"
  .. .. .. .. ..$ :'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`だけ抜けば良いことが分かりました。
抜きます。

```{r}
> 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
```

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

## 「家計調査 家計収支編 二人以上の世帯」を抜く

Qiita Rアドベント記事に[政府統計ポータル（e-Stat）と R でサンタさんの12月の出費動向を調べてみた](http://akiyoko.hatenablog.jp/entry/2014/12/25/025309)ではマウス操作でデータをCSVファイルとして抽出しています。
これをAPIでやってみましょう。

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


```
> library(XML)
> # url <- paste("http://statdb.nstac.go.jp/rest/1.0/app/getStatsList?appId=", yourAPPID, sep = "")
> # estat <- xmlParse(url)
> # あるいは保存したファイルから  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 = "estatIdNames.rda")
> # 検索して、該当するリストの添え字を取ります
> 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])
> 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 = tmp1[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](http://nonki1974.hateblo.jp/entry/2013/06/14/221243) というサイトがあります。以上の操作はベタなので、みなさん、この[Rで統計API](http://nonki1974.hateblo.jp/entry/2013/06/14/221243) で公開されている関数を利用しましょうね。


また、[政府統計の総合窓口(e-Stat)ーＡＰＩ機能テストフォーム](http://www.e-stat.go.jp/api/sample/testform/)なんかも使いましょう。