e-Stat by R

The current release is OpenBSD 6.8, released October 18, 2020. This day marks the OpenBSD project's 25th anniversary.

configure

前回OpenBSDでRを作った時、/usr/ports/math/R/Makefile中に、突然(そのMakefie内では)未定義な変数が表れていて面喰らった。で、逃げてしまったんだけど、今回は正攻法で調べてみる。 まずは、configureが何をやってるか、dry-run してみる。

ob# make -n configure | tr ';' '\n'
lock=R-3.6.3
  export _LOCKS_HELD=" R-3.6.3"
   /usr/bin/perl /usr/ports/infrastructure/bin/portlock  /usr/ports/pobj/locks/$
lock.lock math/R,
 locked=true
  trap 'if $locked
 then  rm -f /usr/ports/pobj/locks/$lock.lock
 locked=false
 fi' 0
  trap 'exit 1' 1 2 3 13 15
 cd /usr/ports/math/R && PKGPATH=math/R make _internal-configure

これを見ると、ロック処理して2重実行を禁止してるのな。次は -p を付けてターゲットルールと変数をダンプしてみる。だらだら出てくるやつから、一度ログに落としてじっくり閲覧。

ob# make -p configure >LOG
ob# emacs LOG

目がチカチカするので、grep MODTK_ LOG して、眼をいたわる。

CONFIGURE_ARGS   = --disable-java  --disable-openmp  --enable-BLAS-shlib  --enable-R-shlib  --with-tcl-config=${MODTCL_CONFIG}  --with-tk-config=${MODTK_CONFIG} --prefix='${PREFIX}' --sysconfdir='${SYSCONFDIR}' --mandir='${PREFIX}/man' --infodir='${PREFIX}/info' --localstatedir='${LOCALSTATEDIR}' --disable-silent-rules --disable-gtk-doc
MODTCL_VERSION   = ${MODTK_VERSION}
MODTK_BIN        = ${LOCALBASE}/bin/wish${MODTK_VERSION}
MODTK_BUILD_DEPENDS = ${_MODTK_SPEC}:x11/tk/${MODTK_VERSION}  ${MODTCL_BUILD_DEPENDS}
MODTK_CONFIG     = ${MODTK_LIBDIR}/tkConfig.sh
MODTK_INCDIR     = ${LOCALBASE}/include/tk${MODTK_VERSION}
MODTK_LIB        = tk85
MODTK_LIBDIR     = ${MODTCL_TCLDIR}/tk${MODTK_VERSION}
MODTK_LIB_DEPENDS = ${_MODTK_SPEC}:x11/tk/${MODTK_VERSION}  ${MODTCL_LIB_DEPENDS}
MODTK_RUN_DEPENDS = ${_MODTK_SPEC}:x11/tk/${MODTK_VERSION}  ${MODTCL_RUN_DEPENDS}
MODTK_VERSION    = 8.5
MODTK_WANTLIB    = ${MODTK_LIB} ${MODTCL_WANTLIB}
 :

makeの変数代入は大きく分けて2種類あるとな。:= は、即実行。= の方は、必要になった時に実行ってやつだ。そんなの今回初めて知ったぞ。で、複雑な事をやって、最終的に決定されるとな。裏では、bsd.port.mkが鎮座してるとな。

ob# grep _internal-configure: LOG | tr ' ' '\n'
_internal-configure:
/usr/ports/pobj/R-3.6.3/.dep-STEM-ge-8,lt-9-lang-gcc-8,-f95
/usr/ports/pobj/R-3.6.3/.dep-archivers-gtar
/usr/ports/pobj/R-3.6.3/.dep-archivers-unzip
/usr/ports/pobj/R-3.6.3/.dep-archivers-zip
 :
/usr/ports/pobj/R-3.6.3/.dep-STEM-ge-8,lt-9-lang-gcc-8,-libs
/usr/ports/pobj/R-3.6.3/.buildwantlibs
/usr/ports/pobj/R-3.6.3/build-amd64/.configure_done
ob# make -dA configure >CONFLOG

