乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

可視化

今回は大谷選手の
ホームラン記録を可視化してみました。


解説動画はこちら



先日2024年09月20日
6打数3安打3ホームランなど
前人未到の記録を打ち立てた
ドジャース大谷選手

2004年シーズンの
今日までの成績を可視化してみました。


データの取得


可視化の元となるデータを取得します。

2024/09/21日現在
今季通算 603打数179安打 打率.297 52本塁打
122打点 125得点 52盗塁
という驚異的な成績です

この結果になるように加工していきます。

import requests
from bs4 import BeautifulSoup
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

url = "https://times.abema.tv/articles/-/10018233"

res = requests.get(url)
soup = BeautifulSoup(res.content,"lxml")
table = soup.table

columns = ['日付', '対戦相手', '打順', '第1', '第2', '第3', '第4', '第5', '第6', '第7']
data = []
trs = table.find_all("tr")
for tr in trs[1:]:
    tds = tr.find_all(["td","th"])
    if "なし" in str(tds) or "中止" in str(tds):
        continue
    tmp = [td.text.replace("\xa0","").replace("\n","").replace("\r","").replace("\t","") for td in tds]
    tmp = tmp[0:3]+[t.replace("ニ","二").replace("2","二").replace("3","三") for t in tmp[3:]]
    if len(tmp)>=11:
        tmp = tmp[:10]
    data.append(tmp)

data2 = []
for d in data:
    t1,t2 = d[0:3],d[3:]
    for t in t2:
        if ""!=t:
            data2.append(t1+[t])

# データの確認
data2[0:2]
[['3月20日', 'パドレス', '2指', '遊ゴ'], ['3月20日', 'パドレス', '2指', '右安']]


これを集計しやすいようにデータフレームに加工していきます。
df = pd.DataFrame(data2,columns=["date","match","order","result"])

# 日本語の日付を変換する関数
def convert_date(date_str):
    month,day = date_str.replace("日","").split("月")
    return f"2024-{int(month):02}-{int(day):02}"

df['date'] = df['date'].apply(convert_date)
df['date'] = pd.to_datetime(df['date'])

# 本塁打
df['home_run'] = df['result'].apply(lambda x: 1 if '本' in x else 0)

# 安打
df['hit1'] = df['result'].apply(lambda x: 1 if '安' in x else 0)

# 2塁打
df['hit2'] = df['result'].apply(lambda x: 1 if x.endswith('二') else 0)

# 3塁打
df['hit3'] = df['result'].apply(lambda x: 1 if x.endswith('三') else 0)

# 指定された用語のリスト
terms = ['四球', '敬遠', '死球', '打妨', '中犠', '右犠', '左犠']

# 打数計算
df['flag'] = df['result'].apply(lambda x: 0 if x in terms else 1)

# 打席数合計
df['strokes'] = df['flag'].cumsum()

# ヒット数合計
df['hit_total'] = df[['hit1','hit2','hit3']].sum(axis=1).cumsum()

# ホームラン数合計
df['home_run_total'] = df['home_run'].cumsum()

# 打率
df['average'] = df[['hit_total','home_run_total']].sum(axis=1) / df['strokes']

# 603打数179安打 打率.297 52本塁打
df.tail()
index,date,match,order,result,home_run,hit1,hit2,hit3,flag,strokes,hit_total,home_run_total,average
688,2024-09-20 00:00:00,マーリンズ,1指,右本,1,0,0,0,1,599,125,51,0.2938230383973289
689,2024-09-21 00:00:00,ロッキーズ,1指,空振,0,0,0,0,1,600,125,51,0.29333333333333333
690,2024-09-21 00:00:00,ロッキーズ,1指,中安,0,1,0,0,1,601,126,51,0.2945091514143095
691,2024-09-21 00:00:00,ロッキーズ,1指,中本,1,0,0,0,1,602,126,52,0.2956810631229236
692,2024-09-21 00:00:00,ロッキーズ,1指,一安,0,1,0,0,1,603,127,52,0.296849087893864

