体育の日は雨が降らないという印象が正しいかデータから真面目に検証してみた[コード編]

体育の日を基準にした雨の発生頻度のグラフ

この記事では、「体育の日に雨が降らないという印象が正しいか」という検証を行なった際に使用したコードを紹介します。

検証や考察についてはこちらの記事を参考にしてください。

体育の日は雨が降らないという印象が正しいかデータから真面目に検証してみた[検証編]
体育の日は天気が良いかどうか、データから真面目に検証してみました。この調査では、天気の良さを雨が降っていないかで評価し、データは気象庁のサイトから取得し、データの加工やグラフの作成をPythonで行なっています。

本来は分析しながら考察していくのが自然な流れなのですが、結果に汎用性があったのでコードと考察を分けました。ご了承ください。

一応レベルとしては、PythonとPandasの基本を勉強して、とりあえず使ってみたい段階の人間が調べながら取り組んでいるぐらいだと想定してください。

参考になるかは分かりませんが、こんなこともできるんだな、ぐらいに思っていただければ幸いです。

事前準備

データを気象庁のページから取得しておきます。画面の一例がこちらです。

気象庁のホームページからデータを取得する画面

(気象庁ホームページより引用。https://www.data.jma.go.jp/gmd/risk/obsdl/index.php  2018年10月7日最終閲覧)

都市名や期間、必要な項目を指定し、csvファイルとしてダウンロードすればOKです。

ダウンロードの段階では何を使うか決めていなかったので色々な項目を選びましたが、今回最終的に使ったのは降雨量だけでした。

対象とする6都市それぞれについてダウンロードしたら、それぞれファイル名を”sapporo.csv”のように”都市名” + “.csv”に変更し、後でアクセスしやすいよう適当なフォルダに移動しておきます。

コードの解説

データの確認

一旦データを確認します。

ターミナル上でcatでデータを確認すると文字化けしたので、普通にファイルを開いて確認しました。

csvファイルの冒頭部分の確認

1行目にダウンロード時刻、2行目は空白行、3行目に都市情報、4行目に行の情報、5行目は空白行、6行目は行への補足情報、7行目からデータが入っているようです。

初めの6行で必要なのは4行目ですね。6行目はあとで行名を変更する際にこちらで情報を足してあげることにしました。

全体の方針

ファイルの確認をした時点で、今回の調査は降雨量に関して行なっていくことにしました。降雨量については「現象なし情報」という補足情報の行があったのですが、これを使うかどうかは実際にデータを確認してから決めることにしました。

体育の日のリストの作成

いよいよここからコードに入っていきます。初めに体育の日のリストを作成しました。

今回は標準ライブラリのcalendarのCalendarオブジェクトが持つmonthdatescalendar()を使用しました。年と月を引数で与えると、その月の日付情報をdatetime.date型でリストとして返します。
例えばこんな感じです。

# カレンダーオブジェクトを使う
import calendar

calendar_obj = calendar.Calendar()
calendar_obj.monthdatescalendar(2018, 10)[0]
[datetime.date(2018, 10, 1),
datetime.date(2018, 10, 2),
datetime.date(2018, 10, 3),
datetime.date(2018, 10, 4),
datetime.date(2018, 10, 5),
datetime.date(2018, 10, 6),
datetime.date(2018, 10, 7)]

これはリストの0番目、つまり冒頭の要素です。リストの各要素は常に月曜日から始まるので、例えば10月1日が火曜日なら、9月30日がリストに入ってくることになります。

それを踏まえて、体育の日を返す関数を作成します。冒頭で見たように、体育の日は1999年までは10月10日、2000年からは10月の第2月曜日、2020年は7月24日です。

def return_sports_day(year):
month = 10
old_sports_date_day = 10
if year == 2020:
return_date = datetime.datetime(2020, 7, 24)
elif year >= 2000:
calendar_obj = calendar.Calendar()
date_list = calendar_obj.monthdatescalendar(year, month)
if date_list[0][0].month == 10:
dateinfo = date_list[1][0]
else:
dateinfo = date_list[2][0]
return_date = datetime.datetime(dateinfo.year, dateinfo.month, dateinfo.day)
else:
return_date = datetime.datetime(year, month, old_sports_date_day)
return return_date

なお、後の処理を考えて、今回は初めからdate型ではなくdatetime型で返すようにしました。この関数を使用して、体育の日のリストを作成します。

sports_day_list = [return_sports_day(each_year) for each_year in range(1989, 2018)]
sports_day_list
[datetime.datetime(1989, 10, 10, 0, 0),
datetime.datetime(1990, 10, 10, 0, 0),
datetime.datetime(1991, 10, 10, 0, 0),
datetime.datetime(1992, 10, 10, 0, 0),
datetime.datetime(1993, 10, 10, 0, 0),
datetime.datetime(1994, 10, 10, 0, 0),
datetime.datetime(1995, 10, 10, 0, 0),
datetime.datetime(1996, 10, 10, 0, 0),
datetime.datetime(1997, 10, 10, 0, 0),
datetime.datetime(1998, 10, 10, 0, 0),
datetime.datetime(1999, 10, 10, 0, 0),
datetime.datetime(2000, 10, 9, 0, 0),
datetime.datetime(2001, 10, 8, 0, 0),
datetime.datetime(2002, 10, 14, 0, 0),
datetime.datetime(2003, 10, 13, 0, 0),
datetime.datetime(2004, 10, 11, 0, 0),
datetime.datetime(2005, 10, 10, 0, 0),
datetime.datetime(2006, 10, 9, 0, 0),
datetime.datetime(2007, 10, 8, 0, 0),
datetime.datetime(2008, 10, 13, 0, 0),
datetime.datetime(2009, 10, 12, 0, 0),
datetime.datetime(2010, 10, 11, 0, 0),
datetime.datetime(2011, 10, 10, 0, 0),
datetime.datetime(2012, 10, 8, 0, 0),
datetime.datetime(2013, 10, 14, 0, 0),
datetime.datetime(2014, 10, 13, 0, 0),
datetime.datetime(2015, 10, 12, 0, 0),
datetime.datetime(2016, 10, 10, 0, 0),
datetime.datetime(2017, 10, 9, 0, 0)]

これで体育の日の日付情報の準備ができました。

1都市に対するデータ処理

まずは1つの都市に対してデータ処理を行い、どういった作業をしていくかの方針を決めます。

初めにライブラリのimportと基本的な設定をいくつか行なっておきます。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import os

plt.rcParams["font.size"] = 12
root_dir = "../input/weather_of_sports_day/"

次にデータを取り込みます。先ほど確認したファイルの中身を踏まえ、不要な部分をskipしておきます。

tokyo_df = pd.read_csv(os.path.join(root_dir, "tokyo.csv"), skiprows=[0, 1, 2, 4, 5], encoding="cp932")
tokyo_df.head()
年月日 平均気温(℃) 最高気温(℃) 最低気温(℃) 日照時間(時間) 日照時間(時間).1 平均風速(m/s) 最小相対湿度(%) 平均湿度(%) 平均蒸気圧(hPa) 天気概況(昼:06時~18時) 平均雲量(10分比) 天気概況(夜:18時~翌日06時) 降水量の合計(mm) 降水量の合計(mm).1
0 1989/10/1 NaN NaN NaN NaN NaN NaN 45 NaN NaN 晴後曇 7.8 曇一時雨 0.0 1
1 1989/10/2 NaN NaN NaN NaN NaN NaN 58 NaN NaN 曇時々雨 10.0 0.0 0
2 1989/10/3 NaN NaN NaN NaN NaN NaN 62 NaN NaN 曇後雨 10.0 雨一時曇 0.0 0
3 1989/10/4 NaN NaN NaN NaN NaN NaN 67 NaN NaN 雨後曇 10.0 曇後晴 34.0 0
4 1989/10/5 NaN NaN NaN NaN NaN NaN 53 NaN NaN 3.5 晴後曇一時雨 0.0 1

この時点で列情報を限定してもよかったのですが、今回はあとで絞る方式にしました。

次は列名をアルファベット表記に変えます。一旦リストを作ったあと、変更用のdictを作成します。

rename_cols = ["date", "temp_mean", "temp_max", "temp_min", "day_length", "day_length_existence", "wind_strength",
"humidity_min", "humidity_mean", "vp_mean", "weather_day", "clouds_mean", "weather_night", "rain_amount", "rain_existence"]
rename_dict = {base_col: new_col for base_col, new_col in zip(tokyo_df.columns, rename_cols)}

これで、元のデータフレームに対応する辞書ができました。例えば{“年月日”: “date”}のように、{元の列名: 新しい列名}になっています。

あと、ここで使用する列名(変更後)のリストも作成しておきます。

use_cols = ["date", "rain_amount", "rain_existence"]

これらを使ってrenameと列情報の限定をします。

tokyo_df = tokyo_df.rename(columns=rename_dict)
tokyo_df = tokyo_df[use_cols]
tokyo_df.head()
date rain_amount rain_existence
0 1989/10/1 0.0 1
1 1989/10/2 0.0 0
2 1989/10/3 0.0 0
3 1989/10/4 34.0 0
4 1989/10/5 0.0 1

これで必要な情報を絞れました。次に、グラフのラベルに使用するための年度ごとの情報をデータフレームに持たせます。
関数を準備して、pandas.Series.apply()を使用して、年月日の要素から年をとってくる方式にしました。
事前に、年月日の列を文字列からdatetime型に変換しています。

def return_year(cdatetime):
return str(cdatetime.year)
tokyo_df["date"] = pd.to_datetime(tokyo_df["date"])
tokyo_df["year"] = tokyo_df["date"].apply(return_year)

最後に、体育の日の情報だけを抽出します。事前に体育の日のリストは作っていたので、isin()を使用すればOKです。

tokyo_df_main = tokyo_df[tokyo_df["date"].isin(sports_day_list)]
tokyo_df_main
date rain_amount rain_existence year
9 1989-10-10 0.0 1 1989
30 1990-10-10 0.0 1 1990
51 1991-10-10 11.5 0 1991
72 1992-10-10 0.0 1 1992
93 1993-10-10 0.0 1 1993
tokyo_df_main
date rain_amount rain_existence year
9 1989-10-10 0.0 1 1989
30 1990-10-10 0.0 1 1990
51 1991-10-10 11.5 0 1991
72 1992-10-10 0.0 1 1992
93 1993-10-10 0.0 1 1993
114 1994-10-10 0.0 0 1994
135 1995-10-10 0.0 1 1995
156 1996-10-10 0.0 1 1996
177 1997-10-10 0.0 0 1997
198 1998-10-10 0.0 0 1998
219 1999-10-10 0.0 1 1999
239 2000-10-09 19.5 0 2000
259 2001-10-08 11.5 0 2001
286 2002-10-14 0.0 1 2002
306 2003-10-13 60.0 0 2003
325 2004-10-11 3.5 0 2004
345 2005-10-10 18.5 0 2005
365 2006-10-09 0.0 1 2006
385 2007-10-08 4.0 0 2007
411 2008-10-13 0.0 1 2008
431 2009-10-12 0.0 1 2009
451 2010-10-11 0.0 1 2010
471 2011-10-10 6.0 0 2011
490 2012-10-08 0.0 1 2012
517 2013-10-14 0.0 0 2013
537 2014-10-13 49.0 0 2014
557 2015-10-12 0.0 1 2015
576 2016-10-10 0.0 0 2016
596 2017-10-09 0.0 0 2017

データフレームが準備できたので、描画も試しておきます。

fig, ax = plt.subplots(figsize=(8, 3))
main_df = tokyo_df_main
data_points = np.arange(len(main_df))
ax.bar(data_points,main_df["rain_amount"])
ax.set_xticks(data_points)
ax.set_xticklabels(main_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title("Tokyo",fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")

体育の日の東京の降雨量の年ごとの棒グラフ

これで1つの都市に対するグラフが作成できました。

工夫としては、横軸をこちらで指定したことぐらいなのですが、実は特に必要ありません。

axes.bar()の第一引数にtarget_df[“year”]を指定してあげれば問題ありません。その場合軸が少しずれますが、そこは調整すれば大丈夫です。

とはいえ、今回はこのコードで実施したので、このまま話を続けていきます。

「現象なし情報」を使用するかの判断

次に、冒頭で少し触れた「現象なし情報」についての判断を行いました。

これは、「降雨」という現象があったかどうかを示すフラグのようでした。データを見る限り、1なら「現象なし」、0なら「現象あり」のようです。

0mmの降雨量でも「現象あり」になっているデータがあったのは、0~0.9mmの雨は0mm扱い、というところにありそうです。

なので、この「現象なし」情報が使えれば、少し話が楽になりそうですね。では、これが0か1かで単純に棒グラフを作成してみましょう。

fig, ax = plt.subplots(figsize=(8, 3))
main_df = tokyo_df_main
data_points = np.arange(len(main_df))
ax.bar(data_points,main_df["rain_existence"])
ax.set_xticks(data_points)
ax.set_xticklabels(main_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title("Tokyo",fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")

東京について、「現象なし」情報を年ごとにプロットした図

現象なし情報が1になった、つまり、雨が一切降っていないとされるデータは14個でした。全29データのうちのおよそ半分です。

存外2年に1回は雨が降っている、ということになりますね。

少し悩みましたが、今回は、「雨が降っている印象」に対する調査ということで、0mm扱いにされるような雨はカウントしない方向にしました。

恣意的にデータを扱っているようになりますが、本分析における前提として提示しているので、分析の方針としては間違っていない…と信じたいです。

3都市に対するデータ処理

ここまでで一つの都市に対しての前処理と作図の方法を確認しました。また、「現象なし」情報は使わないことに決めました。

これを踏まえて、まとめてデータを処理するコードを書いていきます。

ここでは3都市に対するグラフの作成を行いますが、前処理の流れは共通なので、先にすべての都市の情報をもつデータフレームを作成します。

まずは事前にちょっとした準備です。

city_list= ["Sapporo", "Tokyo", "Yokohama", "Nagoya", "Osaka", "Fukuoka"]
use_cols = ["date", "rain_amount"]

次に、前処理を行う関数を作成しておきます。

def preprocess_func(city_name, rename_dict, use_cols):
filepath = os.path.join(root_dir, city_name.lower() + ".csv")
base_df = pd.read_csv(filepath, skiprows = [0,1,2,4,5], encoding="cp932")
renamed_df = base_df.rename(columns=rename_dict)
main_df = renamed_df[use_cols]
main_df["date"] = pd.to_datetime(main_df["date"])
return main_df

初めの10行ほど確認しておきましょう。

main_df.head(10)
date Sapporo Tokyo Yokohama Nagoya Osaka Fukuoka
0 1989-10-01 0.0 0.0 0.0 0.0 0.0 0.0
1 1989-10-02 0.0 0.0 0.0 0.0 0.0 0.0
2 1989-10-03 0.0 0.0 3.5 1.0 6.5 0.0
3 1989-10-04 0.0 34.0 37.0 0.0 0.0 0.0
4 1989-10-05 1.0 0.0 0.0 0.0 0.0 0.0
5 1989-10-06 1.5 5.0 4.5 0.0 0.0 0.0
6 1989-10-07 0.0 13.0 9.0 9.5 0.0 0.0
7 1989-10-08 1.0 0.5 1.0 0.0 0.5 1.5
8 1989-10-09 1.5 0.0 0.0 0.0 0.0 0.0
9 1989-10-10 0.0 0.0 0.0 0.0 1.0 2.0

あとは、年の情報、体育の日かどうかの情報を追加しておきます。

main_df["year"] = main_df["date"].apply(return_year)
main_df["is_sports_day"] = main_df["date"].isin(sports_day_list).apply(int)

体育の日の抽出結果がこちらです。

main_df[main_df["is_sports_day"] == 1 ]
date Sapporo Tokyo Yokohama Nagoya Osaka Fukuoka year is_sports_day
9 1989-10-10 0.0 0.0 0.0 0.0 1.0 2.0 1989 1
30 1990-10-10 0.0 0.0 0.0 0.0 0.0 0.0 1990 1
51 1991-10-10 0.0 11.5 19.5 2.0 8.5 0.0 1991 1
72 1992-10-10 20.5 0.0 0.0 0.0 0.0 0.0 1992 1
93 1993-10-10 5.5 0.0 0.0 0.0 0.0 0.0 1993 1
114 1994-10-10 0.0 0.0 0.0 1.0 1.5 0.0 1994 1
135 1995-10-10 0.0 0.0 0.0 0.0 0.0 0.0 1995 1
156 1996-10-10 0.0 0.0 0.0 0.0 0.0 0.0 1996 1
177 1997-10-10 4.0 0.0 0.0 0.0 0.0 0.0 1997 1
198 1998-10-10 0.0 0.0 0.0 0.0 0.0 0.0 1998 1
219 1999-10-10 0.0 0.0 0.0 0.0 0.0 0.0 1999 1
239 2000-10-09 0.0 19.5 24.0 7.0 45.5 11.5 2000 1
259 2001-10-08 0.0 11.5 9.0 0.0 0.0 0.5 2001 1
286 2002-10-14 0.0 0.0 0.0 0.0 0.0 0.0 2002 1
306 2003-10-13 1.5 60.0 29.5 41.0 34.0 7.0 2003 1
325 2004-10-11 11.0 3.5 5.5 0.0 0.0 0.0 2004 1
345 2005-10-10 0.5 18.5 19.0 2.0 7.0 0.0 2005 1
365 2006-10-09 0.0 0.0 0.0 0.0 0.0 0.0 2006 1
385 2007-10-08 7.0 4.0 1.0 8.5 4.5 37.5 2007 1
411 2008-10-13 0.0 0.0 0.0 0.0 0.0 0.0 2008 1
431 2009-10-12 0.0 0.0 0.0 0.0 0.0 0.0 2009 1
451 2010-10-11 0.0 0.0 0.0 0.0 0.0 0.0 2010 1
471 2011-10-10 9.5 6.0 8.5 0.0 0.0 0.0 2011 1
490 2012-10-08 0.0 0.0 0.0 0.0 0.0 0.0 2012 1
517 2013-10-14 0.0 0.0 0.0 0.0 0.0 0.0 2013 1
537 2014-10-13 0.0 49.0 46.0 60.0 71.0 105.0 2014 1
557 2015-10-12 0.0 0.0 0.0 0.0 0.0 1.5 2015 1
576 2016-10-10 0.0 0.0 0.0 0.0 0.0 0.0 2016 1
596 2017-10-09 0.0 0.0 0.0 0.0 0.0 0.0 2017 1

見たところきちんと抽出できていそうです。
このデータフレームをsports_day_dfすることにします。

sports_day_df = main_df[main_df["is_sports_day"] == 1 ]

3都市に対するプロット

準備ができたのでまずは東京、名古屋、大阪の3都市に対するプロットを作成します。

まずはシンプルなプロット結果です。

# 3都市での雨量のプロット
three_cities = ["Tokyo", "Nagoya", "Osaka"]
fig, axes = plt.subplots(nrows=3, ncols=1, figsize=(8, 9), sharey = False)
target_df = sports_day_df
for city, ax in zip(three_cities, axes.flatten()):
data_points = np.arange(len(target_df))
ax.bar(data_points,target_df[city])
ax.set_xticks(data_points)
ax.set_xticklabels(target_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title(city,fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")
plt.subplots_adjust(hspace=0.7)

sharey = Falseにしましたが、元々これらの3都市の最大雨量が近かったおかげで縦軸が揃っていますね。

このグラフに対する考察は、検証の方で記載した通りなのでここでは省きます。

6都市に対するデータ処理

あとは同じことの繰り返しです。
まずシンプルにグラフを作成します。

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(16, 9), sharey = False)
target_df = sports_day_df
for city, ax in zip(city_list, axes.flatten()):
data_points = np.arange(len(target_df))
ax.bar(data_points,target_df[city])
ax.set_xticks(data_points)
ax.set_xticklabels(target_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title(city,fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")
plt.subplots_adjust(hspace=0.7)

シンプルに6都市の降雨量を描画したグラフ。ただし、列ごとにデータが埋まっている。

これは、今回考察の方では採用しなかったグラフです。その理由は、グラフが描画される順番です。

左上段、右上段、左中段、右中段…という順番で描画されています。

別にこれでも構わないのですが、今回自分でグラフを眺めていて、グラフを比較するときは主に同じ年度を見ていることに気づいたことが、このグラフを採用しなかった理由です。

天気は地理的に近い方が似ていると考え、比較の際になるべくグラデーションになっているようにした方が好ましいと考えました。

そこで、下記のようなコードにすることで、グラフの描画順序を変更しました。

# 見辛いので北側から左に並べなおす
fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(16, 9), sharey = False)
target_df = sports_day_df
for i, city in enumerate(city_list):
ax = axes[i % 3, i//3]
data_points = np.arange(len(target_df))
ax.bar(data_points,target_df[city])
ax.set_xticks(data_points)
ax.set_xticklabels(target_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title(city,fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")
plt.subplots_adjust(hspace=0.6)

縦軸を揃えた6都市の雨量の比較

これでグラフは縦に比較しやすくなっています。最後に、sharey=Trueを入れて、縦軸が共通になっているグラフを作成します。

# sharey = Trueを入れる
fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(16, 9), sharey = True)
target_df = sports_day_df
for i, city in enumerate(city_list):
ax = axes[i % 3, i//3]
data_points = np.arange(len(target_df))
ax.bar(data_points,target_df[city])
ax.set_xticks(data_points)
ax.set_xticklabels(target_df["year"], rotation=50, fontsize=10)
ax.set_xlim([-1, 29])
ax.set_title(city,fontsize=14)
ax.set(xlabel="year", ylabel="rain_amount")
plt.subplots_adjust(hspace=0.7)

縦軸を揃えた6都市の雨量のグラフ

これで降雨量の絶対値の比較ができるようになりました。

1mm以上の雨が降った回数のカウント

続いて、体育の日は他の日よりも雨が降っていないかどうか検証するため、日毎の雨の有無を集計していきます。

この際、条件として、「体育の日」を基準とした差の日数の情報が必要でした。

また、この後で「特異日である10月14日は他の日より雨が少ないか」ということも検証します。

それらを踏まえて、データフレームに情報を足していきます。

まずは、便利な関数を準備しました。

def return_date(each_datetime):
return each_datetime.strftime("%m/%d")
def return_diff_from_sportday(each_datetime, sports_day_dict):
focus_year = return_year(each_datetime)
sports_day_for_the_year = sports_day_dict[focus_year]
date_diff = each_datetime - sports_day_for_the_year
return date_diff.days
def add_israin(c_df, city):
c_series = c_df[city] > 0
c_series = c_series.apply(int)
c_df["is_rain_"+city] = c_series
return c_df

また、これも後で使うために、年度ごとに体育の日を返す辞書を用意しておきます。

sports_day_dict = {return_year(each) : each for each in sports_day_list}

一旦下準備はこれで完了です。

体育の日とその他の日の雨の頻度の検証

それでは、用意した関数を使って情報を追加していきます。

main_df["diff_from_sportsday"] = main_df["date"].apply(return_diff_from_sportday, args=(sports_day_dict,))

for city in city_list:
main_df = add_israin(main_df, city)

体育の日から何日の位置の情報かと、1mm以上の雨が降ったかどうかの情報を追加しました。

これを使って、各都市で体育の日に何回雨が降ったかも確認して見ましょう。

israin_colname_list = ["is_rain_"+city for city in city_list]
main_df[main_df.date.isin(sports_day_list)][israin_colname_list].sum(axis=0).reset_index().rename(columns={0:"counts"})
index counts
0 is_rain_Sapporo 8
1 is_rain_Tokyo 9
2 is_rain_Yokohama 9
3 is_rain_Nagoya 7
4 is_rain_Osaka 8
5 is_rain_Fukuoka 7

この表を使うと、雨が降った頻度だけを知りたい場合、グラフを使うより分かりやすいですね。

話が逸れましたが本題に戻ります。雨が降った頻度を数えるにはgroupbyとsumを利用します。

今回は体育の日±7日のデータだけに注目します。

diff_from_sports_day_sum = main_df.groupby(by="diff_from_sportsday").sum()
count_df_for_sports_day_diff = diff_from_sports_day_sum[(diff_from_sports_day_sum.index <= 7) & (diff_from_sports_day_sum.index >= -7)]

そして、これを描画すると下記のようになります。

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(16, 9), sharey = True)
target_df = count_df_for_sports_day_diff
for i,city in enumerate(city_list):
count_col = "is_rain_" + city
ax = axes[i % 3, i//3]
data_points = np.arange(-7,8)
ax.bar(data_points,target_df[count_col])
ax.set_xticks(data_points)
ax.set_xticklabels(target_df.index, rotation=0, fontsize=9)
ax.set_xlim([-8, 8])
ax.set_title(city,fontsize=14)
ax.set(xlabel="day_difference", ylabel="counts")
plt.subplots_adjust(hspace=0.7)
plt.show()

体育の日を基準にした雨の発生頻度のグラフ

やっていること自体は非常に単純です。

特異日とされる10月14日は他の日よりも雨が少ないかどうかの検証

最後に、10月14日は雨が少ないかどうかの検証です。

まずはデータフレームに月と日からなる情報を追加します。これを追加する理由は、後でgroupbyを使用するためです。

main_df["month_day"] = main_df["date"].apply(return_date)

準備ができたのでgroupbyを取ります。

sum_by_day_df = main_df.groupby(by="month_day").sum()
sum_by_day_df.head()
Sapporo Tokyo Yokohama Nagoya Osaka Fukuoka is_sports_day diff_from_sportsday is_rain_Sapporo is_rain_Tokyo is_rain_Yokohama is_rain_Nagoya is_rain_Osaka is_rain_Fukuoka
month_day
10/01 122.0 329.0 401.0 322.5 238.0 122.5 0 -275 12 18 18 13 12 8
10/02 143.0 176.5 183.0 108.0 94.5 227.5 0 -246 14 11 11 10 10 12
10/03 106.0 116.0 140.5 66.5 72.5 35.0 0 -217 13 10 10 12 8 7
10/04 147.0 126.5 101.0 67.5 100.5 30.5 0 -188 13 10 12 9 7 5
10/05 73.0 399.0 426.5 176.5 96.0 86.5 0 -159 14 13 14 12 13 9

描画は特異日の±7日に絞りたいので、抽出用のリストを作成します。

days_list_for_specific_day = ["10/" + str(num).zfill(2) for num in range(7, 22)]

最後に、このリストを使ってデータを抽出すればデータの準備は完了です。

days_df_for_specific_day = sum_by_day_df[sum_by_day_df.index.isin(days_list_for_specific_day)]

このデータを使って、特異日を中心に前後7日間の都市ごとのデータをプロットしましょう。

fig, axes = plt.subplots(nrows=3, ncols=2, figsize=(16, 9), sharey = True)

for i,city in enumerate(city_list):
count_col = "is_rain_" + city
ax = axes[i % 3, i//3]
data_points = np.arange(-7,8)
ax.bar(data_points,days_df_for_specific_day[count_col])
ax.set_xticks(data_points)
ax.set_xticklabels(days_df_for_specific_day.index, rotation=50, fontsize=10)
ax.set_xlim([-8, 8])
ax.set_title(city,fontsize=14)
ax.set(xlabel="date", ylabel="counts")
plt.subplots_adjust(hspace=0.7)
plt.show()

日付ごとの雨の有無のカウントグラフ

以上で、検証に使用したコードの紹介は終わりです。

まとめ

非常に長くなってしまいましたが、体育の日の天気に関する検証を行なった際に使用したコードを紹介しました。

レベルとしては本当にPythonとPandasの基本を勉強して、実際に使ってみたいというぐらいだと思います。

色々やっていますが、分からないことがあったら都度都度調べてなんとかかんとかやっている状態です。

まだまだ上のレベルがあると思うので、楽しみながら少しずつ勉強していきたいと思います。

作成したグラフを使っての検証編も再掲しておきます。

体育の日は雨が降らないという印象が正しいかデータから真面目に検証してみた[検証編]
体育の日は天気が良いかどうか、データから真面目に検証してみました。この調査では、天気の良さを雨が降っていないかで評価し、データは気象庁のサイトから取得し、データの加工やグラフの作成をPythonで行なっています。

結局、多くの人にとって必要なのは「得られたデータから何が言えるか」、というところなので、人に伝える部分では必要なことを適切に、ということが重要になってくるのだろうなぁと改めて感じました。

学ぶことはまだまだ多そうです。

参考資料

気象庁ホームページ-データ取得画面 https://www.data.jma.go.jp/gmd/risk/obsdl/index.php