Python を用いて、タヒチとダーウィンの海面気圧データから Southern Oscillation Index(SOI)を計算する。
エルニーニョとラニーニャは、海洋と大気が結びついた変動であり、まとめて ENSO(El Niño-Southern Oscillation)と呼ばれる。エルニーニョ時には赤道太平洋東部で暖水が広がり、大気循環も平年と異なる。
その大気側の変動を表す代表的な指標のひとつが 南方振動指数(SOI) である。SOI は、タヒチとダーウィンの海面気圧の変動を用いて計算される。
本演習では、タヒチおよびダーウィンの月平均海面気圧データを用いる。ファイルはこのページと同じディレクトリに置いてある。
SOI の計算では、計算そのものより前に、データの形を整える操作がたくさん出てくる。ここを飛ばすと、コードを読んでいて急に難しく感じる。
| 書き方 | 意味 |
|---|---|
| pd.read_csv(...) | 表形式データを読む。今回は空白区切りテキストを表として読む。 |
| set_index("YEAR") | YEAR 列を行ラベルにする。年でデータを選びやすくなる。 |
| .loc[1981:2010, months] | 行と列をラベルで指定して切り出す。前は年、後ろは月名。 |
| mean(axis=0) | 列ごとに平均する。各月の平均を求めるときに使う。 |
| to_numpy() | pandas の表を NumPy 配列に変える。 |
| ravel() | 2次元配列を1次元に並べ直す。 |
| np.isnan(...) | 欠損値 NaN かどうかを調べる。 |
| enumerate(...) | 中身と番号を同時に取り出す。 |
| pd.DataFrame(...) | 辞書や配列から表を作る。 |
まずは Python でテキストファイルを読み込む。ここでは pandas を用いる。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
dar = pd.read_csv("Darwin.txt", sep=______, engine="python", na_values=[-99.9])
tah = pd.read_csv("Tahiti.txt", sep=______, engine="python", na_values=[-99.9])
print(dar.head())
print(tah.head())
print(dar.columns)
print(dar.shape)
print(dar.tail())
| sep | 意味 |
|---|---|
| "," | カンマ区切り |
| "\t" | タブ区切り |
| " " | スペース1個 |
| r"\s+" | 空白が1個以上 |
基準期間 1981–2010 年の各月平均を求める。これが climatology である。
months = ["JAN","FEB","MAR","APR","MAY","JUN",
"JUL","AUG","SEP","OCT","NOV","DEC"]
dar = dar.set_index("YEAR")
tah = tah.set_index("YEAR")
dar_base = dar.loc[______:______, months]
tah_base = tah.loc[______:______, months]
mslpd = dar_base.mean(axis=______)
mslpt = tah_base.mean(axis=______)
print(mslpd)
print(mslpt)
print(len(mslpd))
print(mslpd["JAN"], mslpd["AUG"])
観測値から climatology を引いて anomaly(平均からのずれ)を求める。
aDar = dar[months] - ______
aTah = tah[months] - ______
print(aDar.head())
print(aTah.head())
print(aDar.loc[1981:2010].mean(axis=0))
print(aTah.loc[1981:2010].mean(axis=0))
anomaly を標準偏差で割り、スケールをそろえる。
aDar_base = aDar.loc[1981:2010, months].to_numpy().ravel()
aTah_base = aTah.loc[1981:2010, months].to_numpy().ravel()
aDar_base = aDar_base[~np.isnan(aDar_base)]
aTah_base = aTah_base[~np.isnan(aTah_base)]
sDar = np.sqrt(np.sum(aDar_base**2) / ______)
sTah = np.sqrt(np.sum(aTah_base**2) / ______)
nDar = aDar / ______
nTah = aTah / ______
print(sDar, sTah)
print(nDar.head())
print(np.nanmean(nDar.loc[1981:2010, months].to_numpy()))
print(np.nanmean(nTah.loc[1981:2010, months].to_numpy()))
標準化したタヒチとダーウィンの差のばらつきを MSD として求める。
diff_base = (nTah.loc[1981:2010, months]
- nDar.loc[1981:2010, months]).to_numpy().ravel()
diff_base = diff_base[~np.isnan(diff_base)]
MSD = np.sqrt(np.sum(diff_base**2) / diff_base.size)
print(MSD)
SOI = (______ - ______) / ______
print(SOI.head())
SOI は、標準化したタヒチとダーウィンの差を MSD で割って求める。
records = []
for year in SOI.index:
for imonth, month in enumerate(months, start=1):
records.append({
"YEAR": year,
"MONTH": imonth,
"SOI": SOI.loc[year, month]
})
soi_ts = pd.DataFrame(records)
soi_ts["TIME"] = soi_ts["YEAR"] + (soi_ts["MONTH"] - 1) / 12.0
soi_ts = soi_ts.dropna(subset=["SOI"])
plt.figure(figsize=(12,4))
plt.plot(soi_ts["TIME"], soi_ts["SOI"])
plt.axhline(0, color="k")
plt.xlabel("Year")
plt.ylabel("SOI")
plt.title("Southern Oscillation Index")
plt.show()
# 月別テーブル型
SOI_out = SOI.copy()
SOI_out.insert(0, "YEAR", SOI_out.index)
SOI_out.to_csv("SOI_monthly_table_updated.txt",
sep="\t", index=False, float_format="%.4f")
# 1次元時系列型
soi_ts.to_csv("SOI_timeseries_updated.txt",
sep="\t", index=False, float_format="%.4f")
完全スクリプトは別ページにしてある。まず自分で考えてから必要なときだけ参照すること。
▶ 解答ページへ本課題は、Jupyter Lab で作成した Notebook を HTML として保存し、Teams に提出すること。