これで同じ結果になりました。



Altairでプロットする

ここからこのデータを用いて
可視化を行います。

import pandas as pd
import altair as alt

# Altairで折れ線グラフを作成
chart = alt.Chart(df).mark_line().encode(
    x='date',
    y='average'
).properties(
    title='Data vs Average',
    width=800
)
chart.display()
visualization (1)



打率とホームラン数の推移を
2軸でプロットするとこうなります。
# averageの折れ線グラフ
average_line = alt.Chart(df).mark_line(color='blue').encode(
    x='date',
    y=alt.Y('average', axis=alt.Axis(title='Average'))
)

# home_run_totalの折れ線グラフ
home_run_line = alt.Chart(df).mark_line(color='red').encode(
    x='date',
    y=alt.Y('home_run_total', axis=alt.Axis(title='Home Run Total'))
)

# 二重軸のグラフを作成
chart = alt.layer(
    average_line,
    home_run_line
).resolve_scale(
    y='independent'
).properties(
    title='Data vs Average and Home Run Total',
    width=800
)
chart.display()
visualization


途中から急にホームラン数のピッチが上がっているようです。

ここからどれだけ伸びるのか
残り試合のホームラン数の予測もしてみます。


ホームラン数を時系列予測

残り試合は8試合です。
ARIMAという時系列予測モデルを用いて
予測をしてきます。

先ほどのデータフレームを加工して
予測データを作っていきます。

import pandas as pd
from statsmodels.tsa.arima.model import ARIMA
import matplotlib.pyplot as plt

# 日付をインデックスに設定
df2 = df.copy()
df2 = df.groupby('date').max()

# 欠損日を埋めて連続した日付にする
df2 = df2.asfreq('D')

# ARIMAモデルのフィッティング
model = ARIMA(df2['home_run_total'], order=(1, 1, 1))
model_fit = model.fit()

# 8日先までの予測
forecast = model_fit.forecast(steps=8)

# 結果のプロット
plt.figure(figsize=(12, 4))
plt.plot(df2.index, df2['home_run_total'], label='Actual')
plt.plot(pd.date_range(df2.index[-1] + pd.Timedelta(days=1), periods=8, freq='D'), forecast, label='Forecast', color='red')
plt.legend()
plt.show()
download

実測と予測を組み合わせて可視化しました。

残り8試合だとすると
およそ55本くらいまでは伸びそうです。

予測が外れて60本塁打くらいまで
行って欲しいですね!!!!

今回は大谷選手の打席結果を用いた
ホームラン成績の可視化でした

それでは。

今回はインタラクティブな可視化が行えるライブラリ
Altairのご紹介です。


解説動画はこちら



Altairライブラリについて

少ないコードで複雑なビジュアライゼーションを作成できる
可視化ライブラリです。

静止画でなくインタラクティブな
ビジュアライゼーションを行うことができます。

Pandasデータフレームと相性が良く
ダッシュボード制作などで活用されるケースが増えています。



他の可視化ライブラリとの違い

スクリーンショット 2024-08-10 15.11.51

Altairの他にも沢山の可視化ライブラリがあります。

その中でもMatplolibは定番の可視化ライブラリですが
ドキュメントの数量は多く、全てを覚えるのはなかなか大変です。

AltairはPlotlyのようなインタラクティブな可視化が出来る事と
学習コストがかなり少ない事が大きな利点です。

可視化の目的によって
これらのライブラリを使い分けると良いでしょう。


Altairの使い方

1.ライブラリのインポート

Google ColabではそのままAltairを利用できます。
import pandas as pd
import numpy as np
import altair as alt
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)

その他、必要なライブラリを読み込んでおきましょう。


2.データの用意

