カテゴリー

発展系

【plotly&mapbox】感染者数と死者数の散布図コロナマップを作成

2021年8月21日

こんな人にオススメ

新型コロナウイルスの感染者数や死者数を実際の都道府県の地図上に示したい!

ということで、今回はplotlymapboxを使用して新型コロナウイルス感染症の累計の感染者数と死者数を都道府県別に図示する。色んな表現方法があるが、今回は散布図で作成。

以前、新コロの感染者数と死者数についてグラフを作成した。これらは棒グラフなどを使用して、より詳細にデータを示した。しかし、今回は地図上なのでより直感的。

地図を扱うことができるようになると、あらゆるデータを地図とともに示して、より相手にわかりやすくなるだろう。

python環境は以下。

  • Python 3.9.6
  • numpy 1.21.1
  • pandas 1.3.1
  • plotly 5.1.0
  • plotly-orca 3.4.2
スポンサーリンク
スポンサーリンク

運営者のメガネです。TwitterInstagramも運営してます。

自己紹介はこちらから、お問い合わせはこちら。

運営者メガネ

作成したコード全文

使用するデータ

今回使用するデータはNHKの「特設サイト 新型コロナウイルス」の「新型コロナ データ一覧」もの。データ自体は編集・加工していないが、ファイル容量を減らしたグラフ化のためにデータの一部のみを使用した場面もある。

なお、データは2021年8月21日時点のもので、日が経過するとデータが変わったり更新されたりするので注意。このサイトには以下のような文言があるので、過去のデータを鵜呑みにし続けてはいけない。

※東京都は2021年2月15日、新型コロナの感染確認者数について、都内の保健所から報告漏れがあったとして、838人を追加で発表しました。(追加発表の対象期間:2020年11月18日~2021年1月31日)

また、今回は地図上の各都道府県に感染者数・死者数の値を入れるので、どの位置にどの都道府県があるのかを知らなくてはいけない。

ということで、「みんなの知識 ちょっと便利調」から「都道府県庁所在地 緯度経度データ」を使用した。このサイトではExcelファイルが公開されているので使用。

都道府県の位置は各庁(県庁など)をプロットする位置とした。

NHKの感染者数・死者数のグラフ

NHKでは、都道府県ごとの感染者数が都道府県の形状に沿って色付けされている。これに似たグラフを作成しようとしたが、ちょっと複雑そうだったので、とりあえずは散布図。

公開されているのは感染者数だが、死者数については地図がない。パッと作成できそうだがないので、今回の散布図で雰囲気だけでも掴んでほしい。

感染者数・死者数のデータの形式

感染者数・死者数のデータは「.csv」形式で以下のような構造となっている。

日付都道府県コード都道府県名各地の感染者数_1日ごとの発表数各地の感染者数_累計各地の死者数_1日ごとの発表数各地の死者数_累計
2020/1/161北海道0000
2020/1/171北海道0000
2020/1/181北海道0000
 ...0000
2021/5/51北海道181252042878
2021/5/61北海道320255245883
2021/5/71北海道248257724887
2020/1/162青森県0000
2020/1/172青森県0000
2020/1/182青森県0000
 ...0000
2021/5/547沖縄県63127780137
2021/5/647沖縄県39128170137
2021/5/747沖縄県82128991138

日付は2020年1月16日から2021年の8月20日までで、毎日更新されている。都道府県コードは都道府県ごとに北から順番に1から振られている。あとは1日ごと、累計の感染者数・死者数のデータがある。

データは合計で583日分でそれが47都道府県分あるので合計で27,401行存在する。なお、NHKのデータについては以下のように書かれている。

各自治体や公的機関の発表数値を基にNHKがまとめたものです。現在は1日に1回程度更新し掲載しています。ダウンロードしたファイルのデータはその時点の数値であり、発表に基づいて、後から修正・更新されることがあります。

繰り返すようだが、過去のデータを鵜呑みにしてはいけない。というよりそもそも情報ってのは1つに絞ってどうこうってものじゃない。自分で取捨選択する方が情報が平均されて安全。

各都道府県の緯度・経度のデータ

各都道府県庁の緯度・経度のデータは上の画像のようになっている。セルの結合があって面倒だが、使用するのは「世界測地系(WGS84)」「10進数」の「緯度」と「経度」。

pandasを使用することでこの列のデータだけを引っ張ってきて使用する。

下準備

import sys
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.io as pio

sys.path.append('../../')
import plotly_layout_template as template

まずは下準備としてのimport関連。plotly_layout_templateplotlyの自作テンプレートでレイアウトをキレイに整えるために使用。

【随時更新 備忘録】plotlyのグラフ即席作成コード

続きを見る

データの作成

まずは必要となるデータを作成する。今回は2種類のデータを整形して使いやすいようにする。

  • 各都道府県の感染者数・死者数
  • 各都道府県庁の緯度・経度

感染者数・死者数

df_covid = pd.read_csv(
    './nhk_news_covid19_prefectures_daily_data.csv',
    index_col=0,
)

感染者数・死者数についてはNHKのページからダウンロードしたファイルを読み込み、0列目にある日付をインデックスとした。出力は以下。

