Python:Matplotlibでグラフの作成(Google Colaboratory)

秋田県の豪雨で友人が床上浸水となりました.国土交通省の「川の防災情報」というサイトで河川の水位と雨量を公開していましたので,何があったのか,可視化してみました.

目標とするグラフ

https://www.river.go.jp/kawabou/mb/tm?zm=12&clat=39.6147222&clon=140.28004591520997&fld=0&mapType=0&viewGrpStg=0&viewRd=1&viewRW=1&viewRiver=1&viewPoint=1&ext=0&ofcCd=21043&itmkndCd=4&obsCd=3

このサイトはAPIが有料のようで,公開されているのは2日分くらいです.

公開されているデータ

Excelにコピペすると水位の矢印が2段になってたり,上が新しい時刻だったり,そこはVBAで形式を以下のように整えました.

データ

ここから随時データを取得して追加して,そのたびにグラフを描いていましたので,Excelでは毎回の操作がとても面倒です.
Pythonなら,プログラムを実行すればよいので,グラフの描き直しがとても楽にできました.

以下の項目をプログラムにしました.

  1. 上部にはデータソースや場所の情報を置いておきたかったが,7行目まではDataFrameに読み込みたくない.

  2. 上部の記述の分も列に含んでしまうので,F列より右は削除

  3. 「欠測」のある行は削除

  4. 「欠測」のため,時間雨量は文字列になってしまっており,数値に変換する必要がある.

  5. 雨量をグラフで,水位を折れ線グラフにしたい.

  6. 水位を第2軸にしたい(本当は水位を第1軸にしたいのだけれど,折れ線グラフを前に表示するために後から表示するため)

  7. 氾濫危険水位(8.1m)と氾濫注意水位(6.3m)を明示したい.

  8. 強い雨の目安(10mm)を明示したい.

  9. 凡例を表示

  10. 横軸ラベルが重ならないように

  11. グラフ内にコメントを入れたい

.(1)グラフに日本語を表示させるためのライブラリをインストール
Colaboratoryは,Googleのサーバ上の仮想環境なので,毎回インストール作業が必要

!pip install japanize_matplotlib

Successfullyと表示されれば,インストール成功です.

(2)データの読み込み
ファイルは,UTF-08CSV形式で保存
上部のメモの部分を避けて読み込む.

import pandas as pd

df = pd.read_csv("/content/秋田豪雨20230716Python.csv" , skiprows=list(range(7)))
df.head()
データを読み込んだ結果

skiprowsには読み込みをスキップする行数をリストで記述します.
例)
skiprows=[1,2,3]
1行目と2行目と3行目をスキップする.
ここでは、1行目から7行目まで全部スキップするので,rangeで数値列を生成し,これをリストに変換しています.

(3)DataFarmeの不要部分の削除

df = df.drop('Unnamed: 5', axis=1)
df = df.drop('Unnamed: 6', axis=1)
df = df.drop('Unnamed: 7', axis=1)
df = df.drop('Unnamed: 8', axis=1)
df = df[~df['時間雨量[mm]'].str.contains('欠測')]
df.head()
不要部分を削除した結果

dropで列を削除します.ここでは列名を指定して削除しました.
列方向の削除なので「axis=1」を記述します.

時間雨量の列に「欠測」を含む行を削除します.
df['時間雨量[mm]'].str.contains('欠測')
は,この列の各行の全部について文字列処理をするというアクセサstrに続き, Stringのメソッドcontainsで「欠測」という文字列があるかどうか判定しています.文字列を含むときはTrueになります.
ここではFalseの行を残したいので,論理否定「~」で結果を反転させ,含まない行が残ります.

(4)文字列を日付と数値に変換
日付と時刻は文字列であり,これを2つ合わせて日付型に変換します.
雨量は「欠測」があったため,列全体が文字列になってしまっており,数値に変換します.

X_dt = pd.to_datetime(df['日付'] + " " +df['時刻']) 
df['DateTime'] = X_dt
df['rain'] = pd.to_numeric(df['時間雨量[mm]'],errors='coerce')
df.head()
変換した結果のDataFrame

日付と時刻は文字列なので、文字列の加算をし,(間に半角空白が1文字ないとエラーになってしまい,それも追加)これを日付型に変換します.

(5)氾濫注意水位以上のDataFrameをとりだす
注意水位以上の部分を色を変えて表示するため,その部分を抜き出します.

limit1 = 8.1
limit2 = 6.6
limit_rain = 10
X_dt_str =df['DateTime'] .dt.strftime('%m/%d %H時')
df_red = df[df['水位[m]'] >= limit2]

limit1 : 氾濫危険水位
limit2 : 氾濫注意水位
limit_rain : これ以上は強い雨

(6)グラフを描く
それではグラフを描きましょう.


import matplotlib.pyplot as plt
import japanize_matplotlib

fig , ax = plt.subplots(figsize=(8,4))

ax2 = ax.twinx()
ax.bar(df['DateTime']  , df['rain'] , color='blue' , label='降水量(mm)' , width=0.03)
ax2.plot(df['DateTime'] , df['水位[m]'] , c='green' , label='安全な水位(m)')
ax2.plot(df_red['DateTime'] , df_red['水位[m]'] , c='red', label='危険な水位(m)') 
ax2.hlines([limit1 , limit2] , X_dt[1] , X_dt[len(X_dt)-1] , colors=['red' , 'green'] ,linestyle='dashed')
ax.hlines([limit_rain] , X_dt[1] , X_dt[len(X_dt)-1] , colors=['blue'] ,linestyle='dotted')
ax.set_xticks([X_dt[i] for i in range(1,len(X_dt),24)] ,
			[X_dt_str[i] for i in range(1,len(X_dt),24)])
ax.set_title('雄物川水系椿川(秋田県)の水位と降雨量')
ax.set_ylabel('1時間あたりの降水量(mm)')
ax2.set_ylabel('水位(m)')
line1 , label1 = ax.get_legend_handles_labels()
line2 , label2 = ax2.get_legend_handles_labels()
plt.legend(handles=line1+line2 , labels=label1+label2)
ax.text(df.iloc[45]['DateTime'], 10.3, "これ以上は強い雨")
ax2.text(df.iloc[1]['DateTime'], 6.7, "氾濫注意水位")
ax2.text(df.iloc[1]['DateTime'], 8.2, "氾濫危険水位")
ax2.text(df.iloc[68]['DateTime'], 4, "また強い雨が降って\n水位が上昇傾向\nこれ以上降らないで!")
plt.show()

ax2 = ax.twinx()
x軸を共通にしたax2を生成する.これで第2軸の指定ができます.
第2軸にしたときは,凡例がそれぞれになってしまうので,ラベルとハンドラを取り出して合わせて表示します.
line1 , label1 = ax.get_legend_handles_labels()
line2 , label2 = ax2.get_legend_handles_labels()
plt.legend(handles=line1+line2 , labels=label1+label2)
line1 , line2 , label1 , label2はそれぞれリストなので、加算すると,要素をくっつけることができます.
label2には「危険な水位」と「安全な水位」という2つの要素が含まれているので,凡例は全部で3つになります.
グラフ中に記入する文字列はx軸・y軸の値で位置を指定します.

7月19日午前8時 現在

また雨が降って,じわじわと水位が上がってきています.
秋田の皆様,どうかご無事で