適当なデータをPandasデータフレームで作成しておきます。
# サンプルデータの作成
data = pd.DataFrame({
    'x': [1, 2, 3, 4, 5],
    'y': [2, 3, 4, 5, 6]
})

3.チャートの作成

Altairの基本的なチャートは、alt.Chartオブジェクトを使用して作成します。

mark_*メソッドでマーク(グラフの種類)を指定し
encodeメソッドでデータのエンコード(軸、色、大きさなど)を
指定する構文になっています。

チャート変数 = alt.Chart(データ変数).mark_point().encode(
  エンコード内容
)

# チャートの作成
chart = alt.Chart(data).mark_point().encode(
    x='x',
    y='y'
)

# チャートの表示(Jupyter Notebook環境などで)
chart
visualization (9)

指定できるmark_*メソッド例として

mark_point:ポイントマーク(散布図)
mark_line:折れ線グラフ
mark_bar:棒グラフ
mark_circle:バブルチャート
mark_rect:矩形マーク(ヒートマップなど)
mark_boxplot:箱ひげ図
mark_errorband:エラーバンド
mark_errorbar:エラーバー
mark_geoshape:地理シェープ(地図)
mark_image:画像マーク
mark_area:エリアチャート
mark_rule:ルールマーク(基準線など)
mark_square:四角形マーク(散布図の一種)
mark_text:テキストマーク
mark_tick:ティックマーク

こんな感じのグラフを作成できます。


4. エンコーディング

encodeメソッドを使用して
データを視覚的な属性にマッピング出来ます。

x : x軸
y : y軸
color : 色
size : サイズ
shape : 形
tooltip : ホバー時に表示されるツールチップ
data = pd.DataFrame({
    'x': [1, 2, 3, 4, 5],
    'y': [2, 3, 4, 5, 6],
    'category': ['A', 'B', 'A', 'B', 'A']
})

chart = alt.Chart(data).mark_point().encode(
    x='x',
    y='y',
    color='category',
    tooltip=['x', 'y', 'category']
)
chart

5.インタラクティブ性の追加

interactiveメソッドを使用して
グラフにインタラクティブ機能(ズーム、パンなど)を
追加できます。
chart = alt.Chart(data).mark_point().encode(
    x='x',
    y='y',
    color='category'
).interactive()
chart

6.レイヤリングと複合グラフ

Altairでは複数のチャートをレイヤリングして
重ね合わせることも可能です。

+演算子を使用してチャートを合成します。

points = alt.Chart(data).mark_point().encode(
    x='x',
    y='y',
    color='category'
)

lines = alt.Chart(data).mark_line().encode(
    x='x',
    y='y',
    color='category'
)

combined_chart = points + lines
combined_chart


Altairグラフのコードサンプル

散布図
# サンプルデータの作成
np.random.seed(42)
data = pd.DataFrame({
    'x': np.random.randn(100),
    'y': np.random.randn(100),
    'category': np.random.choice(['A', 'B', 'C'], 100)
})

# インタラクティブな散布図の作成
scatter_plot = alt.Chart(data).mark_point().encode(
    x='x',
    y='y',
    color='category',
    tooltip=['x', 'y', 'category']
).interactive()
scatter_plot
visualization (5)

棒グラフ
# サンプルデータの作成
data = pd.DataFrame({
    'category': ['A', 'B', 'C', 'D', 'E'],
    'value': [5, 3, 6, 7, 2]
})

# インタラクティブな棒グラフの作成
bar_chart = alt.Chart(data).mark_bar().encode(
    x='category',
    y='value',
    tooltip=['category', 'value']
).interactive()
bar_chart
visualization (4)

折れ線グラフ
# サンプルデータの作成
data = pd.DataFrame({
    'date': pd.date_range(start='2023-01-01', periods=100),
    'value': np.random.randn(100).cumsum()
})

# インタラクティブな折れ線グラフの作成
line_chart = alt.Chart(data).mark_line().encode(
    x='date:T',
    y='value:Q',
    tooltip=['date:T', 'value:Q']
).interactive()
line_chart
visualization

