e-Stat by R (2)

speed up

前回やった apt-file もどきの検索、下記のようにちょいと時間がかかるんだ。 めったに使う事が無いんで、これでもいいんだけど、ちょっと速く検索出来ないか。

vbox$ time  find . -name 'PLIST*' | xargs grep -l xml2-config
./textproc/libxml/pkg/PLIST-main
    0m20.59s real     0m00.72s user     0m09.59s system
vbox$ time  find . -name 'PLIST*' | xargs grep -l xml2-config
./textproc/libxml/pkg/PLIST-main
    0m18.18s real     0m00.64s user     0m10.09s system

予備調査しとく

vbox$ find . -name 'PLIST*' | xargs cat >> /tmp/LOG
vbox$ egrep '(bin|include|lib)' LOG >SMALL
vbox$ wc LOG SMALL
 2613272 2832231 148878528 LOG
  786981  830571 51636431 SMALL
 3400253 3662802 200514959 total
vbox$ time grep xml2-config SMALL
bin/xml2-config
lib/cmake/libxml2/libxml2-config.cmake
    0m00.30s real     0m00.20s user     0m00.10s system

パッキングリスト(の内容)を全て集めて、LOGにする。多分必要も無いやつが混じっているはずだから、必要な行だけを抽出する。1/3ぐらいになった。後はこれをgrepするだけで良いはず。

ちゃんとした検索用フィイルを apt-file ってジョークな名前で作る。

vbox$ find . -name 'PLIST*' | xargs egrep '(bin|lib|include)' >> /tmp/apt-file
vbox$ time grep xml2-config apt-file
./textproc/libxml/pkg/PLIST-main:bin/xml2-config
./textproc/libxml/pkg/PLIST-main:lib/cmake/libxml2/libxml2-config.cmake
    0m01.53s real     0m00.38s user     0m00.91s system

初回はキャッシュに載っていないから、1.5秒かかったけど、2回目からは0.4秒。十分に速い。

get data from e-Stat

e-Stat からデータを取ってくるやつ、1回休んでしまった。して、臆面もなく家計簿もどきのデータにチャレンジした。横にビローンと長いやつ。食費がある。細分化して、米とかパンとか色々。車関係でも、所有率が有ったり、保険があったりカオスな様相を呈している。しまいには、こずかい(使途不明金)なんてのがあったりして、笑わせてもらった。

pre check

今回は心を入れ替えて、 Rからe-Stat APIを使う で扱っているサツの統計をなぞってみる。何はなくともlibreofficeで表を偵察ですよ。 横軸は、時間軸のコード、それの表示データ。要するにコードは暗号に通じる。いわゆるkeyだ。それに対応するvalueが有る。

以下、罪種(刑法犯総数、凶悪犯、凶悪犯ー{殺人、強盗..}、粗暴犯、、)ってな具合。続いて、認知件数、検挙件数、検挙率、検挙人員、人員のうちの少年って具合だ。これらは、列名にコードと名称がある。 この構造を、まず知っておいたほうが良い。そうしないと、折角の解説も意味が不明になってしまう。 EXCEL式はテキストファイルとして見るには不都合なんで、垂れ流し式にファイルを落とした。

"cat01_code","認知・検挙件数・検挙人員","cat02_code","罪種","time_code","時間軸(年次)","unit","value"
"100","認知件数","100","刑法犯総数","2016000000","2016年","件","996120"
"100","認知件数","100","刑法犯総数","2015000000","2015年","件","1098969"
"100","認知件数","100","刑法犯総数","2014000000","2014年","件","1212163"
"100","認知件数","100","刑法犯総数","2013000000","2013年","件","1314140"
"100","認知件数","100","刑法犯総数","2012000000","2012年","件","1403167"
"100","認知件数","100","刑法犯総数","2011000000","2011年","件","1502951"
"100","認知件数","100","刑法犯総数","2010000000","2010年","件","1604019"
"100","認知件数","100","刑法犯総数","2009000000","2009年","件","1713832"
"100","認知件数","100","刑法犯総数","2008000000","2008年","件","1826500"
"100","認知件数","100","刑法犯総数","2007000000","2007年","件","1908836"
"100","認知件数","100","刑法犯総数","2006000000","2006年","件","2050850"
"100","認知件数","110","凶悪犯","2016000000","2016年","件","5130"
"100","認知件数","110","凶悪犯","2015000000","2015年","件","5618"
  :

これが、超まとめになる。2006年から2016年までのサツが知ってる、刑法犯総数って事だな。 犯罪の種別を洗い出してみる。

