08 ENSOコンポジット解析

Niño3.4 指数を用いて El Niño / La Niña の時期を判定し、それぞれの時期の海面水温(SST)を平均して、ENSO にともなう空間パターンを調べる。

講義名
データサイエンス
使用言語
Python
到達目標
イベントを条件としてデータを抽出し、コンポジット平均と差分図から ENSO の代表的な SST パターンを説明できるようになる

1. 背景:コンポジットとは何か

気候・海洋データでは、毎年の値はばらつきが大きい。そのため、ある現象が起きたときの「典型的なパターン」を見たい場合には、条件を満たす時期だけを集めて平均することが多い。この条件付き平均を コンポジット(composite) という。

今回は、ENSO を代表する指標である Niño3.4 anomaly を用いて El Niño と La Niña の時期を判定し、それぞれの時期の全球 SST を平均する。

この回の狙いは、単に時系列を眺めるのではなく、イベントを条件にして平均することで、背後にある空間構造を取り出すことである。
El Niño composite SST = El Niño と判定された月の SST の平均
La Niña composite SST = La Niña と判定された月の SST の平均

2. 使用データ

本演習では、ENSO 判定用の指数ファイル sstoi.indices と、全球 SST データ sst.mnmean.nc を使う。

sstoi.indices の内容

このファイルには NINO1+2, NINO3, NINO4, NINO3.4 の各領域平均 SST と、その偏差(ANOM)が月ごとに並んでいる。今回使うのは、最後の ANOM 列、つまり Nino3.4 anomaly である。

sst.mnmean.nc の内容

このファイルには sst(time, lat, lon) が格納されている。全球 2° × 2° 格子の月平均 SST データであり、単位は ℃ である。

ENSO の定義そのものは海面水温偏差に基づいているので、今回は SOI ではなく Niño3.4 anomaly を用いてイベントを判定する。

3. ENSO の判定方法

Niño3.4 anomaly がある閾値を超えても、1か月だけでは偶然の変動かもしれない。そこで、5か月移動平均 をとったうえで、一定期間以上継続した場合だけイベントとみなす。

現象判定条件
El Niño5か月移動平均した Niño3.4 anomaly が +0.5℃以上5か月以上継続
La Niña5か月移動平均した Niño3.4 anomaly が -0.5℃以下5か月以上継続
なぜ移動平均するのか
ENSO は数か月から 1年程度の時間スケールをもつ現象である。月ごとの細かい揺らぎをそのまま使うと、イベントが頻繁に切り替わってしまう。移動平均は、短周期の揺らぎをならして ENSO の時間スケールを見やすくするための処理である。

4. 計算の流れ

  1. sstoi.indices を読み込む
  2. Niño3.4 anomaly を取り出す
  3. 5か月移動平均を計算する
  4. El Niño / La Niña の条件を満たす月を判定する
  5. sst.mnmean.nc を読み込む
  6. ENSO イベント月に対応する SST だけを抽出する
  7. El Niño composite, La Niña composite, 差分図を描く
  8. 図を PNG として保存する
STEP 1

5. Niño3.4 データの読み込み

import numpy as np
import pandas as pd
import xarray as xr
import matplotlib.pyplot as plt

colnames = [
    "YR", "MON",
    "NINO12", "ANOM12",
    "NINO3",  "ANOM3",
    "NINO4",  "ANOM4",
    "NINO34", "ANOM34"
]

df = pd.read_csv(
    "sstoi.indices",
    sep=r"\s+",
    skiprows=1,
    names=colnames
)

df["time"] = pd.to_datetime(dict(year=df["YR"], month=df["MON"], day=15))
df["ym"] = df["time"].dt.to_period("M")

nino34_anom = pd.Series(df["ANOM34"].values, index=df["time"])

ポイント

STEP 2

6. 移動平均とイベント判定

threshold = 0.5
min_len = 5

nino34_smooth = nino34_anom.rolling(window=5, center=True).mean()

def detect_runs(series, threshold=0.5, mode="el", min_len=5):
    if mode == "el":
        mask = series >= threshold
    elif mode == "la":
        mask = series <= -threshold
    else:
        raise ValueError("mode must be 'el' or 'la'")

    events = np.zeros(len(series), dtype=bool)
    count = 0

    for i in range(len(series)):
        if pd.notna(mask.iloc[i]) and mask.iloc[i]:
            count += 1
        else:
            if count >= min_len:
                events[i-count:i] = True
            count = 0

    if count >= min_len:
        events[len(series)-count:len(series)] = True

    return pd.Series(events, index=series.index)