ヒートマップ
# サンプルデータの作成
data = pd.DataFrame({
    'x': np.repeat(np.arange(1, 11), 10),
    'y': np.tile(np.arange(1, 11), 10),
    'value': np.random.randn(100)
})

# インタラクティブなヒートマップの作成
heatmap = alt.Chart(data).mark_rect().encode(
    x='x:O',
    y='y:O',
    color='value:Q',
    tooltip=['x', 'y', 'value']
).interactive()
heatmap
visualization (2)

バブルチャート
# サンプルデータの作成
np.random.seed(42)
data = pd.DataFrame({
    'x': np.random.randn(100),
    'y': np.random.randn(100),
    'size': np.random.rand(100) * 100,
    'category': np.random.choice(['A', 'B', 'C'], 100)
})

# インタラクティブなバブルチャートの作成
bubble_chart = alt.Chart(data).mark_circle().encode(
    x='x',
    y='y',
    size='size',
    color='category',
    tooltip=['x', 'y', 'size', 'category']
).interactive()
bubble_chart
visualization (1)

ヒストグラム
# データの生成
np.random.seed(42)
data = pd.DataFrame({
    'value': np.random.normal(loc=0, scale=1, size=1000)
})

# ヒストグラムの作成
histogram = alt.Chart(data).mark_bar().encode(
    alt.X('value:Q', bin=True, title='Value'),
    alt.Y('count()', title='Frequency')
).interactive()
histogram
visualization (12)

箱ひげ図
# データの生成(例: 1000個のランダムな数値を3つのカテゴリに分ける)
np.random.seed(42)
data = pd.DataFrame({
    'category': np.random.choice(['A', 'B', 'C'], size=1000),
    'value': np.random.normal(loc=0, scale=1, size=1000)
})

# 箱ひげ図の作成
boxplot = alt.Chart(data).mark_boxplot().encode(
    x='category:O',
    y='value:Q'
).interactive()
boxplot
visualization (11)

エリアチャート
# データの生成
data = pd.DataFrame({
    'year': [2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007],
    'value': [10, 15, 13, 17, 20, 19, 15, 10]
})

# エリアチャートの作成
areachart = alt.Chart(data).mark_area().encode(
    x='year:O',
    y='value:Q'
).interactive()
areachart
visualization (10)




まとめ

Altairは可視化ライブラリの中でも比較的学習コストが低く
かなり使い勝手の良い可視化ライブラリです。

特にインタラクティブな可視化を行えるのが魅力的なライブラリで
最近はデータサイエンスやデータ分析の分野で広く採用されて来ています。

WEBアプリを作成できるStreamlitライブラリと相性が良く
簡易なアプリ作成やダッシュボード作成などにも向いています。

Matplotlibの可視化に物足りなくなって来たら
こちらのライブラリはオススメです。

それでは。





最近、やけに地震が多いので
気になって調べて
データを取って可視化してみました。


解説動画はこちら


 
今回のコードは
気象庁の地震データを取得して
そのデータを可視化するコードです。

先にデータが必要になるので
気になった人はコードを実行してみて下さい。

Jupyter Notebook や Google Colab で
実行できると思います。


地震データを取得する



気象庁から地震の観測データを取得して
データフレームにします。

やりたい人は、30分くらいかかるので
気をつけてやって下さい。

リンクを取得
import re
import time
import pandas as pd
import requests
from tqdm import tqdm
from bs4 import BeautifulSoup

domain = "https://www.data.jma.go.jp/"
index_url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/index.html"
res = requests.get(index_url)
soup = BeautifulSoup(res.content, "html.parser")
eq_link = [i.get("href") for i in soup.find_all("a") if len(i.get("href"))==13]
print(len(eq_link))