sakae@pen:~/Downloads$ cut -d',' -f3,4 flow.csv | uniq
"cat02_code","罪種"
"100","刑法犯総数"
"110","凶悪犯"
"120","凶悪犯_殺人"
"130","凶悪犯_強盗"
"140","凶悪犯_放火"
"150","凶悪犯_強姦"
"160","粗暴犯"
"170","粗暴犯_凶器準備集合"
"180","粗暴犯_暴行"
"190","粗暴犯_傷害"
"200","粗暴犯_傷害_傷害致死"
"210","粗暴犯_脅迫"
"220","粗暴犯_恐喝"
"230","窃盗犯"
 :
"380","知能犯_背任"
"390","風俗犯"
"400","風俗犯_賭博"
"410","風俗犯_わいせつ"
"420","風俗犯_わいせつ_強制わいせつ"
"430","風俗犯_わいせつ_公然わいせつ"
"440","その他の刑法犯"
"450","その他の刑法犯_占有離脱物横領"
"460","その他の刑法犯_公務執行妨害"

賭博って風俗犯に分類されるのか、知りませんでしたよ。公然ってやつは、珍々を見せびらかしたりするやつだな。

sakae@pen:~/Downloads$ cut -d',' -f1,2 flow.csv | uniq
"cat01_code","認知・検挙件数・検挙人員"
"100","認知件数"
"110","検挙件数"
"120","検挙率"
"130","検挙人員"
"150","検挙人員_うち少年"

実際のデータは、こんな感じ。

"120","検挙率","280","知能犯_詐欺","2016000000","2016年","%","45.3"
"120","検挙率","280","知能犯_詐欺","2015000000","2015年","%","44.7"
"120","検挙率","280","知能犯_詐欺","2014000000","2014年","%","41.3"
"120","検挙率","280","知能犯_詐欺","2013000000","2013年","%","48.4"
"120","検挙率","280","知能犯_詐欺","2012000000","2012年","%","58.3"

知能犯の詐欺って、オレオレ詐欺も含まれるのかな。年を追う毎に検挙率が低下してる。だから、あの手この手で、騙されないようにしましょうと躍起になってるのだな。年金日に、だまされないお守りを配ったり、幼稚園児に、おじいちゃんおばあちゃんへの手紙を書いてもらったり、高校の書道部に、垂れ幕を作ってもらったり。。。

まあ、こういうデータが手に入れば、わざわざRを使わなくても、パイプで抽出してgnuplotに流し込んで、グラフにするなんて容易に出来るぞ。テキスト文化は偉大だ。

"120","検挙率","120","凶悪犯_殺人","2016000000","2016年","%","100.7"
"120","検挙率","120","凶悪犯_殺人","2015000000","2015年","%","100.5"
"120","検挙率","170","粗暴犯_凶器準備集合","2012000000","2012年","%","116.7"
"120","検挙率","170","粗暴犯_凶器準備集合","2010000000","2010年","%","133.3"
"120","検挙率","360","知能犯_汚職_賄賂","2008000000","2008年","%","105.4"

100%を超える検挙率って、泥縄式逮捕で、お手柄って事かな。面白いね。こういうのを発見するって。

じゃ、Rが出る幕無いじゃん。ほんとだね。

do exec

そんな事言わないで、ちゃんと実習してみる。 みんな入りのデータを取るやつ。

library(httr)
myid = '40byte here'

res <- GET(
  url = "https://api.e-stat.go.jp/rest/2.1/app/getSimpleStatsData",
  query = list(
    appId = myid ,
    statsDataId = "0003191320"
  )
)

素直に取って来るんだな。

> con <- content(res)
> gv <- read.csv(text = sub('(?s).*"VALUE"\n', "", con, perl=T))

ちょいと加工する。すると

> head(gv)
  cat01_code 認知.検挙件数.検挙人員 cat02_code       罪種  time_code
1        100               認知件数        100 刑法犯総数 2016000000
2        100               認知件数        100 刑法犯総数 2015000000
3        100               認知件数        100 刑法犯総数 2014000000
4        100               認知件数        100 刑法犯総数 2013000000
5        100               認知件数        100 刑法犯総数 2012000000
6        100               認知件数        100 刑法犯総数 2011000000
  時間軸.年次. unit   value
1       2016年   件  996120
2       2015年   件 1098969
3       2014年   件 1212163
4       2013年   件 1314140
5       2012年   件 1403167
6       2011年   件 1502951
> tail(gv)
     cat01_code 認知.検挙件数.検挙人員 cat02_code                      罪種