そして、これが最強のログになる。見るのを拒否してるぐらい分量が有るぞ。grepで絞ろうにも、keywordを思い付かない。トホホ。まあ、このログが困った時の最後の砦って事で、今回のサーチは終了と言う事にしておく。

県別魅力度ランキング

年も終わりに近づくと、その年のまとめって事で、〇〇ランキングってのが、ぞろ出て来る。 そんな事で、 都道府県魅力度ランキング2020 なんてのが発表された。民間な機関がまとめたそうなんだけど、どこから資金が出ているのだろう? こういう事を常に考えてしまうオイラーが居る。要するにバイアスがかかっていないかって事だ。

上記によると、万年最下位なバラキが、とうとう順位を上げた。タレントに露出度アップ作戦が功を奏したようだ。オイラーはてっきり、天然自然なスィーツである、干し芋が高感度に寄与してたと思っていたけど、違ったみたい。是非Rを使って因子分析を行ってほしい所だ。

当然、最下位に沈んだ県がある訳で、栃木らしい。餃子だけじゃ食べて行けない訳ね。カクテルも光輝いてはいないようだし。まて、栃木には那須塩原も有るし日光だってあるぞ。 でも、日光結構、一度行けば満足。シンガポールのマーライオンみたいに、がっかりの筆頭かな。ああ、筆が滑ってしまった。栃木の県知事並びに県民の皆様に、伏してお詫び致します。

e-Stat

上の魅力度は私的機関の調査なので、今度は公的機関の資料に当たってみる。総務省統計局ね。 この局が電子統計を公開してる。 少し、資料集めしておく。

データを抽出してダウンロードできるe-Statの使い方

政府統計e-Stat APIを使うための事前知識

Rに特化した資料も集めておくかな。

政府統計e-Stat APIをプログラミング言語「R(アール)」で使ってみた

プログラミングによるビッグデータの分析(R) おお、総務書のICTスキル教材とやらが出てきたぞ。R言語は、政府公認のアプリに認定されてるって事でいいかな。

総務省 ICTスキル総合習得プログラム こちらが、上記の親ページか。なかなか楽しそう。

e-StatのAPI  奥村先生の所

ユーザー登録

e-Stat へ行く。右上の新規登録をクリックして、メアドを入力。暫くすると本登録の案内がメールで送られて来る。リンクをクリックして本登録。

IDはメアドを入れる。パスワードはご自由に。これで本登録終了。今度はログインボタンを押してログイン。パスワードを忘れた人は、IDを入力して、パスワード要求して下さいですって。 それって、とっても危険な事だよね。メアドを知ってる人は、簡単に乗っ取る事出来ちゃうよ。 それでいいのか >総務省。こういう苦言は、お問い合わせにメールすればいいのかな。

ああ、単にメアドを知ってたって、メアドのパスワードを知らないとだめか。ソーシャルハッキングとか、むにゃむにゃむにゃ。そんな人、どんだけーー居るの?

次はRからデータを引っ張り出す時に必要な、アプリケーションIDを要求する。マイページをクリック。API機能(アプリケーションID発行)をクリック。

3個のIDを取得出来る。けど、普通に使うなら一つあれば十分だろう。 名称は、「Rの勉強」、URLは、「https://localhost」 にした。これで、発行ボタンを押すと、40Byteの長ったらしいhex文字が出てきた。 後は、これを使うだけ。

なお、URLは、httpを指定しちゃうと、強度不足って怒られて、アプリケーションIDの発行を拒否されるんで注意。いつの間にか、httpsがデフォになったんですねぇ。

e-Stat by R

いよいよRからe-Statを叩く。懇切丁寧に使い方を解説されてる、 Rからe-Stat APIを使う をトレースする事にします。

library(httr)

response <- GET(
  url = "https://api.e-stat.go.jp/rest/2.1/app/json/getStatsList",
  query = list(
    appId = '40Byte-appID' ,
    searchWord = "犯罪統計 AND 刑法犯"
  )
)

