15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rからe-Stat APIを使う(一括取得API)

Last updated at Posted at 2019-02-27

前回(Rからe-Stat APIを使う - Qiita)使用しなかった一括取得APIを使ってみたいと思います。

ターゲットになる統計表を決めて統計表IDを取得する

まずは統計表IDを取得します。

今回のターゲットは毎月勤労統計調査です。選択について特に深い理由はありません。強いて言えば似たような連続するデータのテーブルが適度に分かれているからです。似たような連続するデータなのに何故テーブルを分けるのか…?謎ですね。理由はわかりません。いや、分かると言えば分かるんですがひとまず置いておきましょう。e-Statのデータを扱う上では、この「何故なのか…?」という気持ちを抑えていくことも大切です。この世界は想像もできないような謎に満ちているんです。

で、一口に毎月勤労統計調査と言ってもいくつかのデータがありますが、その中の産業大中分類別常用労働者1人平均月間現金給与額、というのを取得してみることにします。

library(httr)
library(listviewer)
library(rlist)
library(pipeR)
library(dplyr)
res1 <- GET(
  url = "https://api.e-stat.go.jp/rest/2.1/app/json/getStatsList",
  query = list(
    appId = keyring::key_get("e-stat"),
    searchWord = "産業大中分類別常用労働者1人平均月間現金給与額 AND 月次"
  )
)

統計表情報を取得してみると、このデータセットはテーブルが3つに分かれていることが分かります。

          id         year
1 0003138350 2004~2008年
2 0003138395 2001~2003年
3 0003138396     2009年~

試しに一つ取得してみましょう。

GET(
  url = "https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData",
  query = list(
    appId = keyring::key_get("e-stat"),
    statsDataId = table_info$id[1],
    sectionHeaderFlg = 2
  )
) %>%
  content %>%
  stringr::str_replace('"VALUE"\n', '') %>% 
  readr::read_csv() -> df1