2305        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
2306        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
2307        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
2308        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
2309        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
2310        150      検挙人員_うち少年        510 その他の刑法犯_器物損壊等
      time_code 時間軸.年次. unit value
2305 2011000000       2011年   人  1342
2306 2010000000       2010年   人  1407
2307 2009000000       2009年   人  1588
2308 2008000000       2008年   人  1734
2309 2007000000       2007年   人  1657
2310 2006000000       2006年   人  1598

全行数が2310行あるデータになった。どうしても横に長くなるんで、2つに分解して表示されてるけど、論理的には横に繋がっている(最左に表示されてる数字は、行番号だ)。

cat01_code   cat02_code   time_code   value

日本語の説明が大仰に幅を利かせていて、構造が見えにくいけど、database的に書くと、valueを説明する為のコードが3つあるよって事だ。データベース理論の草分けコッド先生に感謝、だな。

time_code が10桁もある。無駄? いいえ、ちゃんと考えているのさ。yyyyMMDDhh を満たせるように考えているのさ。要するに、1時間毎のデータもやろうと思えば出来ますって事。 でも、そんな細かい事を言うより、最低でも 2019年までのデータを公開せんかい。さぼるなよ。

人気が無いので、予算を削られましたが真実だろうね。いや、前代の親分と言い、つい最近交代した親分と言い、データは隠せ主義が蔓延してますからね。

これだけ分かると、 xxx_code で、絞り込んで検索したくなるはずだ(既に pre checkでやってしまったけどね)。codeを引っ張り出すには、カテゴリコードとメタ情報取得APIを使うそうだ。

library(httr)
library(dplyr)
myid = '40byte here'

GET(
  url = "https://api.e-stat.go.jp/rest/2.1/app/json/getMetaInfo",
  query = list(
    appId = myid ,
    statsDataId = "0003191320"
  )
) %>% content -> meta
> str(meta)
  .. .. ..$ 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"

政府の一組織、警察庁が、1年かけて調査しました。それを翌年の暮に公開しましたって、機動力なさすぎ。予算が無いので、誰かが手弁当でまとめましたって気がするな。あ、EXCELファイルだと最新のものも公開されてる。と言う事は、EXCELからDBに入れ込むのは、統計局の仕事になるのかな。そんなの、スクリプト一発だろうに。

.. .. .. .. ..$ @id  : chr "cat02"
.. .. .. .. ..$ @name: chr "罪種"
.. .. .. .. ..$ CLASS:List of 42
.. .. .. .. .. ..$ :List of 3
.. .. .. .. .. .. ..$ @code : chr "100"
.. .. .. .. .. .. ..$ @name : chr "刑法犯総数"
.. .. .. .. .. .. ..$ @level: chr "1"
.. .. .. .. .. ..$ :List of 4
.. .. .. .. .. .. ..$ @code      : chr "110"
.. .. .. .. .. .. ..$ @name      : chr "凶悪犯"
.. .. .. .. .. .. ..$ @level     : chr "2"
.. .. .. .. .. .. ..$ @parentCode: chr "100"
.. .. .. .. .. ..$ :List of 4
.. .. .. .. .. .. ..$ @code      : chr "120"
.. .. .. .. .. .. ..$ @name      : chr "凶悪犯_殺人"
.. .. .. .. .. .. ..$ @level     : chr "3"
.. .. .. .. .. .. ..$ @parentCode: chr "110"
.. .. .. .. .. ..$ :List of 4
.. .. .. .. .. .. ..$ @code      : chr "130"
.. .. .. .. .. .. ..$ @name      : chr "凶悪犯_強盗"
.. .. .. .. .. .. ..$ @level     : chr "3"
.. .. .. .. .. .. ..$ @parentCode: chr "110"

cat02(罪種)の説明。レベル分けされてる。最上位が1で刑法犯総数。その下にレベル2の凶悪犯がある。更にレベル3として、殺人とか強盗とかに細分化されてる。

それぞれにコードとが付与されてるし、低いレベルの物は、親を示すコードが付いている。後は、これらのレベルを頼りにデータを要約するもよし、深い所を探求するもよしって事だ。

リベンジ

前回やった家計簿で、余計なidを付けるとエラーになった件、もう少し調べてみる。 メタ情報に何か出てないか。データを取ってみた。str(meta)すると綺麗に表示してくれるんだけど、それはR上での話。結果を外に持ち出したい。色々やったら

