はじめに
こちらの記事の続き。
元々前回で終わる予定だったのだけれども、前回までのやり方では、
元データ(https://jag-japan.com/covid19map-readme)から退院数や死亡者数などが正しく集計できそうにない。
そこで、退院数や死亡者数を集計結果を出力する専用オプションを追加する。
作るもの
前回は4まで。
- データベースの作成・更新と全行全項目の出力
- 全感染者の指定項目を取得 (列の抽出,並べ替え)
- 指定項目の条件でフィルタ (行の抽出)
- 指定項目の内訳集計 (出現回数のカウント)
- 確定日別の集計 (集計用のDB追加する) <== New
以降では、前回同様作るスクリプト名をcovidとした前提で説明する。
実行イメージ
$ covid -t 確定日,感染者数累計,感染者数前日比,死者合計,退院数累計,退院数前日比,PCR検査実施人数,PCR検査実施人数前日比 2020/01/15,1,1,,1,1,, 2020/01/24,2,1,,,,, 2020/01/25,3,1,,,,, ... ... 2020/05/14,16175,49,687,10338,470,233144,5700 2020/05/15,16184,9,,,,,
-t
オプション未指定時と同様、条件指定や列指定もできる。
$ covid -t -m 'date~"2020/05"' date inf die leav pcr 確定日,感染者数累計,死者合計,退院数累計,PCR検査実施人数 2020/05/01,14740,432,3981,174510 2020/05/02,15044,458,4211,181527 2020/05/03,15228,492,4385,183251 ... 2020/05/13,16126,668,9868,223667 2020/05/14,16175,687,10338,233144 2020/05/15,16184,,,
列項目について
今回の-t
オプション指定時は、指定できる列項目が下表のパラメータになる。
項目 | パラメータ | 備考 |
---|---|---|
確定日 | date | PCR検査の陽性確定日 |
感染者数累計 | inf | 感染者数の累計 |
感染者数前日比 | infd | 感染者数の前日比 |
死者合計 | die | 死亡者の累計 |
退院数累計 | leav | 退院者数の累計 |
退院数前日比 | leavd | 退院者数の前日比 |
PCR検査実施人数 | pcr | PCR検査実施人数の累計 |
PCR検査実施人数前日比 | pcrd | PCR検査実施人数の累計。パット見で数が合ってない箇所も。。 |
確定日以外の出力は、空欄か数字(人数)のみ。
空欄は元データが空欄のもの。
公表待ちの他にもありそうで、理由がよく分からないので、本スクリプトでも空欄のまま出力する。
コード
処理的には前回までの一連の処理と同じ。
ただDBファイルや列項目名が変わっただけ。
コード全体はこちらを参照されたし。
DBファイル作成
集計用のDBファイルのパスは以下。
DB_DIR="${XDG_DATA_HOME:-${HOME}/.local/share/}/${CMD_NAME}" # 〜中略〜 TOTAL_DB_FILE="${DB_DIR}/TOTAL_DB_COVID-19.csv"
元データのCSVファイルから取得する列位置と出力ヘッダ名は以下。
TOTAL_DB_ITEMS='$40,$24,$25,$27,$28,$29,$30,$31' #TOTAL_DB_HEADER='date,inf,infd,die,leav,leavd,pcr,pcrd' TOTAL_DB_HEADER='確定日,感染者数累計,感染者数前日比,死者合計,退院数累計,退院数前日比,PCR検査実施人数,PCR検査実施人数前日比'
集計用のDBファイルが無かったら作成する。
# make database for total count if [ ! -f "$TOTAL_DB_FILE" ]; then echo "$TOTAL_DB_HEADER" > "${TOTAL_DB_FILE}" cat "$RAW_DB_FILE" \ | make_total_database >> "${TOTAL_DB_FILE}" fi
実際に作成している関数は以下。
感染者数累計が空欄か#REF!
の行は省いている。
また、確定日の最終出現行のみ出力させる為、一旦降順でソートして、最後に元に戻している。
make_total_database(){ get_body_recode \ | delete_space \ | format_date \ | sort -t ',' -k 1 -nr \ | awk -F"," \ ' BEGIN{ OFS="," } $24!="" && $24!="#REF!" && !uniq[$40]++ { print '"$TOTAL_DB_ITEMS"' }' \ | sort -t ',' -k 1 }
!uniq[$40]++
で、日付の重複を避けている。
awk
ではゼロはFALSEとなる。
論理反転しているのでゼロのときはTRUE、ゼロ以外がFALSEとなる。
つまり2020/5/15が複数回出てきた場合、最初に出てくる2020/5/15の行のときだけ条件が成立する。
そして、awk
に渡す前に降順でソートしているので、元データのCSVファイル上で、
最後に出てきた2020/5/15の行にだけマッチするという想定。
ただ、報告漏れなどがあって後からデータが追加される場合、
元データのCSVがどう修正されていくか分からない。。。
全行上からチェックして空文字じゃなければ上書き更新とした方が安全かもしれない。
その場合、列数分だけ配列が必要になりそう。
すぐできそうなので気になる人はやってみてねと。
列名チェック
-t
オプション指定時の列項目名チェック。
怠いコードをそのまま増産。
if [ -n "$TOTAL_COUNT" ]; then for field in "$@";do case $field in date | inf | infd | die | leav | leavd | pcr | pcrd ) ;; * ) ERR_MSG="invalid field-name '$field'" ;; esac done else
列項目名から列位置への置き換え
-t
オプション指定時は、参照するDBファイルと項目名チェックに使う関数を切り替える。
if [ -n "$TOTAL_COUNT" ]; then NAME_TO_POS=total_name_to_pos DB_FILE="$TOTAL_DB_FILE" fi
チェックに使う関数も怠いコードをそのまま増産。
total_name_to_pos(){ sed -e 's/date/$1/g' \ -e 's/infd/$3/g' \ -e 's/inf/$2/g' \ -e 's/die/$4/g' \ -e 's/leavd/$6/g' \ -e 's/leav/$5/g' \ -e 's/pcrd/$8/g' \ -e 's/pcr/$7/g' }
ただ、置き換える順番には注意。
infd
より先にinf
で置き換えるとinfd
が$2d
とかになっちゃう。
出力
出力するコードは今までと一緒。
条件を絞る処理なんかも今までと共通。
PICKUP_COND=`echo $PICKUP_COND | $NAME_TO_POS \ | sed 's/\([^!~><=]\)=\([^=]\)/\1==\2/g'` if [ -n "$CNT_IN_FIELD" ]; then CNT_IN_FIELD=`echo $CNT_IN_FIELD | $NAME_TO_POS` # output count each of field cat "$DB_FILE" \ | sed '1d' \ | awk -F"," ' BEGIN{ OFS="," } '"$PICKUP_COND"'{ count['$CNT_IN_FIELD']++ } END{ total=0 for (field in count) { print field,count[field] total+=count[field] } print ":TOTAL",total }' elif [ -n "$CNT_OF_RECODE" ]; then # output count of pickup recodes cat "$DB_FILE" \ | sed '1d' \ | awk -F"," ' BEGIN{ count=0 } '"$PICKUP_COND"'{ count++ } END{ print count }' else # output pickup headr head -n 1 "$DB_FILE" \ | awk -F"," ' BEGIN{ OFS="," } { print '"${OUTPUT_ITEMS}"' }' # output pickup recodes cat "$DB_FILE" \ | sed '1d' \ | awk -F"," ' BEGIN{ OFS="," } '"$PICKUP_COND"'{ print '"${OUTPUT_ITEMS}"' }' fi
さいごに
今回のスクリプトはawk
さまさまです。
表計算も楽々ですな。やらんけど。
ただ、初回の記事にも書いたけど、僕のようにシェルスクリプトを書くことが目的じゃなければ、
他のサイトさんのデータを使った方が楽だしデータの更新頻度や精度も良いかも。
ちなみに、かの有名なmattnさんなんて下のようなものを作ってらっしゃる。
.@nikkei_Linux さんの corona-stats を Vim から見れる様にしました。https://t.co/jOBepWerJm pic.twitter.com/bbu4xYiQNb
— mattn (@mattn_jp) 2020年3月30日
まるで息を吐くかの如く VimScript や Goプログラム を量産されるお方ですな。スゴイ。
ということで、これでおしまい。
もうやらなーい。
確認環境
PC | Thinkpad X1 Carbon 2nd Gen |
OS | FreeBSD 12.1-RELEASE-p4 |