response

url + query な所にアクセスして、そのレスポンスを取得するんだな。検索ワードは AND で 絞り込みとな。

library(listviewer)
res_content <- content(response)
jsonedit(res_content)

コンテンツはbody部になろんだな。してその結果はjsonとな。jsonのビューアーは、firefox上に展開された。ブラウザーが得意とする分野だ。

ちょいと寄り道

サーバーからのレスポンスを眺めてみる。

> str(response)
List of 10
 $ url        : chr "https://api.e-stat.go.jp/rest/2.1/app/json/getStatsList?appId=40hex....&searchWord=%E7....%8A%AF"
 $ status_code: int 200
 $ headers    :List of 12
  ..$ server                     : chr "nginx"
  ..$ date                       : chr "Sun, 18 Oct 2020 22:25:07 GMT"
  ..$ content-type               : chr "application/json; charset=utf-8"
    :

headerにnginxなんてのが見えるな。政府もOSSを採用してんのかな。バックエンドのDBはMySQLだったりして。

リクエストにしっかりとappIDが埋め込まれていて、どこの誰が使ったか、確認出来るとな。 この方法、超昔にやった事があるぞ。懐かしい方法だわい。昔やったのはCGIですよ。最初はperlだったけどrubyに鞍替えしたな。確か20世紀の最後の頃だったような。

裸のresponseを見ると

$times
     redirect    namelookup       connect   pretransfer starttransfer
     0.000000      0.117418      0.138843      0.193385     29.406071
        total
    29.406309
$content
   [1] 7b 22 47 45 54 5f 53 54 41 54 53 5f 4c 49 53 54 22 3a 7b 22 52 45 53 55
    :
[4057] 34 45 31 22 7d 7d 5d 7d 7d 7d

こんな応答時間まで取得してた。それから生ボディーも見られた。 次は、生ボディーをRに解釈してもらう。

> str(res_content)
List of 1
 $ GET_STATS_LIST:List of 3
  ..$ RESULT      :List of 3
  .. ..$ STATUS   : int 0
  ..$ PARAMETER   :List of 3
  .. ..$ LANG       : chr "J"
  .. ..$ SEARCH_WORD: chr "犯罪統計 AND 刑法犯"
  .. ..$ DATA_FORMAT: chr "J"
  .. ..$ TABLE_INF :List of 4
  .. .. ..$ :List of 15
  .. .. .. ..$ @id                 : chr "0003191320"
  .. .. .. ..$ STAT_NAME           :List of 2
  .. .. .. .. ..$ @code: chr "00130001"
  .. .. .. .. ..$ $    : chr "犯罪統計"
  .. .. .. ..$ GOV_ORG             :List of 2
  .. .. .. .. ..$ @code: chr "00130"
  .. .. .. .. ..$ $    : chr "警察庁"
  .. .. .. ..$ STATISTICS_NAME     : chr "犯罪統計"
  .. .. .. ..$ TITLE               : chr "第1表 刑法犯 罪種別 認知・検挙件数・検挙人員"
  .. .. .. ..$ CYCLE               : chr "年次"
  .. .. .. ..$ SURVEY_DATE         : chr "201601-201612"
  .. .. .. ..$ OPEN_DATE           : chr "2017-12-11"
  .. .. .. ..$ SMALL_AREA          : int 0
  .. .. .. ..$ MAIN_CATEGORY       :List of 2
  .. .. .. .. ..$ @code: chr "14"
  .. .. .. .. ..$ $    : chr "司法・安全・環境"
  .. .. .. ..$ SUB_CATEGORY        :List of 2
  .. .. .. .. ..$ @code: chr "02"
  .. .. .. .. ..$ $    : chr "犯罪"
  .. .. .. ..$ OVERALL_TOTAL_NUMBER: int 2310
  .. .. .. ..$ UPDATED_DATE        : chr "2017-12-11"
  .. .. .. ..$ STATISTICS_NAME_SPEC:List of 1
  .. .. .. .. ..$ TABULATION_CATEGORY: chr "犯罪統計"
   :

