四国の水がめと呼ばれる早明浦ダム。1994年と2005年には貯水率ゼロを記録し、大きな渇水被害をもたらしました。しかし最近のデータを見ると、あまり雨が降っていないにもかかわらず、貯水率は90%以上を維持しています。なぜこのようなことが可能なのでしょうか?データ分析を通じて、その謎に迫ってみましょう。
国土交通省 水文水質データベース から12月25日から1月25日までのデータを取得して分析しました
データ可視化と基本情報の取得
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# データを読み込む
df = pd.read_csv('/mnt/data/sameura.dat', skiprows=9, encoding='utf-8')
header = ['date', 'time', 'rainfall', 'rainfall_attr', 'storage', 'storage_attr',
'inflow', 'inflow_attr', 'outflow', 'outflow_attr', 'storage_rate', 'storage_rate_attr']
df.columns = header
# 24:00を00:00に変換し、日付を1日進める
def fix_datetime(row):
if row['time'] == '24:00':
date = pd.to_datetime(row['date']) + pd.Timedelta(days=1)
return date.strftime('%Y/%m/%d'), '00:00'
return row['date'], row['time']
df['date'], df['time'] = zip(*df.apply(fix_datetime, axis=1))
# 日時を結合してdatetime型に変換
df['datetime'] = pd.to_datetime(df['date'] + ' ' + df['time'])
# 数値型に変換
df['storage_rate'] = pd.to_numeric(df['storage_rate'], errors='coerce')
df['inflow'] = pd.to_numeric(df['inflow'], errors='coerce')
df['outflow'] = pd.to_numeric(df['outflow'], errors='coerce')
df['rainfall'] = pd.to_numeric(df['rainfall'], errors='coerce')
# グラフのプロット設定
plt.rcParams['figure.figsize'] = [15, 15]
plt.rcParams['axes.grid'] = True
plt.rcParams['grid.alpha'] = 0.3
fig, (ax1, ax2, ax3) = plt.subplots(3, 1)
# 1. 貯水率の推移
ax1.plot(df['datetime'], df['storage_rate'], color='blue', linewidth=2)
ax1.set_title('Sameura Dam - Storage Rate', fontsize=14, pad=15)
ax1.set_ylabel('Storage Rate (%)')
ax1.set_ylim(0, max(df['storage_rate']) * 1.1)
# 2. 流入量と放流量の比較
ax2.plot(df['datetime'], df['inflow'], label='Inflow', color='green', linewidth=2)
ax2.plot(df['datetime'], df['outflow'], label='Outflow', color='red', linewidth=2)
ax2.set_title('Inflow and Outflow Comparison', fontsize=14, pad=15)
ax2.set_ylabel('Flow Rate (m³/s)')
ax2.legend()
# 3. 雨量の推移
ax3.bar(df['datetime'], df['rainfall'], color='skyblue', width=0.02, alpha=0.7)
ax3.set_title('Average Basin Rainfall', fontsize=14, pad=15)
ax3.set_ylabel('Rainfall (mm)')
# X軸の設定
for ax in [ax1, ax2, ax3]:
ax.tick_params(axis='x', rotation=45)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%d %H:%M'))
ax.xaxis.set_major_locator(mdates.HourLocator(interval=12))
plt.tight_layout()
plt.savefig('/mnt/data/sameura_analysis_en.png', dpi=300, bbox_inches='tight')
基本統計情報
貯水率 流入量 放流量 流域平均雨量
count 742.000000 742.000000 742.000000 742.000000
mean 91.561995 3.917102 14.641779 0.041779
std 6.336790 3.181232 18.765453 0.211686
min 79.500000 0.000000 0.000000 0.000000
25% 86.300000 1.112500 0.000000 0.000000
50% 92.100000 3.430000 0.000000 0.000000
75% 97.550000 6.162500 30.100000 0.000000
max 100.000000 13.380000 61.400000 3.000000
最大値記録時刻
- 貯水率の最大値: 100.00% (2024-12-26 02:00:00)
- 流入量の最大値: 13.38 m³/s (2025-01-06 12:00:00)
- 放流量の最大値: 61.40 m³/s (2025-01-06 20:00:00)
- 流域平均雨量の最大値: 3.00 mm (2025-01-06 09:00:00)
分析結果の特徴
-
貯水率の状況
- 期間中の平均貯水率は91.56%と高水準を維持
- 最小でも79.5%を記録し、安定した貯水状況を示す
- 期間中に満水状態(100%)を記録
-
流入量と放流量の特徴
- 流入量は平均3.92 m³/s、最大13.38 m³/s
- 放流量は平均14.64 m³/s、最大61.40 m³/s
- 放流量の変動が流入量より大きい傾向
-
降雨状況
- 期間中の平均降雨量は0.04 mmと少雨傾向
- 最大でも3.00 mmと比較的小規模な降雨
- 降雨は散発的で、多くの時間帯で無降雨
高貯水率の要因分析
降雨が少ないにも関わらず貯水率が高いのは、農業用水の需要が少ない時期であることと、水需要管理にあるのではないかと推察しました。
-
ダムの特性と管理方法
- 四国の水がめとして機能する多目的ダム
- 年間8.63億m³の用水を開発し、四国4県へ供給
- 洪水調節、用水の安定供給、発電などの複数機能を保持
-
現在の高い貯水率の主要因
a) 季節的要因- データ取得期間が12月下旬から1月上旬の冬季
- 農業用水など水需要が比較的少ない時期
- 降水量は少ないが、需要も少ない時期的特性
b) 水需要管理
- 過去の渇水経験(1994年、2005年に貯水率ゼロ)を活かした厳密な管理
- 段階的な取水制限システムの整備
- 早期の取水制限実施による予防的な貯水量確保
c) ダム運用の最適化
- 降雨予測と需要予測に基づく計画的な放流管理
- 複数ダムの連携による効率的な水運用
流入量と放流量の周期性について
流入量と放流量に周期性があるように見えたので調べてみました
0. データの読み込みと前処理
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import acf
# データを読み込む
df = pd.read_csv('/mnt/data/sameura.dat', skiprows=9, encoding='utf-8')
header = ['date', 'time', 'rainfall', 'rainfall_attr', 'storage', 'storage_attr',
'inflow', 'inflow_attr', 'outflow', 'outflow_attr', 'storage_rate', 'storage_rate_attr']
df.columns = header
# 数値型に変換
df['inflow'] = pd.to_numeric(df['inflow'], errors='coerce')
df['outflow'] = pd.to_numeric(df['outflow'], errors='coerce')
1. 自己相関分析
# 自己相関分析の実装
lags = 48 # 48時間(2日分)まで確認
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))
# 流入量の自己相関
acf_inflow = acf(df['inflow'], nlags=lags, fft=False)
lags_array = np.arange(len(acf_inflow))
ax1.plot(lags_array, acf_inflow, 'bo-', markersize=4)
ax1.set_title('Autocorrelation of Inflow')
ax1.set_xlabel('Lag (hours)')
ax1.set_ylabel('Correlation')
ax1.axhline(y=0, linestyle='-', color='gray')
ax1.axhline(y=1.96/np.sqrt(len(df['inflow'])), linestyle='--', color='gray')
ax1.axhline(y=-1.96/np.sqrt(len(df['inflow'])), linestyle='--', color='gray')
ax1.grid(True, alpha=0.3)
# 放流量の自己相関
acf_outflow = acf(df['outflow'], nlags=lags, fft=False)
ax2.plot(lags_array, acf_outflow, 'ro-', markersize=4)
ax2.set_title('Autocorrelation of Outflow')
ax2.set_xlabel('Lag (hours)')
ax2.set_ylabel('Correlation')
ax2.axhline(y=0, linestyle='-', color='gray')
ax2.axhline(y=1.96/np.sqrt(len(df['outflow'])), linestyle='--', color='gray')
ax2.axhline(y=-1.96/np.sqrt(len(df['outflow'])), linestyle='--', color='gray')
ax2.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('/mnt/data/autocorrelation_analysis.png', dpi=300, bbox_inches='tight')
2. クロス相関分析
# クロス相関分析の実装
def ccf(x, y, max_lags=None):
n = len(x)
if max_lags is None:
max_lags = n - 1
lags = np.arange(-max_lags, max_lags + 1)
ccf = np.zeros(len(lags))
x = (x - np.mean(x)) / (np.std(x) * len(x))
y = (y - np.mean(y)) / np.std(y)
for i, lag in enumerate(lags):
if lag < 0:
ccf[i] = np.sum(x[-lag:] * y[:lag])
elif lag == 0:
ccf[i] = np.sum(x * y)
else:
ccf[i] = np.sum(x[:-lag] * y[lag:])
return ccf
plt.figure(figsize=(12, 6))
max_lags = 48
ccf_result = ccf(df['inflow'], df['outflow'], max_lags)
lags = np.arange(-max_lags, max_lags + 1)
plt.plot(lags, ccf_result, 'go-', markersize=4)
plt.title('Cross-correlation between Inflow and Outflow')
plt.xlabel('Lag (hours)')
plt.ylabel('Correlation')
plt.axhline(y=0, linestyle='-', color='gray')
plt.axhline(y=1.96/np.sqrt(len(df['inflow'])), linestyle='--', color='gray')
plt.axhline(y=-1.96/np.sqrt(len(df['inflow'])), linestyle='--', color='gray')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('/mnt/data/cross_correlation_analysis.png', dpi=300, bbox_inches='tight')
3. 周期性の検出
from scipy.signal import find_peaks
# 流入量の主要な周期性
peaks_inflow, _ = find_peaks(acf_inflow[1:], height=1.96/np.sqrt(len(df['inflow'])))
print("\n流入量の主要な周期性(ラグ時間):")
print(peaks_inflow + 1)
# 放流量の主要な周期性
peaks_outflow, _ = find_peaks(acf_outflow[1:], height=1.96/np.sqrt(len(df['outflow'])))
print("\n放流量の主要な周期性(ラグ時間):")
print(peaks_outflow + 1)
分析結果
流入量の特性
-
周期性
- 主要な周期: 24時間 (自己相関: 0.5408)
- 短期周期: 2時間
- 自然な水流の日周期を反映
-
特徴
- 比較的緩やかな自己相関の減衰
- 短期的な変動と日周期の組み合わせ
- 自然現象としての特徴を示す
放流量の特性
-
周期性
- 主要な周期: 24時間 (自己相関: 0.8121)
- 副次的な周期: 12時間, 36時間
- 流入量より強い規則性を示す
-
特徴
- 強い24時間周期性
- 計画的な放水管理を示唆
- 人為的な制御パターンの存在
流入量と放流量の関係
-
クロス相関
- 最大相関: -13時間ラグ
- 相関係数: 0.0841(比較的弱い相関)
-
運用特性
- 流入と放流の直接的な連動性は低い
- 計画的な放流管理の存在を示唆
流入量の周期性に関する考察
流入量の周期性について、上流のダムの放流の関与を考えてみましたが、早明浦ダムには直接の上流に大規模ダムは存在せず複数の支流からの自然流入が主体となっていることから自然要因の周期性と予想しました。大変興味深い事象なので機会があればまた調べてみたいと思います。