リンクからデータを取得する
# 地震データをテキストから取得
def data_pick(text):
    row_data = []
    for row in [i for i in text.split("\n") if len(i)>1][2:]:
        row = row.replace("° ","°")
        for i in range(7,1,-1):
            row = row.replace(" "*i, " ")
        row = row.replace(":"," ")
        tmp = row.split(" ")
        row_data.append(tmp[:-1])
    return row_data

all_data = []
for day in tqdm(eq_link):
    url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/" + day
    #print(url)
    res = requests.get(url)
    soup = BeautifulSoup(res.content, "html.parser")
    time.sleep(3.971)
    text_data = soup.pre.text
    all_data += data_pick(text_data)
    #break

print(len(all_data))


データを加工してデータフレームにする
columns=["年","月","日","時","分","秒","緯度","経度","深さ(km)","M","震央地名"]
df = pd.DataFrame(all_data,columns=columns)
df = df[df["M"] != "-"].reset_index(drop=True)
df = df.astype({"M": float , "深さ(km)":int, "年":int,"月":int,"日":int})
df["M2"] = df["M"]//1
df["年月日"] = df.apply(lambda x : "{0}{1:02}{2:02}".format(x["年"],x["月"],x["日"]),axis=1)

def lat_lon_10(x):
    tmp = x.split("°")
    degree = int(tmp[0])
    minute = int(tmp[1].split(".")[0])
    second = int(tmp[1].split(".")[1].split("'")[0])
    # 度、分、秒を10進法で表現
    decimal_degree = degree + minute/60 + second/3600
    return decimal_degree

df["緯度10"] = df["緯度"].map(lat_lon_10)
df["経度10"] = df["経度"].map(lat_lon_10)
df["size"] = df["M2"] ** 3

うまく実行できたら
df という変数にデータが格納されると思います。

エラーが出る人は
ライブラリが足りなかったり
通信がうまくいっていなかったり
色々な事象が起きると思いますが
全部は対応出来ませんので
がんばって解決して下さい。


データを見てみる


データを取得できたら
データを見てみましょう。

変数 df に対して
条件を指定すれば
絞り込みをしてみる事ができます。

マグニチュード別の地震回数
df[["M2","震央地名"]].groupby("M2").count().sort_index(ascending=False)
スクリーンショット 2024-03-02 16.29.41

日本地図にプロットする


日本地図にプロットするには
緯度と経度が必要です。

df 変数にはカラムとして
作成しているので
地図に表示する事ができます。

import plotly.express as px

fig = px.scatter_mapbox(
    data_frame=df[df["M2"]>=5],
    lat="緯度10",
    lon="経度10",
    hover_data=["年月日","深さ(km)"],
    color="M2",
    size="size",
    size_max=20,
    opacity=0.3,
    zoom=4,
    height=700,
    width=1500)

fig.update_layout(mapbox_style='open-street-map')
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
fig.show()
output_88_0




千葉県だけにすると
こんな感じです。

output_106_0


色々な条件で絞り込んで
地図に反映させる事ができます。

最近は地震が多いので
何かが起きている可能性がありますね。

警戒を強める意味でも
データを見る価値はあるかもしれません。

今回は地震のデータを取得して
それを可視化するコードについてでした。

それでは。


先日結構大きな地震が有ったので
気になってしまいました。

地震のデータを集めてみたので
可視化することにしました。

解説動画はこちら



さて今回参考にしたのは
気象庁のデータです。

地震データベースがあるので
そこからCSVでダウンロードできるようです。

観測当時からのデータがありますが
震度1とかにしてしまうと
データが多すぎて全件は無理そうでした。

今回は震度5弱以上で抽出し
地震リスト.tsvに保存しています。

早速データを読み込みします。
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

data1_path = '地震リスト.tsv'
df1 = pd.read_table(data1_path)

中身はこんな感じになっています。

スクリーンショット 2021-10-09 16.57.00

つい最近までの震度5弱以上の震源地が記載されています。


これを可視化してみます。