> cat(meta, file="HOGE")
Error in cat(meta, file = "HOGE") :
  argument 1 (type 'list') cannot be handled by 'cat'
> cat(as.character(meta), file="HOGE")

文字列に変換して取り出せた。

list(RESULT = list(STATUS = 0, ERROR_MSG = "正常に終了しました。", DATE = "2020-
10-24T07:05:54.652+09:00"), PARAMETER = list(LANG = "J", STATS_DATA_ID = "000313
0397", DATA_FORMAT = "J"), METADATA_INF = list(TABLE_INF = list(`@id` = "0003130
397", STAT_NAME = list(`@code` = "00200564", `$` = "全国消費実態調査"), GOV_ORG
= list(`@code` = "00200", `$` = "総務省"), STATISTICS_NAME = "平成26年全国消費実
態調査 全国 品目及び購入先・購入地域に関する結果 総世帯",

見るに堪えないリストになった。少し整理

sakae@pen:/tmp$ cat HOGE | tr ',' '\n' | less
list(RESULT = list(STATUS = 0
 ERROR_MSG = "正常に終了しました。"
 DATE = "2020-10-24T07:05:54.652+09:00")
 PARAMETER = list(LANG = "J"
 STATS_DATA_ID = "0003130397"
 DATA_FORMAT = "J")
 METADATA_INF = list(TABLE_INF = list(`@id` = "0003130397"
 STAT_NAME = list(`@code` = "00200564"
 `$` = "全国消費実態調査")
 GOV_ORG = list(`@code` = "00200"
 `$` = "総務省")
  :
    list(`@id` = "cat01"
 `@name` = "品目分類表(二人以上・総世帯)_2014"
 CLASS = list(list(`@code` = "00010"
 `@name` = "集計世帯数"
 `@level` = "1")
 list(`@code` = "00020"
 `@name` = "世帯数分布(抽出率調整)"
 `@level` = "1")
  :
 list(`@code` = "00200"
 `@name` = "食料"
 `@level` = "2"
 `@unit` = "円")
 list(`@code` = "00210"
 `@name` = "穀類"
 `@level` = "3"
 `@unit` = "円")
 list(`@code` = "00220"
 `@name` = "米"
 `@level` = "4"
 `@unit` = "円")

でも、目当てなコードは出てこなかった。 もう、諦めて、 家計調査 家計収支編 二人以上の世帯 こういうのとか、 収録統計表一覧 を見る方が早いか。

sakae@pen:/tmp$ gosh
gosh> (define obj (read (open-input-file "FUGA")))
obj
gosh> (car obj)
("@code" "100" "@name" "刑法犯総数" "@level" "1")
gosh> (cadr obj)
("@code" "110" "@name" "凶悪犯" "@level" "2" "@parentCode" "100")
gosh> (caddr obj)
("@code" "120" "@name" "凶悪犯_殺人" "@level" "3" "@parentCode" "110")
gosh> (length obj)
42

括弧で括られたリストって言われると、ついつい、こういう事をやりたくなるオイラーですよ。 Listとか、=とか,を取り除きS式にしてあげた。そしたら、簡単にschemeで読み込めたぞ。

gosh> (for-each (^x (print x)) obj)
(@code 100 @name 刑法犯総数 @level 1)
(@code 110 @name 凶悪犯 @level 2 @parentCode 100)
(@code 120 @name 凶悪犯_殺人 @level 3 @parentCode 110)
(@code 130 @name 凶悪犯_強盗 @level 3 @parentCode 110)
(@code 140 @name 凶悪犯_放火 @level 3 @parentCode 110)
(@code 150 @name 凶悪犯_強姦 @level 3 @parentCode 110)
(@code 160 @name 粗暴犯 @level 2 @parentCode 100)
(@code 170 @name 粗暴犯_凶器準備集合 @level 3 @parentCode 160)
(@code 180 @name 粗暴犯_暴行 @level 3 @parentCode 160)

こんなのも思い出したぞ。

それはそうと、codeが100番から10刻みで増えている。これって超昔にBASICのプログラミングをやる時に付けた、行番号みたいだな。指南書には、行番号を10刻みで指定しろ。その理由は、後で行と行の間に、新しい命令を挿入出来るから。

BUG取りが終わったら、確かrenumberとかの命令で、綺麗な刻みで行番号を追加した(ような覚えがある)。 DBのコード番号を付与する時も、同じような考えをしたんだろうね。

コード番号は通しナンバーにしてる。そして別にレベルってのを付けてる。このレベルって無駄じゃなかろうか?

レベル2は2000番代、レベル3は3000番代ってコードを決める方法があるはず。そしたらDBの列を一つ減らせるぞ(と、けち臭い考えが頭を過る)。

まあ、こういうのは素人の考えだな。統計の分散とかを計算する時、個々のデータを平均から引いて、それを2乗してる。 そんな2乗なんて面倒するより、個々のデータと平均の差の絶対値の方が計算負荷が少ないと思ったものだ。じつは、この方がプログラミングする上で手間がかかる。

ふと、そんな事を思い出したのさ。

by curl or wget

余りRばかりに頼っていては、あれなんで、普通のコマンドラインからやってみる。目当てなDBに辿り付いたら、APIってボタンを押して、データをDLする為の雛型を出してクリップボードにコピーする。何故雛型かと言うとappIDが空欄になっているからだ。

雛型が長ったらしいので、一度editor画面にでも張り付けてから、URLを完成させよう。

curl -o po.xml 'https://api.e-stat.go.jp/......&sectionHeaderFlg=1'

上記はcurlを使ってURLからDLし、結果をpo.xmlにセーブしろって指令。curlの所をwgetに変更して、直接URLを指定してもよい。出力先ファイルを指定しないと、wget-log ってファイルに結果が残るのかな。

一番注意しなければならないのはURLをシングルクォーテーションで囲んでおく事だ。何故なら、URLの一部に & が使われているため、shellは、バックグランドで実行してねって意味にとっちゃうから。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GET_STATS_DATA xsi:noNamespaceSchemaLocation="https://api.e-stat.go.jp/rest/2.1
/schema/GetStatsData.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <RESULT>
        <STATUS>101</STATUS>
        <ERROR_MSG>統計表ID(statsDataId)または データセットID(dataSetId)のどらか一方を指定して下さい。</ERROR_MSG>
          :

こんな結果しか返ってこず、悩む事になる(何を隠そう、オイラーも悩んだ口だ)。

それから、ひな型では、httpを出してくるけど、それ恐いと思おう。心ある人は https を使うように。大事な事だからね。

  :
<CLASS_INF>
    <CLASS_OBJ id="cat01" name="認知・検挙件数・検挙人員">
        <CLASS code="100" name="認知件数" level="1" unit="件"/>
        <CLASS code="110" name="検挙件数" level="1" unit="件"/>
        <CLASS code="120" name="検挙率" level="1" unit="%"/>
        <CLASS code="130" name="検挙人員" level="1" unit="人"/>
        <CLASS code="150" name="検挙人員_うち少年" level="2" unit="人" parentCode="130"/>
    </CLASS_OBJ>
    <CLASS_OBJ id="cat02" name="罪種">
        <CLASS code="100" name="刑法犯総数" level="1"/>
        <CLASS code="110" name="凶悪犯" level="2" parentCode="100"/>
        <CLASS code="120" name="凶悪犯_殺人" level="3" parentCode="110"/>
        <CLASS code="130" name="凶悪犯_強盗" level="3" parentCode="110"/>
        <CLASS code="140" name="凶悪犯_放火" level="3" parentCode="110"/>
        <CLASS code="150" name="凶悪犯_強姦" level="3" parentCode="110"/>
        <CLASS code="160" name="粗暴犯" level="2" parentCode="100"/>
   :
</CLASS_INF>
<DATA_INF>
    <NOTE char="-">-</NOTE>
    <VALUE cat01="100" cat02="100" time="2016000000" unit="件">996120</VALUE>
    <VALUE cat01="100" cat02="100" time="2015000000" unit="件">1098969</VALUE>
   :

結果は、コードの説明とかが冒頭の方にあって、その後ろにデータが続いている。太っちょの括弧さえ気にしなければ、ちゃんと完結したデータになってる。json形式よりはオイラーに取って見易い、かな。

xmllint

xmlなんてのが出てきたので、軽くxmlのアプリは無いものかと調べてみた。勿論emacsは、きちんとxmlサフィックスを認識して、編集出来るようになるけどね。

ob$ xmllint --shell po.xml
/ > cd GET_STATS_DATA
GET_STATS_DATA > ls
ta-        5
---        7 RESULT
ta-        5
---       15 PARAMETER
ta-        5
---        9 STATISTICAL_DATA
ta-        1
GET_STATS_DATA > cd RESULT
RESULT > ls
tan        9
---        1 STATUS
tan        9
---        1 ERROR_MSG
tan        9
---        1 DATE
ta-        5

太っちょS式用の、構造editor かな?


This year's Index

Home