気象データのメモ(その1)「最新の気象データ」を取得する

Weather

気象庁から様々な気象データが発表されています。そんなデータを取得したり、表示したりしています。ここでは、「最新の気象データ」を取得する方法についてメモしてみました。

気象庁ホームページで公開されている情報の取得や利用は、気象庁の利用規約等に従って、適正に行いましょう。

「最新の気象データ」

気象庁ホームページの「最新の気象データ」CSVダウンロードのサイトでは、全国の気象官署やアメダス観測所で観測された降水量や風速、気温などの気象データの最新情報をCSV形式のファイルでダウンロードすることができます。

出典:気象庁ホームページ。画像をクリックすると別ウィンドウで気象庁の該当ページを開きます。

例えば「最高気温」の項を見ると、データ内容の詳細を知ることができます。

出典:気象庁ホームページ。画像をクリックすると別ウィンドウで気象庁の該当ページを開きます。

これらのデータのうち、前日の最高気温、最低気温のCSVファイルを1日1回取得し、DBに保存する仕組みを作ってみました。

データの取得

前日の最高気温、最低気温のCSVファイルは、翌朝5時頃に更新されるので、その時刻以降に、指定されたURLからCSVファイルを取得します。URLは(先のダウンロードサイトに記載されています)次の通りです。

最高気温:https://www.data.jma.go.jp/obd/stats/data/mdrr/tem_rct/alltable/mxtemsadext01.csv
最低気温:https://www.data.jma.go.jp/obd/stats/data/mdrr/tem_rct/alltable/mntemsadext01.csv

最高気温と最低気温の違いは、ファイル名の最初が「mx」(最高気温)か、「mn」(最低気温)かの違い。ファイル名の最後の「01」が1日前、つまり前日を意味し、例えば3日前のデータを取得したいのであれば、ここを「03」に変更します。最大7日前のデータまで遡及することが可能。

このURLから、最高気温、最低気温のデータをDataFrameに取り込み、保存します。

import os
import datetime
import pandas as pd

# 取得先のURL
url_mx = 'https://www.data.jma.go.jp/obd/stats/data/mdrr/tem_rct/alltable/mxtemsadext01.csv'
url_mn = 'https://www.data.jma.go.jp/obd/stats/data/mdrr/tem_rct/alltable/mntemsadext01.csv'

# 保存先のディレクトリ名(あらかじめ作成)
dirname = 'temperature'

# 保存するファイル名
date_no = (datetime.date.today() - datetime.timedelta(days=1)).strftime('%Y%m%d')
filename_mx = 'maxtemp_' + date_no + '.csv'
filename_mn = 'mintemp_' + date_no + '.csv' 

# データ読み込み
data_mx = pd.read_csv(url_mx, encoding='SHIFT_JIS')
data_mn = pd.read_csv(url_mn, encoding='SHIFT_JIS')

# データ保存
data_mx.to_csv(os.path.join(dirname, filename_mx), index=False)
data_mn.to_csv(os.path.join(dirname, filename_mn), index=False)

temperature フォルダに最高気温/最低気温それぞれを maxtemp_20211128.csv, mintemp_20211128.csv のような名前で保存しました。ファイル名の日付は、今日の日付から1日引いて、データの日付=昨日の日付としています。
保存の際に、0, 1, 2, ・・・・ の連番のインデックスは不要なので、index=False としました。

取得されたデータを詳細に見てみます。4列目までは、観測地点のデータです。

print(data_mx.iloc[:, :4])
    観測所番号   都道府県             地点            国際地点番号
0      11001  北海道宗谷地方   宗谷岬(ソウヤミサキ)      NaN
1      11016  北海道宗谷地方   稚内(ワッカナイ)        47401.0
2      11046  北海道宗谷地方   礼文(レブン)              NaN
3      11061  北海道宗谷地方   声問(コエトイ)            NaN
4      11076  北海道宗谷地方   浜鬼志別(ハマオニシベツ)  NaN
..       ...      ...            ...      ...
915    94062      沖縄県       西表島(イリオモテジマ)  47917.0
916    94081      沖縄県       石垣島(イシガキジマ)    47918.0
917    94086      沖縄県       盛山(モリヤマ)            NaN
918    94101      沖縄県       大原(オオハラ)            NaN
919    94116      沖縄県       波照間(ハテルマ)          NaN

[920 rows x 4 columns]