df1 %>% head
# A tibble: 6 x 12
  tab_code 表章項目 cat01_code `産業分類(200203改定)… cat02_code 事業所規模
     <dbl> <chr>    <chr>      <chr>            <chr>      <chr>     
1     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
2     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
3     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
4     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
5     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
6     3040 現金給与総額… TL         TL_調査産業計    T          5人以上   
# … with 6 more variables: area_code <chr>, 全国 <chr>, time_code <dbl>,
#   年月 <chr>, unit <chr>, value <dbl>

はい。不穏ですね。なんで列名に改訂履歴が書いてあるんでしょうか。

列名を操作する方法を覚えておく

実は、この「列名にちょっと情報書いちゃう」というのはe-Statのデータでは割とよく見かけるやつです。このテーブルでは1列だけですが、色々な列に色々なことが書いてある場合もあります。

したがって、e-Statに立ち向かう上では列名を一括操作することに慣れておく必要があります。特に、同じようなデータが複数のテーブルに分かれている場合、「列名なんてどれも同じだろう」なんて期待は決してしてはならないことです。

列名の操作はdplyrrename_allを使うと良いでしょう。例えば括弧と括弧の中身を取り除きたければ次のようにします。

df1 %>% rename_all(~stringr::str_replace(., "\\(.*\\)", "")) %>% colnames
 [1] "tab_code"   "表章項目"   "cat01_code" "産業分類"   "cat02_code"
 [6] "事業所規模" "area_code"  "全国"       "time_code"  "年月"      
[11] "unit"       "value"     

一括データ取得

さて気を取り直して一括取得APIをつかってやっていきましょう。果たして一括取得APIは便利なんでしょうか。

一括取得APIはPOSTで叩きます。そしてパラメータは3つあります。メタ情報の出力を制御するmetaGetFlg、セクションヘッダの出力を制御するsectionHeaderFlg、そして統計表取得のためのパラメータを一括指定するstatsDatasSpecです。statsDataSpecには、個別の統計表を取得するためのパラメータをJSON形式でまとめた文字列を渡します。

なので、まずはパラメータをJSON形式にまとめる必要があります。rlistパッケージでリストとしてパラメータを整理してからjsonliteパッケージのtoJSON()関数でJSONにすると良いと思います。ちなみにauto_unboxしておかないとエラーが出るので注意しましょう。

sds <- res_content$GET_STATS_LIST$DATALIST_INF$TABLE_INF %>>%
  list.select(statsDataId = `@id`) %>>% 
  jsonlite::toJSON(auto_unbox = TRUE)

cat(sds)
[{"statsDataId":"0003138350"},{"statsDataId":"0003138395"},{"statsDataId":"0003138396"}]

あとは、POST()でAPIを叩くだけです。注意点として、content_typeに"application/x-www-form-urlencoded"を指定する必要があります。これはAPIの仕様を読んでいてもわかりませんでした。

POST(
  url = "https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsDatas",
  query = list(
    appId = keyring::key_get("e-stat"),
    sectionHeaderFlg = 2,
    statsDatasSpec = sds
  ),
  content_type("application/x-www-form-urlencoded")
) -> res
res
Response [https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsDatas?...]
  Date: 2019-02-27 11:20
  Status: 200
  Content-Type: text/plain; charset=utf-8
  Size: 5.95 MB
"VALUE"
"requestNo","id","tab_code","表章項目","cat01_code","産業分類(200203改定)","cat02_...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
"1","0003138350","3040","現金給与総額","TL","TL_調査産業計","T","5人以上","00000","全国"...
...

はい。無事に取得できました。レスポンスはいつものcsvのような何かです。しかもこれが縦にそのままくっついてます。セクションヘッダフラグの出力を抑制しておかないと辛さが増大するので、忘れずOFFにしておきましょう。

データを読み込む

この異常なテキストも"VALUE"\nで切断してしまえば普通のcsvになるので後は読み込むだけなんですが、読み込んだ結果を結合する前に列名と順序の確認をするのを忘れないようにしておきましょう。「列名が変わっていた」は普通によくある話で(実際今回の例も変わってますし)、「列の順序が入れ替わっている」とか「列の数が変わってる」なんてことも稀によくあるのがe-Statです。生き延びたかったら決して気を許してはいけません。

res %>% 
  content %>% 
  stringr::str_split('"VALUE"\n') %>% 
  unlist %>% 
  `[`(-1) %>% 
  purrr::map(readr::read_csv) %>% 
  purrr::map(names)
[[1]]
 [1] "requestNo"            "id"                   "tab_code"            
 [4] "表章項目"             "cat01_code"           "産業分類(200203改定)"
 [7] "cat02_code"           "事業所規模"           "area_code"           
[10] "全国"                 "time_code"            "年月"                
[13] "unit"                 "value"               

[[2]]
 [1] "requestNo"            "id"                   "tab_code"            
 [4] "表章項目"             "cat01_code"           "産業分類(199310改定)"
 [7] "cat02_code"           "事業所規模"           "area_code"           
[10] "全国"                 "time_code"            "年月"                
[13] "unit"                 "value"               

[[3]]
 [1] "requestNo"            "id"                   "tab_code"            
 [4] "表章項目"             "cat01_code"           "産業分類(200711改定)"
 [7] "cat02_code"           "事業所規模"           "area_code"           
[10] "全国"                 "time_code"            "年月"                
[13] "unit"                 "value"               

今回は幸いなことにどのデータも同じ構造になっているようです。なので、列名の整理だけ行ったらそのまま結合してしまいます。欠損値の表現は-なので注意しましょう。

read_estat <- function(csv, ...){
  readr::read_csv(csv, ...) %>% 
    rename_all(~stringr::str_replace(., "\\(.*\\)", ""))
}
res %>% 
  content %>% 
  stringr::str_split('"VALUE"\n') %>% 
  unlist %>% 
  `[`(-1) %>% 
  purrr::map_df(read_estat, na = "-") -> df
df
# A tibble: 36,792 x 14
   requestNo id    tab_code 表章項目 cat01_code 産業分類 cat02_code
       <dbl> <chr>    <dbl> <chr>    <chr>      <chr>    <chr>     
 1         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 2         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 3         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 4         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 5         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 6         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 7         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 8         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
 9         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
10         1 0003…     3040 現金給与総額… TL         TL_調査産業… T         
# … with 36,782 more rows, and 7 more variables: 事業所規模 <chr>,
#   area_code <chr>, 全国 <chr>, time_code <dbl>, 年月 <chr>, unit <chr>,
#   value <dbl>

無事に読み込めました。で、「せっかくだから産業分類別の推移でも見てみよっかな〜〜〜」と思って産業分類コードを確認してしまうと本当の地獄に遭遇するわけですが、地獄は見たくないのでやりません。

毎月のお給料総額を適当にプロットしてお茶を濁して終わりたいと思います(適当にプロットするだけでも若干めんどい…)。

library(ggplot2)
df %>% 
  filter(
    tab_code == "3040",
    cat01_code == "TL"
  ) %>% 
  mutate(
    ym = lubridate::parse_date_time(年月, "Ym") %>% as.Date()
  ) %>%
  ggplot(aes(x = ym, y = value, color = 事業所規模)) +
  labs(x = "年月", y = "現金給与総額(円)") +
  scale_y_continuous(labels = scales::comma) +
  geom_line() +
  theme_bw(base_family = "IPAexGothic")

image.png

結局一括取得APIは便利なのか

クエリを一旦JSONに整形しないといけなかったり、謎csvがくっついたテキストをバラして読み込む必要があったりして、正直Rで使う分には別に便利ではないと思いました。私はもう使わないと思います。

15
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?