ほぼ老人のプログラミング日記

定年後の平凡なサラリーマンの趣味の日記

「北九州市 新型コロナウイルス感染症 陽性患者数」日毎集計プログラム

私が住んでいる北九州市では、市のホームページで市内の新型コロナウイルスの感染状況などが発信されています。

www.city.kitakyushu.lg.jp

やはり、新型コロナウィルスの感染状況など気になるものですから、時々は見ていました。そんな中、上のページから「市内の最新感染動向」というページにたどり着き、そこから感染状況のデータがオープンデータとしてダウンロードできることを知りました。

stopcovid19-kitakyushu.jp

GROWI へのデータ移行も終わり、せっかく Python に取り組み始めたので、また何か作ってみたいと思っていたこともあり、「これは良いデータを見つけた」と思い、このデータからカレンダー形式で日毎の感染者数を表示する HTML ファイルを出力するプログラムを作ってみることにしました。
先に完成形のスクリーンショットを載せておきます。

f:id:tiger62shin:20220307170106p:plain

このように日毎の感染者数を集計して出力しています。ピンク色のセルは前週の同一曜日より感染者数が増えているところ、黄色のセルは減っているところです。
感染者数がいない (ゼロ) の日は白色にしています。
なんとなく、感染者数が減ってきているのがわかる気がします (2022/3/7 福岡県で実施中の「まん延防止等重点措置」が解除されました)

いきなり、スクリーンショットを載せましたが、実際にはこの段階では頭の中に完成形のイメージがあるだけですから、実現方法を考えていきます。

入力データ

まずは、どのような形式のデータがダウンロードできるのか確認します。対象とするデータは次のページからダウンロードできる「北九州市 新型コロナウイルス感染症 陽性患者属性」とします。

ckan.open-governmentdata.org

実際にダウンロードして中身を確認してみます。

f:id:tiger62shin:20220307171521p:plain

1 行で陽性患者 1 名分となっています。[公表_年月日] があるので、これをキーにしてデータ件数を数えればよさそうです。

入力データの取得

GROWI データ移行で使用した requests モジュールが使えそうです。

入力データの読み込みと集計

入力データの形式が CSV なので、CSV パーサー探してきて日付をキーにしたディクショナリに集計すればよいと考えていていたところ pandas の入力に CSV データが指定できることがわかり、「読み込み→集計」が pandas で完結するんじゃないかと思い調べてみました。
まずは、簡単なテストプログラムを作ってみます。

import requests
import io
import pandas as pd

url = 'https://ckan.open-governmentdata.org/dataset/aad66771-0e86-4d38-b08e-7b74d31f442e/resource/111b9476-bc80-4700-9551-3ba8a4ffcebc/download/401005_kitakyushu_covid19_patients.csv'
res = requests.get(url)
df = pd.read_csv(io.BytesIO(res.content), encoding='shift-jis', encoding_errors='ignore')
print(df.head())

問題なさそうです。
encoding_errors='ignore' があるのは元データにデコードできないデータがあってエラーになってしまうので指定しています。
日毎の集計は DataFrame の size() メソッドでできそうなところめまでは、わりとすぐに突き止めたのですが、size() メソッドでちょっとハマりました。そもそも pandas は存在は知っていましたが、使うのは初めてなので DataFrame や Series などの特性もわかっていませんでした。
DataFrame に対して size() メソッド実行すると Series になるところまでは分かったのですが、Series から DataFrame に戻す方法がわからなかったりで、結構時間がかかりました。最終的にこのようなコードになっています。

number_of_patients = df.groupby('Date').size() \
    .reset_index(name='Count') \
    .set_index('Date')
  • 'Date' をキーにして件数を数える
  • reset_index() で Series から DataFrame に変換。この時、件数に 'Count' という名前をつける
  • 'Date' (日付) をインデックスにする

Series から DataFrame に戻しているのは、この後各日のステータス (セルの色を変えるための「増えた」、「減った」の状態) の列を加えるためです。

HTML の出力

Python で使えるテンプレート エンジンを探したところ jinja2 を見つけましたので、これを使うことにします。



pandas 以外にも、いろいろなところでハマっていたので (pandas の使い方は、もっとしっかり勉強する必要がありそうです)、結構時間がかかりましたが、とりあえず完成しましたので、一日一回定時起動して生成した内容を公開しています。

www.calcium.mydns.jp

データはあまり頻繁には更新されないみたいなので、今現在の状況はわかりませんが、おおよその傾向はわかるのではないかと思います。

GROWI の時はソースコードもブログに掲載しましたが、これからは GitHub の方で公開します。

github.com