会場の様子を後ろから写真に撮る、いつになったら実現出来るのかもう分からなくなってきた今日この頃。イマイチ雨の降りが悪い梅雨の最中、脳みそスパルタ教育にご参加いただきありがたや。
シェル芸勉強会へ遠隔参加する福岡サテライト会場も、今回で7回目を数えることになった。参加者の方々と会場のご提供いただいているベータソフト様と管理担当者、そしてもちろんUSP友の会運営陣と問題作成会長のおかげである。今回は午前中が別件の用事があったため、午後の部から参加することになった。
参加者はのべ9人だった。(途中出入有)初めての参加者は1名。福岡のペースで考え方や解説、余談を重視して進めるスタイルで。
イベント案内ページ
本家東京会場
大阪サテライト会場
5f01b3bc1d81c1fae2378cdc89.doorkeeper.jp
福岡サテライト会場
Twitterおまとめ
上田会長によるTogetterおまとめ。
開始前
福岡サテライト会場にも少しずつ顔なじみな方が増えてきた。ということでSoftware Design 6月号のbash特集記事と、シェルスクリプトマガジンvol.38を宣伝しておいた。
勉強会ライブ配信前のイントロで話したスライドはこちら。といっても時間が足りず7ページまでしか話せていないが。
開始
そしていよいよライブ配信開始。オープンデータについては・・・残念ながらPDFとか画像とかXLSですよねー。久留米市で人口データが公開されているが、HTMLのテーブルになっている分については、シェルスクリプトでスクレイピングしてCSVにする処理を以前ブログに書いたのでここで紹介しておく。
今回の問題は変態チックな問題ではなく、比較的正統派な問題が多かった。だが手練れが少ない福岡サテライト会場にはちょうどいい感じだったかもしれない。
問題と解答のページは、上田さんブログの記事を参照してもらいたい。
【問題と解答】第23回梅雨でモワッとしたシェル芸勉強会 – 上田ブログ
ではここから福岡サテライト会場で解説した内容を。完全に解けることよりも考え方や余談を重視するスタイル。
解答例はMacで作成。GNU版のコマンドはguniq
など頭にgが付くコマンドになっている場合がある。
Q1
年月をキーにしてデータを縦に並べる処理。とりあえずはawk一発芸で。会長の解答のように分割して処理した方が分かりやすいかもしれない。福岡ではawkに慣れていない人も多いので、-F
オプション、三項演算子、sprintf
、i
と$i
の違いなどについて解説した。
$ awk -F, 'NR>1{for(i=2;i<=13;i++){m=sprintf("%02d",i-1);print $1m,$i!=""?$i:"0"}}' landing.csv > monthly_typhoon
勉強会の時には話さなかったが、MacやFreeBSDなどのBSD系OSに付属のodコマンドだと、マルチバイトなテキストデータのダンプに対応しているので、BOM付きデータの確認とかが分かりやすいかも!?下記のコマンドで、最初の3バイトが通常のテキストデータではないことがわかる。
$ curl -s http://www.data.jma.go.jp/fcd/yoho/typhoon/statistics/landing/landing.csv | od -t x1c | head -n 2 0000000 ef bb bf e5 b9 b4 2c 31 e6 9c 88 2c 32 e6 9c 88 357 273 277 年 ** ** , 1 月 ** ** , 2 月 ** **
Q2
年毎台風上陸数の基本的な集計。もっと突っ込んだ解答は会長の解答を参照。福岡サテライトでは、基本的な集計方法について解説した。ポイントは6桁の年月から月を削って4桁の年にし、年をキーにして合計すれば良い。
具体的な処理方法としては、awkのsubstr()
関数と連想配列使うか?Tukubaiのself
とsm2
を使うか?どちらも常套句に近いので体で慣れていこう。
$ cat monthly_typhoon | awk '{print substr($1,1,4),$2}' | awk '{a[$1]+=$2}END{for(v in a){print v,a[v]}}' $ awk '{a[substr($1,1,4)]+=$2}END{for(v in a){print v,a[v]}}' monthly_typhoon $ cat monthly_typhoon | self 1.1.4 2 | sm2 1 1 2 2
上田会長の解答を参考に、後で作った解答がこちら。実行結果何も出力されないので整合性が取れている。
$ paste <(cat monthly_typhoon | self 1.1.4 2 | sm2 1 1 2 2) <(tail -n +2 landing.csv | awk -F, '{print $1,$NF==""?0:$NF}') | awk '$1!=$3 || $2!=$4'
Q3
各月で台風が上陸する確率。これは勘違いをしていた。(上陸した台風の数を合計した数を月の数で割っていた。台風の数ではなく台風が上陸した月の数を数えなくてはいけない。)帰宅後、下記の解答を考えたので載せておく。
まずは年月の月だけを切り出す。上陸数については1以上は全て1に変換。(0と1で上陸したかしてないかを表現する)
$ cat monthly_typhoon | awk '{print substr($1,5),$2>=1?1:0}' 01 0 02 0 03 0 04 0 05 0 06 0 07 1 08 0 09 0 10 1 ....
次に並べ替えてTukubaiのcountで各行の行数を集計する。2カラム目が0の行が上陸しなかった場合で、1が上陸した場合になる。3カラム目がそれぞれの回数になる。
$ cat monthly_typhoon | awk '{print substr($1,5),$2>=1?1:0}' | sort | count 1 2 01 0 65 02 0 65 03 0 65 04 0 64 04 1 1 05 0 63 05 1 2 06 0 56 06 1 9 07 0 39 ....
これをTukubaiのmap
コマンドで2次元展開する。1カラム目が月、2カラム目が上陸しなかった数、3カラム目が上陸した数になる。1行めのヘッダは今後不要。
$ cat monthly_typhoon | awk '{print substr($1,5),$2>=1?1:0}' | sort | count 1 2 | map num=1 * 0 1 01 65 0 02 65 0 03 65 0 04 64 1 05 63 2 06 56 9 07 39 26 08 24 41 09 24 41 10 52 13 11 64 1 12 65 0
あとはsed
で1行目のヘッダを消し、awk
で確率を計算すれば良い。
$ cat monthly_typhoon | awk '{print substr($1,5),$2>=1?1:0}' | sort | count 1 2 | map num=1 | sed 1d | awk '{print $1,$3/($2+$3)}' 01 0 02 0 03 0 04 0.0153846 05 0.0307692 06 0.138462 07 0.4 08 0.630769 09 0.630769 10 0.2 11 0.0153846 12 0
Q4
各年で台風が最初に上陸した月を抽出して、月毎に回数をカウントする。会長の解答とほぼ同じだ。GNU版のuniq
を使った場合(Macではguniq
)と、Tukubaiコマンドのgetfirst
を使った場合で比較してみよう。
$ cat monthly_typhoon | grep -v ' 0$' | awk '{print substr($1,1,4),substr($1,5)}' | guniq -w 5 | awk '{print $2}' | sort | uniq -c 1 04 2 05 9 06 21 07 19 08 7 09 2 10 $ cat monthly_typhoon | grep -v ' 0$' | self 1.1.4 1.5 | getfirst 1 1 | self 2 | sort | uniq -c 1 04 2 05 9 06 21 07 19 08 7 09 2 10
Q5
台風が上陸しなかった年を抽出。Tukubai使ってるだけで、これも会長とほぼ同じ解答。
$ cat monthly_typhoon | grep ' 0$' | self 1.1.4 | count 1 1 | awk '$2==12{print $1}' 1984 1986 2000 2008
しかし福岡サテライトで天才的な解答が。「使うファイルって指定されてないっすよね????」
$ cat landing.csv | grep ',,,,,,,,,,,,,' | sed 's/,,,,,,,,,,,,,//' ファイルの指定がなかったので笑 Q5 #シェル芸
— 香椎亘 (@kecy_) June 18, 2016
あああ元のCSVファイルでコンマ,
が13個連続して並んでれば、台風上陸してないよね確かに!
Q6
これは敢えて簡単な問題にしたということで、解答は会長のページを参考にされたし。
Q7
これもほぼ会長と同じ解答。福岡ではjoin
ではなくpaste
コマンドで横に並べてから処理する方法を話したが、あくまでもこれは全ての地区でひったくりが起きているという前提じゃないとダメ!。普通はちゃんとjoin
しようと注意喚起しておいた。
$ paste -d' ' <(cat hittakuri | sort -s -k1,1 | awk '{print $1}' | uniq -c | awk '{print $2,$1}') <(sort -k1,1 population_h27sep) 大阪市北区 53 大阪市北区 117384 大阪市旭区 8 大阪市旭区 91169 大阪市港区 6 大阪市港区 82391 大阪市西区 28 大阪市西区 90712 ....
Q8
2回以上起きている場合、と聞いた時点でuniq -d
がピコーン!と閃くようになりたい。そうすれば上田会長の解答に。閃かなかったのでuniq -c
でカウントしてawkで処理した。
$ cat hittakuri | awk '{print $1,$2,$3,$8$9$10}' | sort | uniq -c | awk '$1>=2{$1="";print }'
ここでuniq -d
とセットになるオプションuniq -u
についても補足で説明をしておいた。sort | uniq -c | sort -nr
など集計操作常套句についても改めておさらいをした。
Q9
会長の解答はawk
で未遂と未遂+既遂の2つの連想配列を使うことで処理している。福岡では手段毎にデータをどうやって横に並べるかを解説した。あとはawk
で計算するだけなので。
xargs
を使った例はイカサマ臭い。(手段毎で既遂と未遂のどちらかがゼロだとダメ)
$ self 7 5 hittakuri | sort | uniq -c | xargs -n 6 49 徒歩 既遂 3 徒歩 未遂 19 自動車 既遂 2 自動車 未遂 139 自転車 既遂 12 自転車 未遂 271 自動二輪 既遂 13 自動二輪 未遂
データを横に並べるのに、awk
の連想配列を使った場合と、Tukubaiのyarr
コマンドを使った場合で比較してみよう。
$ self 7 5 hittakuri | sort | uniq -c | awk '{a[$2]=a[$2]" "$1" "$3}END{for(v in a)print v,a[v]}' 徒歩 49 既遂 3 未遂 自動車 19 既遂 2 未遂 自転車 139 既遂 12 未遂 自動二輪 271 既遂 13 未遂 $ self 7 5 hittakuri | sort | uniq -c | self 2 3 1 | yarr num=1 徒歩 既遂 49 未遂 3 自動車 既遂 19 未遂 2 自転車 既遂 139 未遂 12 自動二輪 既遂 271 未遂 13
終了後
終了後はbash
のプロセス置換についても使い方を解説したりしたあと、TLネタを話した。BSD版date
コマンドとBSD環境でビルドされたmawk
の致命的バグ!?案件。
あとless
の話題も上がったので、下記のブログの紹介もした。「巨大なファイルをviで開いて死亡する」という事例は皆さん結構体験されているようで・・・-S
や-X
や-N
オプションは要チェックかと。
ということで今回も無事終了した。初めていらっしゃった方にも、何かしらのヒントが伝わっているといいなあと思う。東京、大阪、福岡、三ヶ所同時開催の勉強会、皆様お疲れ様!