可視化にはFoliumを使用します。
これはデータを地図上にプロットして
見やすくしてくれるものです。

マッピングには緯度経度を使用します。
データだと緯度経度の値などが
文字列かつ形式も違うので
そのままではマッピングに適していません。

DMS形式の緯度経度を
Degree形式の緯度経度に直します。

その他数値であるデータもデータ型を直す
前処理を行うコードがこれです。

# 不明データの削除
df1 = df1[~(df1['緯度']=='不明') | ~(df1['経度']=='不明')]
df1 = df1[~(df1['M']=='不明')]
df1 = df1.reset_index(drop = True)

# 震度データの整理
df1['最大震度'][df1['最大震度']=='震度5'] = '震度5弱'
df1['最大震度'][df1['最大震度']=='震度6'] = '震度6弱'

# 数値データの整形
df1['緯度2'] = df1['緯度'].str.replace('′N','').str.replace('°','.').str.split('.')
df1['経度2'] = df1['経度'].str.replace('′E','').str.replace('°','.').str.split('.')
df1['深さ'] = df1['深さ'].str.replace(' km','')

# 緯度経度をDegree形式へ変換
import math
from decimal import Decimal, ROUND_HALF_UP

# DMS形(度分秒)からDegree形式(度)への変換
def dms_to_degree(d):
    h,m,s = int(d[0]),int(d[1]),int(d[1])
    return Decimal(str(h + (m / 60) + (s / 3600))).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP)
    
# 緯度経度の変換
df1['longitude'] = df1['緯度2'].apply(dms_to_degree)
df1['latitude'] = df1['経度2'].apply(dms_to_degree)

# データ型変換
df1['longitude'] = df1['longitude'].astype('float')
df1['latitude'] = df1['latitude'].astype('float')
df1['M'] = df1['M'].astype('float')
df1['深さ'] = df1['深さ'].astype('int')

これで下処理ができました。
出来上がりはこんな感じです。

スクリーンショット 2021-10-09 16.58.15


早速このデータを使って
Foliumでマッピングしたいと思います。

今回は震度別で色で分けられるように
マッピングしました。

データは前処理したdf1をそのまま使用します。

import folium
from folium.features import CustomIcon
import pandas as pd

# マーカーの指定
def make_maker(r,color):
    rad = r['M'] * 1000
    name = r['地震の発生日'] + ' ' + r['地震の発生時刻'] +' : ' + r['震央地名']
    tmp = folium.Circle(
        location = [r['longitude'],r['latitude']], 
        popup    = name ,
        tooltip  = '{0} , 深さ : {1}km , M{2} , {3}'.format(name,r['深さ'],r['M'],r['最大震度']),
        radius = rad , color     = color , fill = True )
    return tmp
    
plot_map = folium.Map(location=[適当な緯度,適当な経度] ,  zoom_start=5)
s5j_group = folium.FeatureGroup(name="震度5弱").add_to(plot_map)
s5k_group = folium.FeatureGroup(name="震度5強").add_to(plot_map)
s6j_group = folium.FeatureGroup(name="震度6弱").add_to(plot_map)
s6k_group = folium.FeatureGroup(name="震度6強").add_to(plot_map)
s7_group = folium.FeatureGroup(name="震度7").add_to(plot_map)

for i, r in df1[df1['最大震度']=='震度5弱'].iterrows():
    s5j_group.add_child(make_maker(r,'#99FFFF'))
for i, r in df1[df1['最大震度']=='震度5強'].iterrows():
    s5k_group.add_child(make_maker(r,'#66FF00'))
for i, r in df1[df1['最大震度']=='震度6弱'].iterrows():
    s6j_group.add_child(make_maker(r,'#FFFF00'))
for i, r in df1[df1['最大震度']=='震度6強'].iterrows():
    s6k_group.add_child(make_maker(r,'#FF00FF'))
