SLA(海面高度偏差)データを用いて、ENSOコンポジット、長期トレンド、p値マップを作成する。08と09で学んだ方法を、別の海洋データへ応用する課題である。
これまでの回では、SOI、Niño3.4、SSTを使ってENSOや長期トレンドを調べてきた。 ここでは、海面高度偏差(SLA: Sea Level Anomaly)を使う。
SSTは海面の温度を表す。一方、SLAは海面の高さの偏差であり、海水の熱膨張、風による水の吹き寄せ、 海洋循環の変化などを反映する。つまり、SSTよりも海洋の力学的な変化を見やすい場合がある。
以下の3つの図を作成し、SSTで得られた結果と比較する。
このページと同じディレクトリに次のファイルを置く。
| ファイル | 内容 |
|---|---|
| sla_monthly_light_2deg_1993_2025.nc | CMEMS月平均SLAを約2度格子へ軽量化したデータ。変数は sla(time, latitude, longitude)。 |
| sstoi.indices | Niño3.4 anomalyを含むENSO指数データ。 |
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib.pyplot as plt
from scipy import stats
sla_file = "______________________________.nc"
nino_file = "______________"
start_year = ______
end_year = ______
alpha = 0.05
ds = xr.open_dataset(sla_file)
sla = ds["____"]
lat = ds["________"].values
lon = ds["_________"].values
time_sla = pd.to_datetime(ds["time"].values)
sla = sla.sel(time=slice(f"{start_year}-01-01", f"{end_year}-12-31"))
time_sla = pd.to_datetime(sla["time"].values)
print(sla)
colnames = [
"YR", "MON",
"NINO12", "ANOM12",
"NINO3", "ANOM3",
"NINO4", "ANOM4",
"NINO34", "ANOM34"
]
nino_df = pd.read_csv(
nino_file,
sep=r"\s+",
skiprows=1,
names=colnames
)
nino_df["time"] = pd.to_datetime(
dict(year=nino_df["YR"], month=nino_df["MON"], day=15)
)
nino_df = nino_df.set_index("time")
nino34 = nino_df["________"].astype(float)
nino34_smooth = nino34.rolling(
window=______,
center=True
).mean()
el_event = nino34_smooth >= ______
la_event = nino34_smooth <= -______
09で行ったSSTトレンド解析と同じ考え方で、各格子点におけるSLAの時間変化を線形回帰する。
years = time_sla.year + (time_sla.month - ______) / 12.0
years = np.asarray(years, dtype=float)
def calc_trend_pvalue(x, y):
x = np.asarray(x, dtype=float)
y = np.asarray(y, dtype=float)
mask = np.isfinite(x) & np.isfinite(y)
x = x[mask]
y = y[mask]
n = len(x)
if n < 3:
return np.nan, np.nan, np.nan, np.nan
slope, intercept = np.polyfit(x, y, 1)
r = np.corrcoef(x, y)[0, 1]
yhat = slope * x + intercept
residual = y - yhat
dof = n - 2
sse = np.sum(residual**2)
sxx = np.sum((x - np.mean(x))**2)
mse = sse / dof
se_slope = np.sqrt(mse / sxx)
tval = slope / se_slope
pval = 2 * (1 - stats.t.cdf(np.abs(tval), df=dof))
return slope, pval, r, n
sla_np = sla.values
ntime, nlat, nlon = sla_np.shape
trend = np.full((nlat, nlon), np.nan)
pval = np.full((nlat, nlon), np.nan)
for i in range(nlat):
for j in range(nlon):
y = sla_np[:, i, j]
slope, p, r, n = calc_trend_pvalue(_____, _____)
trend[i, j] = _____
pval[i, j] = _____
sig_mask = pval < alpha
trend_mm = trend * ______
def plot_global_map(data, title, cbar_label, save_name,
cmap="RdBu_r", vmin=None, vmax=None, sig=None):
fig, ax = plt.subplots(figsize=(14, 6))
pcm = ax.pcolormesh(
lon, lat, data,
shading="auto",
cmap=cmap,
vmin=vmin,
vmax=vmax
)
ax.set_xlim(-180, 180)
ax.set_ylim(-80, 80)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_title(title)
ax.grid(True, alpha=0.3)
cbar = fig.colorbar(pcm, ax=ax, pad=0.02)
cbar.set_label(cbar_label)
if sig is not None:
yy, xx = np.where(sig)
ax.scatter(lon[xx], lat[yy], s=2, c="k", alpha=0.45, label=f"p < {alpha}")
ax.legend(loc="lower left")
plt.tight_layout()
plt.savefig(save_name, dpi=300, bbox_inches="tight", facecolor="white")
plt.show()
enso_df = pd.DataFrame({
"nino34": nino34,
"nino34_smooth": nino34_smooth,
"el": el_event,
"la": la_event
}).dropna()
tmp = enso_df.reindex(time_sla)
el_mask = tmp["el"].fillna(False).values
la_mask = tmp["la"].fillna(False).values
sla_el = sla.isel(time=________).mean(dim="time", skipna=True)
sla_la = sla.isel(time=________).mean(dim="time", skipna=True)
sla_diff = ________ - ________
sla_el_cm = sla_el.values * ______
sla_la_cm = sla_la.values * ______
sla_diff_cm = sla_diff.values * ______
fig, axes = plt.subplots(3, 1, figsize=(14, 15), constrained_layout=True)
panels = [
(sla_el_cm, "El Niño composite SLA", "SLA (cm)", -15, 15),
(sla_la_cm, "La Niña composite SLA", "SLA (cm)", -15, 15),
(sla_diff_cm, "El Niño - La Niña SLA", "SLA difference (cm)", -20, 20),
]
for ax, (data, title, label, vmin, vmax) in zip(axes, panels):
pcm = ax.pcolormesh(
lon, lat, data,
shading="auto",
cmap="________",
vmin=vmin,
vmax=vmax
)
ax.set_xlim(-180, 180)
ax.set_ylim(-80, 80)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_title(title)
ax.grid(True, alpha=0.3)
cbar = fig.colorbar(pcm, ax=ax, pad=0.02)
cbar.set_label(label)
plt.savefig("sla_enso_composite.png", dpi=300, bbox_inches="tight", facecolor="white")
plt.show()
作成したSLAのENSOコンポジット、SLA長期トレンド、p値マップを見て、 これまでに作成したSSTのENSOコンポジットおよびSST長期トレンドと比較しなさい。
課題提出後、確認用としてパスワードを入力すると、完全スクリプトと作成例の図を表示する。
sla_monthly_light_2deg_1993_2025
sstoi.indices
1993
2024
sla
latitude
longitude
ANOM34
5
0.5
0.5
0.5
years, y
slope
p
1000
el_mask
la_mask
sla_el, sla_la
100
100
100
RdBu_r
import numpy as np
import pandas as pd
import xarray as xr
import matplotlib.pyplot as plt
from scipy import stats
sla_file = "sla_monthly_light_2deg_1993_2025.nc"
nino_file = "sstoi.indices"
start_year = 1993
end_year = 2024
alpha = 0.05
nino_threshold = 0.5
smooth_window = 5
ds = xr.open_dataset(sla_file)
sla = ds["sla"]
lat = ds["latitude"].values
lon = ds["longitude"].values
time_sla = pd.to_datetime(ds["time"].values)
sla = sla.sel(time=slice(f"{start_year}-01-01", f"{end_year}-12-31"))
time_sla = pd.to_datetime(sla["time"].values)
colnames = [
"YR", "MON",
"NINO12", "ANOM12",
"NINO3", "ANOM3",
"NINO4", "ANOM4",
"NINO34", "ANOM34"
]
nino_df = pd.read_csv(
nino_file,
sep=r"\s+",
skiprows=1,
names=colnames
)
nino_df["time"] = pd.to_datetime(
dict(year=nino_df["YR"], month=nino_df["MON"], day=15)
)
nino_df = nino_df.set_index("time")
nino34 = nino_df["ANOM34"].astype(float)
nino34_smooth = nino34.rolling(
window=smooth_window,
center=True
).mean()
el_event = nino34_smooth >= nino_threshold
la_event = nino34_smooth <= -nino_threshold
years = time_sla.year + (time_sla.month - 0.5) / 12.0
years = np.asarray(years, dtype=float)
def calc_trend_pvalue(x, y):
x = np.asarray(x, dtype=float)
y = np.asarray(y, dtype=float)
mask = np.isfinite(x) & np.isfinite(y)
x = x[mask]
y = y[mask]
n = len(x)
if n < 3:
return np.nan, np.nan, np.nan, np.nan
slope, intercept = np.polyfit(x, y, 1)
r = np.corrcoef(x, y)[0, 1]
yhat = slope * x + intercept
residual = y - yhat
dof = n - 2
sse = np.sum(residual**2)
sxx = np.sum((x - np.mean(x))**2)
mse = sse / dof
se_slope = np.sqrt(mse / sxx)
tval = slope / se_slope
pval = 2 * (1 - stats.t.cdf(np.abs(tval), df=dof))
return slope, pval, r, n
sla_np = sla.values
ntime, nlat, nlon = sla_np.shape
trend = np.full((nlat, nlon), np.nan)
pval = np.full((nlat, nlon), np.nan)
rmap = np.full((nlat, nlon), np.nan)
nmap = np.full((nlat, nlon), np.nan)
for i in range(nlat):
for j in range(nlon):
y = sla_np[:, i, j]
slope, p, r, n = calc_trend_pvalue(years, y)
trend[i, j] = slope
pval[i, j] = p
rmap[i, j] = r
nmap[i, j] = n
sig_mask = pval < alpha
trend_mm = trend * 1000.0
def plot_global_map(data, title, cbar_label, save_name,
cmap="RdBu_r", vmin=None, vmax=None, sig=None):
fig, ax = plt.subplots(figsize=(14, 6))
pcm = ax.pcolormesh(
lon, lat, data,
shading="auto",
cmap=cmap,
vmin=vmin,
vmax=vmax
)
ax.set_xlim(-180, 180)
ax.set_ylim(-80, 80)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_title(title)
ax.grid(True, alpha=0.3)
cbar = fig.colorbar(pcm, ax=ax, pad=0.02)
cbar.set_label(cbar_label)
if sig is not None:
yy, xx = np.where(sig)
ax.scatter(lon[xx], lat[yy], s=2, c="k", alpha=0.45, label=f"p < {alpha}")
ax.legend(loc="lower left")
plt.tight_layout()
plt.savefig(save_name, dpi=300, bbox_inches="tight", facecolor="white")
plt.show()
plot_global_map(
trend_mm,
title=f"SLA Trend ({start_year}-{end_year})",
cbar_label="SLA trend (mm/year)",
save_name="sla_trend_1993_2024.png",
cmap="RdBu_r",
vmin=-10,
vmax=10,
sig=sig_mask
)
plot_global_map(
pval,
title=f"p-value of SLA trend ({start_year}-{end_year})",
cbar_label="p-value",
save_name="sla_pvalue_1993_2024.png",
cmap="viridis_r",
vmin=0,
vmax=0.1,
sig=sig_mask
)
enso_df = pd.DataFrame({
"nino34": nino34,
"nino34_smooth": nino34_smooth,
"el": el_event,
"la": la_event
}).dropna()
tmp = enso_df.reindex(time_sla)
el_mask = tmp["el"].fillna(False).values
la_mask = tmp["la"].fillna(False).values
sla_el = sla.isel(time=el_mask).mean(dim="time", skipna=True)
sla_la = sla.isel(time=la_mask).mean(dim="time", skipna=True)
sla_diff = sla_el - sla_la
sla_el_cm = sla_el.values * 100.0
sla_la_cm = sla_la.values * 100.0
sla_diff_cm = sla_diff.values * 100.0
fig, axes = plt.subplots(3, 1, figsize=(14, 15), constrained_layout=True)
panels = [
(sla_el_cm, "El Niño composite SLA", "SLA (cm)", -15, 15),
(sla_la_cm, "La Niña composite SLA", "SLA (cm)", -15, 15),
(sla_diff_cm, "El Niño - La Niña SLA", "SLA difference (cm)", -20, 20),
]
for ax, (data, title, label, vmin, vmax) in zip(axes, panels):
pcm = ax.pcolormesh(
lon, lat, data,
shading="auto",
cmap="RdBu_r",
vmin=vmin,
vmax=vmax
)
ax.set_xlim(-180, 180)
ax.set_ylim(-80, 80)
ax.set_xlabel("Longitude")
ax.set_ylabel("Latitude")
ax.set_title(title)
ax.grid(True, alpha=0.3)
cbar = fig.colorbar(pcm, ax=ax, pad=0.02)
cbar.set_label(label)
plt.savefig("sla_enso_composite.png", dpi=300, bbox_inches="tight", facecolor="white")
plt.show()