本当の生データは

$GET_STATS_LIST
$GET_STATS_LIST$RESULT
$GET_STATS_LIST$RESULT$STATUS
[1] 0

$GET_STATS_LIST$RESULT$ERROR_MSG
[1] "正常に終了しました。"

$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]
$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$`@id`
[1] "0003195002"

$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$STAT_NAME
$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$STAT_NAME$`@code`
[1] "00130001"

$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$STAT_NAME$`$`
[1] "犯罪統計"


$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$GOV_ORG
$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$GOV_ORG$`@code`
[1] "00130"

$GET_STATS_LIST$DATALIST_INF$TABLE_INF[[4]]$GOV_ORG$`$`
[1] "警察庁"

こんな具合になっていた。こうしてみると裸のデータでも、何とかなりそうな雰囲気だな。

rlist

指南書では、かっこよく rlist ってパッケージで、綺麗にデータを見せてくれている。 Rでリストを扱うrlistパッケージのチュートリアル こんな素晴らしい案内も公開されている。

さすが、listって名前が付いているぐらいだから、lispの主要関数をそっくりRに移植しましたって趣で、オイラーは大好きですよ、こういうの。

ああ、オイラーの所でこのrlistパッケージを入れようとしたら、基底にXMLが必要って言われてエラーになった。エラー内容から、システムに欠けている、何とかパッケージを入れて、事無きを得た。ほんとにリナ系のパッケージングには腹が立つ。たかがヘッダーぐらい、けちけちしないで、ライブラリィー本体と一緒に入れろよ。disk容量の節約なんてのは、理由にならんからね。プンプン。

リナをdisるには、オイラーが常日頃愛用してる、OpenBSDではどうか確認しておかねばならない。そうしないと、何処かの国の野党みたいに、反対するのが使命ってなるからね。取り合えず、問題になったrlistだけを入れてみた。

> install.packages('rlist')
--- Please select a CRAN mirror for use in this session ---
also installing the dependencies 'yaml', 'jsonlite', 'XML', 'data.table'
   :
 * installing *source* package 'XML' ...
checking for pkg-config... /usr/bin/pkg-config
checking for xml2-config... /usr/local/bin/xml2-config
USE_XML2 = yes
SED_EXTENDED_ARG: -E
Minor 9, Patch 10 for 2.9.10
Located parser file -I/usr/local/include/libxml2 -I/usr/local/include/parser.h
Checking for 1.8:  -I/usr/local/include/libxml2 -I/usr/local/include
Using libxml2.*

リナの時は、確かこの辺りで引っかかったはず。OpenBSDでは、難なくクリアしたぞ。使ってて、超気持ちイイィー(って、往年の北島風の雄たけび)

この際だから、リナの apt-cache 相当を調べておく。

ob$ locate xml2-config
/usr/local/bin/xml2-config
/usr/local/lib/cmake/libxml2/libxml2-config.cmake
/usr/local/man/man1/xml2-config.1
ob$ pkg_info -E /usr/local/bin/xml2-config
/usr/local/bin/xml2-config: libxml-2.9.10p0
libxml-2.9.10p0     XML parsing library

そして、そんなの入れた記憶が無いんで、それを必要とする(身に覚えがあるであろう)パッケージを炙り出してみる。

ob$ pkg_info -R libxml
Information for inst:libxml-2.9.10p0

Required by:
emacs-26.3p1-no_x11
libglade2-2.6.4p17v0
librsvg-2.48.4
py-gtk2-2.24.0p7
qemu-4.2.0p0
shared-mime-info-1.15
tshark-3.2.7

ははは、オイラーの大好物が出て来た。emacsでもXMLをハンドリング出来るのか。あれは太っちょの括弧もどきだから、スマートなカッコ・コッカと親類ではあるからね。