for i, r in df1[df1['最大震度']=='震度7'].iterrows():
    s7_group.add_child(make_maker(r,'#FF0000'))
folium.LayerControl().add_to(plot_map)
plot_map.save("HTMLファイル名")

実行するとHTMLファイルが出来上がります。
それを開くとこんな感じです。

スクリーンショット 2021-10-09 16.52.44


地図の右上から震度別で
表示を切り替えできるようになっています。

スクリーンショット 2021-10-09 16.54.24

震度6以上だとこんな感じに

スクリーンショット 2021-10-09 16.54.51

震度7だけだとこうなります。
スクリーンショット 2021-10-09 16.55.15


先日地震のあった場所は
千葉県の北西部で震度5強を観測しています。

スクリーンショット 2021-10-09 17.05.12


こんな感じでうまくマッピング出来て
いろいろ切り替えながら眺められるので
面白いと思います。

50年以上の観測データのようですが
関東圏では震度6強より大きな地震の発生がなさそうですね。

ただ、ここ最近の大地震を線で結ぶと
スクリーンショット 2021-10-09 16.54.51のコピー

きれいに一直線でつながるのが
少し怖いですねーー

無理くりつなげてみましたが
このライン上には何か有るのかも・・・

いつ地震が来ても良いような
備は考えておくべきですね!!!!

今回は地震データを
Folium可視化してみました。

やりたい方はコピペして
試してみてくださいね。

それでは。

最近感染が拡大しているということで
最新のコロナ関連のデータを見てみましょう。

解説動画はこちら



今回参考にさせていただくデータは
東洋経済さんのサイトのデータです。

それも厚生労働省のオープンデータが元になっていますが
サイトからダウンロードすることができます。

東洋経済さんのサイト

こちらのデータを元にして
現状を見てみることにしましょう。

まずはデータの読み込みです。
こちらのコードで直接データフレームに
データを読み込むことができます。

import pandas as pd

url = 'https://toyokeizai.net/sp/visual/tko/covid19/csv/prefectures.csv'
df = pd.read_csv(url)
df.head()
スクリーンショット 2021-07-31 17.21.16


集計したりするには
少しデータの形がアレなので
データをきれいに整形します。

def to_date(d):
    return pd.to_datetime('{0:02}/{1:02}/{2:02}'.format(d['year'],d['month'],d['date']),format='%Y/%m/%d')

# 日付に直す
df['days'] = df.apply(to_date, axis=1)

# 値の補完
df['deaths'] =  df['deaths'].str.replace('-','0')
df['serious'] =  df['serious'].str.replace('-','0')

# Null値の補完
df = df.fillna(0)

# 型変換
df['deaths'] =  df['deaths'].astype(int)
df['serious'] =  df['serious'].astype(int)
df['peopleTested'] =  df['peopleTested'].astype(int)
df['hospitalized'] =  df['hospitalized'].astype(int)
df['discharged'] =  df['discharged'].astype(int)

# 必要なカラムのみに絞る
df = df[['days','prefectureNameJ', 'peopleTested', 'testedPositive','discharged', 'hospitalized', 'serious', 'deaths' ]]

df.head()
スクリーンショット 2021-07-31 17.23.00

だいぶ綺麗になりましたね。

これを使って色々集計してみることにしましょう。
こちらのデータは累計データのようです。

まずは都道府県別の累計感染者数を
見てみることにしましょう。

df.groupby('prefectureNameJ').max().sort_values('testedPositive',ascending=False)
スクリーンショット 2021-07-31 17.25.09

2021年7月29までのデータで
感染者数の多い順で都道府県別の累計数を見ています。

東京や大阪、神奈川が多いのは
人口比どうりかと思いますが
沖縄とかも増えてきていますね。


次は色々な率を見てみます。
まずは全国の率を見てみます。