el_event = detect_runs(nino34_smooth, threshold=threshold, mode="el", min_len=min_len)
la_event = detect_runs(nino34_smooth, threshold=threshold, mode="la", min_len=min_len)

el_years = np.unique(el_event.index[el_event].year)
la_years = np.unique(la_event.index[la_event].year)

ポイント

確認
  • なぜ 1か月だけ閾値を超えた月を El Niño / La Niña と呼ばないのか
  • 移動平均をとると、元の時系列のどのような情報が弱くなるか
STEP 3

7. SST データとの対応付け

ds = xr.open_dataset("sst.mnmean.nc")
sst = ds["sst"].astype(float)
sst = sst.where(sst > -100)

time_sst = pd.to_datetime(ds["time"].values)
ym_sst = pd.Series(time_sst).dt.to_period("M")

event_df = pd.DataFrame({
    "ym": df["ym"],
    "el": el_event.values,
    "la": la_event.values
})

el_map = dict(zip(event_df["ym"], event_df["el"]))
la_map = dict(zip(event_df["ym"], event_df["la"]))

el_mask_sst = np.array([el_map.get(ym, False) for ym in ym_sst], dtype=bool)
la_mask_sst = np.array([la_map.get(ym, False) for ym in ym_sst], dtype=bool)

ポイント

STEP 4

8. コンポジット平均の計算

sst_el = sst.isel(time=el_mask_sst)
sst_la = sst.isel(time=la_mask_sst)

comp_el = sst_el.mean(dim="time")
comp_la = sst_la.mean(dim="time")
comp_diff = comp_el - comp_la

clim_all = sst.mean(dim="time")
comp_el_anom = comp_el - clim_all
comp_la_anom = comp_la - clim_all

ポイント

STEP 5

9. 図の描画と保存

fig_ts_name   = "enso_event_detection.png"
fig_comp_name = "enso_composite_sst.png"
fig_anom_name = "enso_composite_sst_anomaly.png"

plt.savefig(fig_ts_name, dpi=300, bbox_inches="tight", facecolor="white")
plt.savefig(fig_comp_name, dpi=300, bbox_inches="tight", facecolor="white")
plt.savefig(fig_anom_name, dpi=300, bbox_inches="tight", facecolor="white")

ポイント

10. 実行結果の例

以下は、実際にこのスクリプトを実行して得られた図である。

Niño3.4 anomaly と ENSO 判定

Niño3.4 anomaly and ENSO event detection
灰色の線は月ごとの Niño3.4 anomaly、黒線は 5か月移動平均、赤と青の帯はそれぞれ El Niño / La Niña と判定された月を示す。

絶対値 SST のコンポジット

ENSO composite SST
左:El Niño composite SST、中央:La Niña composite SST、右:差分(El Niño − La Niña)。絶対値では緯度方向の温度勾配が強く見える。

SST anomaly のコンポジット

ENSO composite SST anomaly
左:El Niño composite SST anomaly、中央:La Niña composite SST anomaly、右:差分(El Niño − La Niña)。赤道太平洋に ENSO の代表的なパターンが明瞭に現れている。

11. 考察:平均すると何が見えるか

個々の年の SST 分布は複雑であり、毎年まったく同じ形になるわけではない。しかし、El Niño の月だけ、La Niña の月だけを集めて平均すると、それぞれに共通する空間構造が浮かび上がる。

コンポジットの本質
ばらつきの大きいデータでも、同じ現象に属するケースだけを平均すると、その現象の代表的パターンが見えやすくなる。

この図から読み取れること

考えてみよう
  • なぜ absolute SST の図よりも anomaly の図の方が ENSO 信号を見やすいのか。
  • 赤道太平洋以外の海域にも変化が見えるのはなぜか。
  • この図だけで、長期的な温暖化の影響まで説明できるだろうか。
この回で見ているのは主に 短期(年々)変動 である。長期的な変化を調べるには、次回のトレンド解析が必要になる。

12. まとめ

この回では「イベントを条件にして平均する」考え方を学んだ。次回は、ENSO のような年々変動とは別に、海面水温の 長期トレンド を調べる。

13. 解答の表示

まずは上の流れに沿って自分でコードを組み立てること。確認したい場合だけ、パスワードを入力して解答と完全スクリプトを表示する。