「地点」には漢字の名称と、カッコ内にフリガナが記載されていましたが、フリガナは長くなるのでカットしました。具体的には、3列目の各要素について、”(” の直前までの文字列を取得するlambda式を適用しました。

data_mx.iloc[:, 2].apply(lambda x: x[:x.find('(')])

9列目までは観測日時です。

print(data_mx.iloc[:, 4:9])
     観測時刻(年)  観測時刻(月)  観測時刻(日)  観測時刻(時)  観測時刻(分)
0       2021           11            28           NaN           NaN
1       2021           11            28           NaN           NaN
2       2021           11            28           NaN           NaN
3       2021           11            28           NaN           NaN
4       2021           11            28           NaN           NaN
..       ...          ...           ...           ...           ...
915     2021           11            28           NaN           NaN
916     2021           11            28           NaN           NaN
917     2021           11            28           NaN           NaN
918     2021           11            28           NaN           NaN
919     2021           11            28           NaN           NaN

[920 rows x 5 columns]

前日のデータを取得しているので、年月日は全て同じ(昨日)、また、時分のデータはありません。DBには、単純に年・月・日を文字列として結合して登録することにします。

16列目までが気温のデータです。

print(data_mx.iloc[:, 9:16])
(※は「昨日の最高気温」)
	※(℃) ※の品質情報 ※起時(時)※起時(分)※起時の品質情報  平年差(℃) 前日差(℃)
0    	 6.6  	     8  	12  	    47  	   8  	       3.6         1.6
1    	 7.3  	     8  	23  	    55  	   8  	       4.2         1.8
2    	 7.2  	     8  	23  	    58  	   8  	       4.6         2.4
3    	 7.0  	     8  	12  	    25  	   8  	       4.4         1.6
4    	 7.1  	     8  	11  	    41  	   8  	       5.1         1.9
..   	 ... 	     ..  	..  	    .. 	   	   ..  	       ...         ...
915  	22.7  	     8  	18  	    56  	   8 	      -1.5         0.1
916  	22.6  	     8  	18  	    57  	   8 	      -2.3        -0.3
917  	22.6  	     8  	22  	    30  	   8 	      -2.3        -0.1
918  	22.6  	     8  	18  	    38  	   8 	      -1.9         0.3
919  	22.5  	     8  	12  	    49  	   8 	      -2.3         0.4

[920 rows x 7 columns]

「品質情報」の説明は、こちら(気象庁の説明ページにリンクします)を参照。
「起時」は、最高気温を記録した時・分です。こちらもDBには単純に、時・分を文字列として結合して登録することにします。

これらの最高気温、最低気温のデータを結合して、DBに保存するDataFrameを作りました。国際地点番号と16列目以降の統計値については、割愛しました。

# 最高気温データの整理
data_mx = data_mx.iloc[:,:16]
data_mx.dropna(subset=[data_mx.columns[11]], inplace=True)
data_mx.iloc[:, 2] = data_mx.iloc[:, 2].apply(lambda x: x[:x.find('(')])
data_mx.iloc[:, 4] = data_mx.iloc[:, 4].astype(str) + '-' + data_mx.iloc[:, 5].astype(str) + '-' + data_mx.iloc[:, 6].astype(str)
data_mx.iloc[:,11] = data_mx.iloc[:,11].astype(int).astype(str) + ':' + data_mx.iloc[:,12].astype(int).astype(str)
data_mx = data_mx.drop(columns = data_mx.columns[[3,5,6,7,8,12]])
data_mx.columns = ['no', 'pref', 'name', 'date', 'mx', 'mx_c', 'mxt', 'mxt_c', 'mx_y', 'mx_d']

# 最低気温データの整理
data_mn = data_mn.iloc[:,:16]
data_mn.dropna(subset=[data_mn.columns[11]], inplace=True)
data_mn.iloc[:, 2] = data_mn.iloc[:, 2].apply(lambda x: x[:x.find('(')])
data_mn.iloc[:, 4] = data_mn.iloc[:, 4].astype(str) + '-' + data_mn.iloc[:, 5].astype(str) + '-' + data_mn.iloc[:, 6].astype(str) 
data_mn.iloc[:,11] = data_mn.iloc[:,11].astype(int).astype(str) + ':' + data_mn.iloc[:,12].astype(int).astype(str)
data_mn = data_mn.drop(columns = data_mn.columns[[3,5,6,7,8,12]])
data_mn.columns = ['no', 'pref', 'name', 'date', 'mn', 'mn_c', 'mnt', 'mnt_c', 'mn_y', 'mn_d']

# データの結合
data = pd.merge(data_mx, data_mn, on = ['no','pref','name','date'])
print(data)
	no	 pref	     name	date	  mx  mx_c   mxt  mxt_c  mx_y  mx_d     mn  mn_c   mnt  mnt_c  mn_y  mn_d 
0    11001  北海道宗谷地方   宗谷岬  2021-11-28   6.6   8   12:47    8    3.6    1.6    1.8   8   19:32    8    3.6   -0.3
1    11016  北海道宗谷地方    稚内   2021-11-28   7.3   8   23:55    8    4.2    1.8    3.9   8    5:25    8    5.6    1.6
2    11046  北海道宗谷地方    礼文   2021-11-28   7.2   8   23:58    8    4.6    2.4    2.3   8    6:23    8    4.5    1.0
3    11061  北海道宗谷地方    声問   2021-11-28   7.0   8   12:25    8    4.4    1.6    0.0   8   18:47    8    3.5   -2.3
4    11076  北海道宗谷地方  浜鬼志別 2021-11-28   7.1   8   11:41    8    5.1    1.9   -1.2   8    6:41    8    3.0   -3.8
..     ...       ...          ...      ...        ...  ..    ...    ..   ...    ...    ...   ..    ...    ..  ...   ...
915  94062      沖縄県       西表島  2021-11-28  22.7   8   18:56    8   -1.5    0.1   19.5   8    1:36    8   -0.4    0.8
916  94081      沖縄県       石垣島  2021-11-28  22.6   8   18:57    8   -2.3   -0.3   19.6   8    0:49    8   -0.7    0.5
917  94086      沖縄県        盛山   2021-11-28  22.6   8   22:30    8   -2.3   -0.1   19.7   8    0:47    8   -0.4    0.7
918  94101      沖縄県        大原   2021-11-28  22.6   8   18:38    8   -1.9    0.3   19.4   8    4:15    8   -0.1    0.5
919  94116      沖縄県       波照間  2021-11-28  22.5   8   12:49    8   -2.3    0.4   19.5   8    5:28    8   -1.0    0.6

[920 rows x 16 columns]

列名の _c は項目の品質情報、_y は平年差、_dは前日差としました。

(補足)

最高(最低)気温の起時は、11列目(時)と12列目(分)を結合して作成しました。例えば、1時2分の場合、文字列は “1:2” となりますが、DBには “01:02:00″ として問題なく挿入することが出来ました。また、24時00分の場合も、”24:0” という文字列が “24:00:00” として正しく挿入されました。

しかし、一つでも起時が欠測の行が含まれていると(例えば、気温データそのものが欠測の場合に起こりえます)エラーとなるので、最初に、起時が欠測の行は削除する処理を追加しています。

data_mx.dropna(subset=[data_mx.columns[11]], inplace=True)

data_mx.columns[11] は、11列目の列名です(長くて記載するのが面倒なので)。dropna(subset=[列名]) で、指定した列が欠測となる行が削除されます。inplace=True とすることで、オブジェクト自体が更新されます。欠測であったときも行を削除したくない(欠測であることをDBに残したい)ときは別の方法を考える必要があります。

データベースに保存

MySQLに、データベース weatherdata を作成し、先のDataFrame の各列名をカラム名とするテーブル temperature を作成しました。具体的には、次のような構造のテーブルとしました。

このテーブルに、Pandas の to_sql メソッドを使って、DataFrameのデータを保存しました。データベースの設定は「Pythonのメモ(その1)SQLAlckemy を使う」の記載を踏襲しました。

con = 'mysql://namae:Password-SQL-namae1@localhost/weatherdata'
data.to_sql('temperature', con, if_exists='append', index=False)

保存することができました。

今後の話

こんな感じのプログラムを作成して、cronを利用して毎日の気象データを取り溜めています。そんなことしなくても、気象庁のサイトへ行けば、データの検索だって、ダウンロードだってできるではないか、というご意見はごもっともです。ただの自己満足ですが、日本各地の気象の様子を手元で扱うことができるのはとても楽しい。そんなことも今後、記述していきたいと思います。

タイトルとURLをコピーしました