カテゴリー

当サイトはアフィリエイトプログラムによる収益を得ています〈景品表示法に基づく表記です)

Pythonの発展系

【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

運営者のメガネです。YouTubeTwitterInstagramも運営中。自己紹介お問い合わせページあります。

運営者メガネ

作成したコード全文

使用するデータ

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

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

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

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

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

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

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

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

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

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

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

日付 都道府県コード 都道府県名 各地の感染者数_1日ごとの発表数 各地の感染者数_累計 各地の死者数_1日ごとの発表数 各地の死者数_累計
2020/1/16 1 北海道 0 0 0 0
2020/1/17 1 北海道 0 0 0 0
2020/1/18 1 北海道 0 0 0 0
 ... 0 0 0 0
2021/5/5 1 北海道 181 25204 2 878
2021/5/6 1 北海道 320 25524 5 883
2021/5/7 1 北海道 248 25772 4 887
2020/1/16 2 青森県 0 0 0 0
2020/1/17 2 青森県 0 0 0 0
2020/1/18 2 青森県 0 0 0 0
 ... 0 0 0 0
2021/5/5 47 沖縄県 63 12778 0 137
2021/5/6 47 沖縄県 39 12817 0 137
2021/5/7 47 沖縄県 82 12899 1 138

日付は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で各国の収入と平均寿命の時代変化をバブルチャートで描く

続きを見る

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-Pythonの発展系
-, ,