ob$ pkg_info -Q libxml
debug-libxml++-2.40.1p2
debug-libxml++3-3.2.0
hs-libxml-sax-0.7.5p8
libxml++-2.40.1p2
libxml++3-3.2.0
libxml-2.9.10p0 (installed)
libxmlbird-1.2.0p0
p5-Libxml-0.08p2
p5-XML-LibXML-2.0204v0
p5-XML-RSS-LibXML-0.3105
py-libxml-2.9.10p0

似たような名前も検索できるとな。残念ながら上記の -Q を除いて検索対象はインストール済みのportsだけを対象にしてる。apt-file 相当は、自分で汗を流してpkg/PLISTあたりを検索するしかないだろう。上手いコマンドを知っていたら、教えてください。

> install.packages('estatapi')
--- Please select a CRAN mirror for use in this session ---
also installing the dependencies 'sys', 'askpass', 'assertthat', 'digest', 'utf8
', 'curl', 'mime', 'openssl', 'R6', 'cli', 'clipr', 'crayon', 'hms', 'lifecycle'
, 'BH', 'cpp11', 'ellipsis', 'generics', 'glue', 'tidyselect', 'vctrs', 'fansi',
 'pillar', 'pkgconfig', 'httr', 'purrr', 'readr', 'dplyr', 'tibble', 'rlang'

余勢を駆ってestatapiも入れてみようとした。なんだか色々な物の盛り合わせセットになってる。11.3Mのサイズが有るBHをダウンロードし始めたので、インストールを中止した。折角公開してくれた皆さんの努力を踏みにじるようで、申し訳ない。またいつか。

ああ、またいつかの理由は、e-StatのAPIを見てたら、新しい版の3.0が提供されてた。この版では、CSVが取れるらしい。なら、現状の環境でもすんなり行くのではないかと思ったのさ。

色々なライブラリィーを入れれば、それは便利だろうけど、脆弱性に繋がるからね。何たってオイラーはTeoさんの弟子ですから。まあ、茨の道が待ってるかも知れないけど。

> library(rlist)
> library(pipeR)
> res_content$GET_STATS_LIST$DATALIST_INF$TABLE_INF %>>%
+   list.mapv(`@id`)
[1] "0003191320" "0003191340" "0003194968" "0003195002"

@idで評価すると、idを取り出せるとな。

> res_content$GET_STATS_LIST$DATALIST_INF$TABLE_INF %>>%
+   list.select(
+     id = `@id`,
+     table_name = TITLE_SPEC$TABLE_NAME
+   ) %>>%
+   list.stack()
          id
1 0003191320
2 0003191340
3 0003194968
4 0003195002
                                                            table_name
1                     第1表 刑法犯 罪種別 認知・検挙件数・検挙人員
2         第1表 刑法犯 罪種別 認知・検挙件数・検挙人員 対前年比較
3 第3表 刑法犯総数  都道府県別 認知・検挙件数・検挙人員 対前年比較
4             第3表 刑法犯総数 都道府県別 認知・検挙件数・検挙人員

名前も取り出しに追加しておいて、list.stackで、フレームにも出来るとな。

get data

APIで公的統計データを取得 なんて言うのが載ってた、統計局の勉強資料に、ちょいと鞍替えする。

例で、全国消費実態調査/ 平成26年全国消費実態調査/ のデータをDLしようとしたんだけど、 該当データは無いと言われた。

e-Statへ行って、統計データを探す。データ種別は、ファイルとデータベースが有るけど、勿論データベースからの検索。

検索ワードは、政府統計(初期状態)とデータセットが有るんで、データセットを選び、平成26年全国消費実態調査 総世帯 購入地域 で、検索。19件出て来た。

エラーした時の案内に、フロー編第151表 のが載ってたので、それを頼りに、APIのクエリィーを出してみた。

 "\"RESULT\"\n\"STATUS\",\"1\"\n\"ERROR_MSG\",