print(df_covid)
#            都道府県コード 都道府県名  ...  各地の死者数_1日ごとの発表数  各地の死者数_累計
# 日付                        ...
# 2020/1/16        1   北海道  ...                0          0
# 2020/1/17        1   北海道  ...                0          0
# 2020/1/18        1   北海道  ...                0          0
# 2020/1/19        1   北海道  ...                0          0
# 2020/1/20        1   北海道  ...                0          0
# ...            ...   ...  ...              ...        ...
# 2021/8/16       47   沖縄県  ...                1        246
# 2021/8/17       47   沖縄県  ...                1        247
# 2021/8/18       47   沖縄県  ...                0        247
# 2021/8/19       47   沖縄県  ...                1        248
# 2021/8/20       47   沖縄県  ...                0        248

# [27401 rows x 6 columns]

緯度・経度

df_position = pd.read_excel(
    './latlng_data.xls', header=(0, 1, 2), index_col=0,
    skiprows=2, skipfooter=6,  # データではない部分をスキップ
)

緯度・経度のデータについてはExcel形式なので、読み込みにはread_csvではなくread_excelを使用。基本的な使用方法はread_csvと同等。

表のタイトル2行分と表の最後に書いてある注釈6行分をスキップしたのち、結合されている0, 1, 2行目をヘッダとして使用する。インデックスは衣装しないけど自治体コードにしておいた。

緯度と経度のデータは予め変数lat, lonに入れてわかりやすくしておく。ヘッダがMulticolunsになっているので[]をつなげてデータを取り出している。空白とかがあるのでそこは注意して選択する。

lat = df_position['世界測地系(WGS84)'][' 10進数']['緯度']
lon = df_position['世界測地系(WGS84)'][' 10進数']['経度']

特定の日付のデータを抽出する関数

def get_date_df(year: int, month: int, day: int):
    date = f'{year}/{month}/{day}'  # ある特定の日付を選択
    date_df = df_covid.loc[date]
    return date_df, date

今回はある特定の日付時点での累計感染者数・死者数をマップ化するので、作成したデータフレームから特定の日付のデータを抽出。get_date_df関数の入力は年・月・日。

指定した日付を.locを使用してデータフレームから抽出。このデータと日付を返り値とする。

地図上に散布図をプロット(マップボックス)

def get_mapbox(df, size_data: str, color_data: str,
               cmin: float, cmax: float, size_scale: int):
    d = go.Scattermapbox(
        lat=lat, lon=lon,
        marker=dict(
            # マーカーサイズにするデータ
            size=np.log10(df[f"各地の{size_data}数_累計"]) * size_scale,
            # カラーにするデータ
            color=np.log10(df[f"各地の{color_data}数_累計"]),
            showscale=True,  # カラーバーを作成するか否か
            colorscale='Jet',  # カラースケール
            colorbar=dict(
                title=dict(text=f"累計{color_data}数(log)", side='right',),
            ),
            # カラーバーの表示色範囲
            cmin=np.log10(cmin), cmax=np.log10(cmax), cauto=False,
        ),
        # customdataで追加の情報を一括管理
        customdata=df[['都道府県名', '各地の感染者数_累計', '各地の死者数_累計']],
        hovertemplate='%{customdata[0]}<br>'
        + '累計感染数: %{customdata[1]}人<br>'
        + '累計死者数: %{customdata[2]}人<br>'
        + 'lat: %{lat}&deg;<br>'
        + 'lon: %{lon}&deg;<br>'
        + '<extra></extra>',
    )
    return d

ここが今回のメインテーマになる部分。地図上に散布図を作成する場合はgo.Scattermapboxを使用すればいい。これにlat, lonを入力するだけで地図上に散布図を作成可能。簡単。

今回はマーカーの色で感染者数(もしくは死者数)、マーカーのサイズで死者数(もしくは感染者数)を表すことにする。get_mapbox関数ではそれぞれsize_data, color_dataで指定している。

シンプルにデータだけを使用すると色が偏ったりマーカーのサイズが極端に大きくなるので、今回は以下のように対数(log)を使用して調整を入れている。

# マーカーサイズにするデータ
size=np.log10(df[f"各地の{size_data}数_累計"]) * size_scale,
# カラーにするデータ
color=np.log10(df[f"各地の{color_data}数_累計"]),

また、customdataを使用することで、複数個の追加データを指定することができる。1つだけならtextで賄えるが複数個になるとcustomdataが便利。

ここでは都道府県名と各地の感染者数、死者数の累計値をcustomdataにし、hoverでそれぞれの値を表示するように設定した。

# customdataで追加の情報を一括管理
customdata=df[['都道府県名', '各地の感染者数_累計', '各地の死者数_累計']],
hovertemplate='%{customdata[0]}<br>'
+ '累計感染数: %{customdata[1]}人<br>'
+ '累計死者数: %{customdata[2]}人<br>'
+ 'lat: %{lat}&deg;<br>'
+ 'lon: %{lon}&deg;<br>'
+ '<extra></extra>',

レイアウトの作成

