#導入
画像とグラフを重ね合わせる機会はそれなりにある(特に古い文献を扱う場合).ここでは,画像とグラフをgnuplotでどう重ねるかについて,コロナ禍のなか話題になった,あるショッキングなグラフを題材にして述べる.
そのショッキングなグラフとは,東京五輪開催に伴う新規陽性者シミュレーションと実際を比較したこのグラフである:
シミュレーションのグラフは,実際には新聞記事の画像であり,その上に実際の新規陽性者数のグラフ(水色実線)が重なっている.今回は,これをgnuplotで再現する.
#やったこと
- 東京都新規陽性者の時系列データを作成する (bash)
- 元画像の原点と縮尺を決定する (gnuplot)
- 元画像とグラフを重ね合わせる (gnuplot)
#東京都新規陽性者の時系列データ作成
単にgnuplotの練習になるだけだと思ったが,日にちごとの新規陽性者数というドンピシャなデータが手に入らない.どうやら直接そのような形で提供されているものはないようで(単に調べ方が悪いという説も…),詳細データをシェルスクリプト (bash) でいろいろと下処理することになった.詳細データは東京都オープンデータカタログサイトから取得した.
この詳細データは,陽性者一人一人の情報が書き込まれているもので,
- 5列目(公表年月日)のみを抜き出す
- 日付でgrep
- wcで列数を数える
という方針で日ごとの新規陽性者を数え上げた(エクセルでもいいと思う).
シェルスクリプトは以下の通り(最後に中間ファイルの削除処理を行うので注意):
#!/bin/bash
rawfile=130001_tokyo_covid19_patients.csv
daily=daily.txt
wget https://stopcovid19.metro.tokyo.lg.jp/data/130001_tokyo_covid19_patients.csv
#日ごとの新規陽性者を数える関数
new_infec () {
for day in $(seq -w ${2} 1 ${3}); do
pat=$(cat ${daily} | grep "2021-${1}-${day}" | wc -l)
if test ${pat} != "0"; then
echo ${pat} >> patient_data.txt
fi
done
}
cut -d , -f 5 ${rawfile} > ${daily} #5列目だけ取り出し
new_infec 06 20 30 #06/20~06/30の陽性者を数える
new_infec 07 01 31 #07/01~07/31の陽性者を数える
new_infec 08 01 31 #08/01~08/31の陽性者を数える
nl -v 0 patient_data.txt > data.txt #06/20からの経過日数を追加
rm -f patient_data.txt ${rawfile} ${daily} #中間ファイルのお掃除
今回は,例の図に合わせて,06/20からの2か月強を対象に数え上げている.未来の日付を数え上げると新規陽性者0となるので,条件分岐で0のデータを出力しないようにした.単純に数えるだけが目的ならば,2019年~2020年初頭に実際に新規陽性者が0だったときのデータを弾いてしまうので,この分岐は有害になる可能性があることを追記しておく.
data-proc.shを実行して出力されるファイルがこんな感じ(1列目は06/20を起点にした経過日数,2列目は東京都の新規陽性者数.):
0 376
1 236
2 435
3 619
4 570
5 562
6 534
7 386
8 317
9 476
10 714
……
#元画像の原点の位置調べ
陽性者予測の画像は
から拝借した.この画像ファイルをyosoku.jpg
とする.画像利用にあたっては,
を参考にした.本記事はこの項目のうち「引用」に該当すると判断している1.
まず,この画像をgnuplotで表示してみよう.画像が配置されているディレクトリでgnuplotを起動し,以下を入力する.
plot "yosoku.jpg" binary filetype=auto with rgbimage
うまくいけば,画像の左下端がグラフ上の原点 (0, 0) に,1ピクセルの大きさがグラフ上で幅1となるようにプロットされているはずである(画像は東京新聞WEB 2021年6月11日を改変):
まず,このグラフ画像の原点(人数0・6月20日)の座標を調べたい.当該座標のx成分をorig_x
,y成分をorig_y
としよう.画像の原点に相当するピクセルの座標値を調べて代入し,目論んだ位置と重なるかを確かめる.プロットウィンドウは自由に拡大/縮小できるのでそれも活用する.
orig_x=72.0
orig_y=45.0
set parametric
replot orig_x,orig_y with point
x座標値が0.5ほど右にずれていると判断したので再調整.
orig_x=71.5
replot
こんなところだろうか.凝りだすとキリがないのでこの程度にしておく.
#元画像へプロットするための縮尺調べ
次に縮尺を調べる.「縮尺」とだけ言うとわかりづらいが,要は横軸の1日の幅・縦軸の1人がグラフ上でどの程度の幅になるかを調べたいということだ.この作業によって,新規陽性者の実際値をどうスケール変換すればうまくグラフと重ねることができるかがわかる.
今回は,格子が重なっていて都合がいい点である (70日 (=8月29日), 1000人) の座標値を調べることにする.この点のグラフ画像の原点に対する相対位置ベクトルを(align_x, align_y)
とおこう.これは,おおまかにはピクセル座標の差を調べればよい.調べたら,プロットして位置を確認する.
align_x=417.0
align_y=243.0
set autoscale xy
replot align_x+orig_x,align_y+orig_y with point
(画像は東京新聞WEB 2021年6月11日を改変)
x座標値が右に1,y座標値が下に1.5ずれていると判断したので微調整.
align_x=416.0
align_y=244.5
replot
(画像は東京新聞WEB 2021年6月11日を改変)
いい感じ(自画自賛).
このalign_x
とalign_y
を用いれば,データ(d_x, d_y)
が次の座標値(x, y)
にプロットされるべきであることがわかる:
x=orig_x+(align_x/70.0)*d_x
y=orig_y+(align_y/1000.0)*d_y
#元画像とグラフの重ね合わせ
以下のようなgnuplotバッチファイルを作った.
reset #精神安定剤
set terminal pngcairo size 960,1280 #縦長画像
set output "comp.png"
set size ratio -1 #アスペクト比保存
set autoscale xy
#枠なし・軸なし・凡例なし
unset border
unset tics
unset key
orig_x=71.5
orig_y=45.0
align_x=416.0 #70日間の幅
align_y=244.5 #1000人の幅
set label 1 at screen 0.1,0.95 "画像は東京新聞WEB 2021年6月11日\nまとりょーしか加筆 (緑色グラフ)\nデータ元: 東京都 新型コロナ陽性患者発表詳細"
#画像出典・データ提供元の表示
plot "yosoku.jpg" binary filetype=auto with rgbimage,\
"data.txt" using (orig_x+(align_x/70.0)*($1)):(orig_y+(align_y/1000.0)*($2)) with lines linewidth 6 linecolor rgb "forest-green"
set terminal pop #精神安定剤
次のような画像が出力される:
(画像:東京新聞WEB 2021年6月11日を改変,データ元:東京都 新型コロナウイルス陽性患者発表詳細)
みなさん感染対策は万全に.体調にはお気をつけて.
また,新型コロナウイルスに感染された方々の,一日も早い回復をお祈りします.
#参考
- きっかけとなった図 https://twitter.com/toshiyatakeuchi/status/1421378542370054146
- 東京都 新型コロナウイルス陽性患者発表詳細 https://catalog.data.metro.tokyo.lg.jp/dataset/t000010d0000000068/resource/c2d997db-1450-43fa-8037-ebb11ec28d4c
- 東京新聞WEB(2021年6月11日)https://www.tokyo-np.co.jp/article/110157
- 東京新聞WEB 著作権に関するQ&A https://www.tokyo-np.co.jp/info/copyrights/qa
-
正確には,陽性者数シミュレーションと実際の陽性者数の比較をテーマにしたうえで,その題材として引用するものである. ↩