\"正常に終了しましたが、該当データはありませんでした。\"\n\"DATE\",\"2020-10-18T15:33:13.957+09:00\"\n\
"RESULT_INF\"\n\"TOTAL_NUMBER\",\"0\"\n
\"TABLE_INF\",\"0003130397\"\n\"STAT_NAME\",\"00200564\",\"全国消費実態調査\" ...

http://api.e-stat.go.jp/rest/2.1/app/getStatsData?appId=&lang=J&statsDataId=0003130397&metaGetFlg=Y&cntGetFlg=N&sectionHeaderFlg=1

このURLを見ると、statsDataIdが、スクリプト中で指定されてる。けど、補助のidは無い。きっと補助のidが間違ってて、エラーになってるんだろうね。取り合えずそれを削除した。

そんな時は、 estatapiパッケージで簡単にe-Statのデータ取得 を参考に、 統計分野 を見れば良いのかな。なんか、図書の10進分類みたいな番号が割り当てられている。すると、個別のデータを示すIdってのは、ISBN番号になるのか。慣れれば便利なんだろうね。図書館の司書に相当する、データ・ソムリエみたいになれるのかな?

library("httr")
library("listviewer")
library("rlist")
library("pipeR")
library("stringr")
library("dplyr")

# 全国消費実態調査/ 平成26年全国消費実態調査/
# 全国品目及び購入先・購入地域に関する結果総世帯に関する結果

response2 <- GET(
    url = "https://api.e-stat.go.jp/rest/3.0/app/getSimpleStatsData",
    query = list(
        appId = '40byte-appId' ,
        statsDataId = "0003130397" )   )

con2 <- content(response2)
result <- read.csv(text = sub('"VALUE"\n', "", con2), row.names = c() )

write.csv(result, "/tmp/zzz.csv", row.names=FALSE)

read.csvの所でエラーになったので、row.namesを何も無しに設定した。仕様が変わったの?

> response2$times
     redirect    namelookup       connect   pretransfer starttransfer
     0.000000      0.039533      0.060795      0.126765      4.090438
        total
     4.435121

とんでもなく時間がかかる時が有るけど、混雑してるのかな?

sakae@pen:/tmp$ wc zzz.csv
  513836   549811 12501388 zzz.csv
sakae@pen:/tmp$ nkf -g zzz.csv
UTF-8

出来上がったcsvファイル。

"2014000000","2014年"
"円","56"
"003","金額"
"02610","紙おむつ(大人用)"
"00020","現金"
"07005","総世帯"
"00620","70歳以上"
"00000","全国"

何となく、2014年の全国70歳以上の総世帯が現金で紙おむつ代を56円払いましたって風に読むのかな。

DBで拾い出したものをEXCEL風の表形式でDL出来るんでやってみた。

sakae@pen:/tmp$ wc FEH_00200564_201019061408.csv
   155    242 393220 FEH_00200564_201019061408.csv
sakae@pen:/tmp$ nkf -g FEH_00200564_201019061408.csv
Shift_JIS

nkf -w してutfに変換して見ると

"統計名:","平成26年全国消費実態調査 全国 品目及び購入先・購入地域に関する結果
総世帯"
"表番号:","151"
"表題:","[総世帯]フロー編第151表 年間収入階級・世帯主の年齢階級,購入形態,
品目別1世帯当たり1か月間の支出"
"実施年月:","2014年","-"
"市区町村時点(年月日):","-"

"***","数字が得られないもの"
"-","該当数字がない"
 :
"テレビゲーム機【円】","ゲームソフト等【円】","他の玩
具【円】","切り花【円】","音楽・映像用未使用メディア【円】","音楽・映像収録済メ
ディア【円】","ペットフード【円】","他の愛玩動物・同用品【円】","園芸用植物【円
】","園芸用品【円】","電池【円】"
sakae@pen:/tmp$ grep テレビゲーム機 aa.csv | tr ',' '\n' | wc
    459     471   13857

随分と細かい家計簿(相当)だなあ。特定なデータを抜き出す方法は、 政府統計の総合窓口(e-Stat)のAPIを使ってみよう {R編} が参考になる。


This year's Index

Home