def set_layout(date: str, color_data: str, size_data: str):
    title = f"{date}時点の日本各地の累計{color_data}数(色)"\\
        f"と{size_data}数(サイズ)"
    layout = go.Layout(
        template=template.plotly_layout(),
        mapbox=dict(
            # マップの初期表示中心位置
            center=dict(lat=lat.mean(), lon=lon.mean(),),
            zoom=5,
            style="open-street-map",  # マップの種類
            # pitch=0,  # マップの傾き(正の数のみ受け付け、手前に傾く)
            # bearing=0,  # 正の値で反時計回りに何度回転させるか
        ),
        title=title,
        showlegend=False,
        # デフォルトだと地図の外側に白い枠が広がっているから白い部分を削除
        margin=dict(l=0, r=0, t=0, b=0),
    )

    return layout

レイアウトではマップボックス特有のmapboxという引数が存在する。これらのうち、使えそうなものを上のコードに書いておいた。他にもあるかもしれないが、パッと見はこんなもん。

zoomは拡大率を示すが、いまいちどういう基準かがわからない。今回はいい感じになった5を選択。pitchは値を大きくしすぎると頭打ちとなって傾きが止まる。

コロナマップ作成(散布図やけど)



ということで、これまでの関数を総動員して新コロの感染者数・死者数の累計のマップを作成。上で書いた関数の引数をmapbox_graph関数でも使用。

def mapbox_graph(year: int, month: int, day: int,
                 size_data: str, color_data: str,
                 cmin: float, cmax: float, size_scale=1):

    # 特定の日付のデータを抽出
    date_df, date = get_date_df(year=year, month=month, day=day,)

    # プロットデータ作成
    plot = []
    # 都道府県と感染者数を同時に表示したい
    d = get_mapbox(
        df=date_df,
        size_data=size_data, color_data=color_data,
        cmin=cmin, cmax=cmax, size_scale=size_scale,
    )
    plot.append(d)

    # レイアウト作成
    layout = set_layout(date=date, color_data=color_data, size_data=size_data)

    fig = go.Figure(data=plot, layout=layout)
    # fig.show(config=template.plotly_config())

    # グラフの保存
    pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
    pio.write_html(fig, f"{color_data}.html")
    pio.write_image(fig, f"{color_data}.png")

あとは以下のように、用途に応じて引数を指定してあげると簡単に散布図版のコロナマップができてしまう。

# マーカーサイズを死者数、色合いを感染者とする
mapbox_graph(
    year='2021', month='8', day='20',
    size_data='死者', color_data='感染者', cmin=1e3, cmax=5e5, size_scale=20,
)

# マーカーサイズを感染者数、色合いを死者数とする
mapbox_graph(
    year='2021', month='8', day='20',
    size_data='感染者', color_data='死者', cmin=1e1, cmax=5e3, size_scale=10,
)

地図があるとわかりやすいな

以前作成した、新コロの感染者数と死者数のグラフはどちらも棒グラフなどで、パッと見で位置とデータが結びつきにくかった。

しかし、今回のグラフだと地図上でどれくらいなのかがわかりやすい。やっぱり都市圏だと感染者数が多いということがパッと見で分かる。

地図を使うことができたので、これを応用すると各都道府県を塗りつぶしたり3D棒グラフにしたりできる。より効果的に見せることができる。試行錯誤する。

関連記事

【plotly&Scattergl】大量のデータをplotlyで軽くグラフ化

続きを見る

【plt&棒グラフ】pythonのmatplotlibで棒グラフを作成してみる

続きを見る

【plotly&工夫】楽にグラフを描くためのplotlyの関数化

続きを見る

【plotly&バブルチャート】plotlyで各国の収入と平均寿命の時代変化をバブルチャートで描く

続きを見る

関連コンテンツ

スポンサーリンク

Amazonのお買い物で損したない人へ

1回のチャージ金額通常会員プライム会員
¥90,000〜2.0%2.5%
¥40,000〜1.5%2.0%
¥20,000〜1.0%1.5%
¥5,000〜0.5%1.0%

Amazonギフト券にチャージすることでお得にお買い物できる。通常のAmazon会員なら最大2.0%、プライム会員なら2.5%還元なのでバカにならない。

ゲットしたポイントは通常のAmazonでのお買い物に使えるからお得だ。一度チャージしてしまえば、好きなタイミングでお買いものできる。

なお、有効期限は10年だから安心だ。いつでも気軽にAmazonでお買い物できる。

Amazonチャージはここから出来るで

もっとお得なAmazon Prime会員はこちらから

30日間無料登録

執筆者も便利に使わせてもらってる

スポンサーリンク

  • この記事を書いた人

メガネ

独学でpythonを学び天文学系の大学院を修了。 ガジェット好きでMac×Android使い。色んなスマホやイヤホンを購入したいけどお金がなさすぎて困窮中。 元々、人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂ったために今も人生めちゃくちゃ楽しい。 pythonとガジェットをメインにブログを書いていますので、興味を持たれましたらちょこちょこ訪問してくだされば幸いです🥰。 自己紹介→変わって楽しいの繰り返し

-発展系
-, , , ,