df['陽性率'] =  df['testedPositive'] / df['peopleTested'] * 100
df['復帰率'] = df['discharged'] / df['testedPositive'] * 100
df['療養率'] = df['hospitalized'] / df['testedPositive'] * 100
df['重症化率'] = df['serious'] / df['testedPositive'] * 100
df['死亡率'] = df['deaths'] / df['testedPositive'] * 100
days = '2021-07-29'
df2 = df[['prefectureNameJ','peopleTested','testedPositive','discharged','hospitalized','serious','deaths','陽性率','復帰率','療養率','重症化率','死亡率']][df['days']==days]

df3 = pd.DataFrame(data=df2[['peopleTested','testedPositive','discharged','hospitalized','serious','deaths']].sum())
df3 = df3.T
df3['陽性率'] =  df3['testedPositive'] / df3['peopleTested'] * 100
df3['復帰率'] = df3['discharged'] / df3['testedPositive'] * 100
df3['療養率'] = df3['hospitalized'] / df3['testedPositive'] * 100
df3['重症化率'] = df3['serious'] / df3['testedPositive'] * 100
df3['死亡率'] = df3['deaths'] / df3['testedPositive'] * 100

df3
スクリーンショット 2021-07-31 17.28.51

累計感染者数は90万人弱
累計死亡者数は15166人で
死亡率だと1.68%ほどになります。

参考として2018年の
インフルエンザの感染者が1200万人
死亡者は3325人ということでしたので
そこから計算すると死亡率は0.027%
コロナの方が60倍も高いことになってしまいます。

次のコードで率の高いところを出すことができます。

PCR検査の陽性率は??
df2[['prefectureNameJ','peopleTested','testedPositive','陽性率']].sort_values('陽性率',ascending=False)
死亡者の割合が高いところは?
df2[['prefectureNameJ','testedPositive','deaths','死亡率']].sort_values('死亡率',ascending=False)

最後に最近では感染者が拡大傾向にあるということで
直近の60日間の動向から年末まで
このままいくとどうなるかを予測してみました。

こちらはfbprophetを使うので
インストールしていない人は
インストールなどが必要です。
(要pip install)


ライブラリの読み込みと学習データに整形します。
from fbprophet import Prophet
import warnings
warnings.simplefilter('ignore')

tmp_df = df[['days','testedPositive','discharged','hospitalized','serious','deaths']].groupby('days').sum()
tmp_df = tmp_df.reset_index()
tmp_df.head()

まずは感染者数の推移を予測します。
model = Prophet()
model_df = tmp_df.rename(columns={'days':'ds','testedPositive':'y'})
model.fit(model_df.tail(60))

future_df = model.make_future_dataframe(150)
forecast = model.predict(future_df)
model.plot(forecast)
download
7月に入ってからの感染者数が増えてきています。
年末までこのままのペースだと160万人
最悪180万人ほどにもなる見込みです。


次に死亡者の推移を予測してみます。
model = Prophet()
model_df = tmp_df.rename(columns={'days':'ds','deaths':'y'})
model.fit(model_df.tail(60))

future_df = model.make_future_dataframe(150)
forecast = model.predict(future_df)
model.plot(forecast)
downloadのコピー

死亡者は最近おちついてきている傾向です。
感染拡大が続いても今のペースが続くなら
そこまでの死亡者が出ない予測になっていますが
油断は禁物です。



まとめです。

2021年7月29日までの感染者数は90万人ほどで
うち死亡者は15166人で死亡率1.68%

感染拡大が広がっているが、現状のまま進むと
2021年内には累計160万人が感染し
死亡者累計1万7千人ほどになる見込みです。

ワクチンを打ってもかかる人はいます。
過去に感染しても再感染する人もいます。

正直な所
強めの風邪なんじゃないかと思います。

何も打つ手が無さそうなので
人の多いところに行かないで
静かに生活するしかなさそうですね。

データは日々更新されていきますが
このコードを使えば簡単に見れますので
興味のある方は見てみて下さい。

それでは。



このページのトップヘ