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/......§ionHeaderFlg=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 かな?