Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- This script determines what proxy data was used in the AD 1400–1449
- portion of the MBH98 temperature reconstruction by regressing it on
- * the proxy data used in Wahl & Ammann 2007 (WA), and
- * an extended dataset from the Climategate archive.
- Results:
- * MBH98 and WA used different datasets.
- * The MBH98 proxy data is in the Climategate archive.
- NOTE: It may take a while to download the data (about 150 MB).
- Tested on Linux with Python 3.8.10 and these library versions:
- NumPy 1.17.4
- SciPy 1.3.3
- pandas 0.25.3
- Matplotlib 3.1.2
- """
- import os.path
- import tarfile
- import requests
- import numpy as np
- import scipy.signal as sps
- import pandas as pd
- import matplotlib.pyplot as plt
- # Get submatrix of x where the first column of x is in range t.
- def submatrix(x, t):
- return x[(x[:, 0] >= t[0]) & (x[:, 0] <= t[-1]), :]
- # Fill in missing data in the last rows of matrix x.
- def fill_in_missing(x):
- for i in range(1, x.shape[1]):
- if np.isnan(x[-1, i]):
- index = (~np.isnan(x[:, i])).nonzero()[0]
- x[index[-1]:, i] = x[index[-1], i]
- # Download data from url.
- def get_data_from(url):
- filename = url.split("/")[-1]
- if not os.path.isfile(filename):
- r = requests.get(url, stream=True)
- with open(filename, "wb") as fd:
- for chunk in r.iter_content(chunk_size=1024**2):
- fd.write(chunk)
- return filename
- # Untar tar archive in directory.
- def untar(tar, directory):
- if not os.path.isdir(directory):
- with tarfile.TarFile(tar, "r") as td:
- td.extractall(directory)
- # Get MBH98 temperature reconstruction.
- def get_mbh98_reconstruction():
- # Get data.
- url = "http://www.meteo.psu.edu/holocene/public_html/shared/research/MANNETAL98/nhmean.dat"
- filename = get_data_from(url)
- # Load data.
- mbh = np.genfromtxt(filename, skip_header=1)[:, [0, 1]]
- return mbh
- # Get Wahl & Ammann 2007 emulation of MBH98.
- def get_wahl_and_ammann_reconstruction():
- # Get archive.
- url = "https://projects.ral.ucar.edu/rc4a/millennium/WA_MBH-emulation.tar"
- tarname = get_data_from(url)
- directory = tarname.split(".tar")[0]
- untar(tarname, directory)
- # Load data.
- filename = "WAscenarios/WA_original/WA_original/NHem.reconstruction.dat"
- path = os.path.join(directory, filename)
- wa = np.genfromtxt(path)
- wa = wa[:, [0, 2]]
- return wa
- # Get proxy data used in Wahl & Ammann 2007.
- def get_wahl_and_ammann_proxy_data():
- # Get proxy list.
- url_datalist = "http://www.meteo.psu.edu/holocene/public_html/shared/research/MANNETAL98/PROXY/datalist1400.dat"
- filename_datalist = get_data_from(url_datalist)
- # Get proxy data.
- url_data = "http://www.meteo.psu.edu/holocene/public_html/shared/research/MANNETAL98/PROXY/data1400.dat"
- filename_data = get_data_from(url_data)
- # Load proxy list and edit PC names.
- datalist = pd.read_table(filename_datalist)
- datalist = datalist.iloc[:, -1]
- for i, item in enumerate(datalist):
- if not item.startswith("pc"):
- line = item
- else:
- datalist[i] = f"({line}) {item}"
- datalist = [x for x in datalist
- for y in [".dat", ".out", ".txt"]
- if y in x]
- # Load proxy data and fill in missing values.
- proxy = np.genfromtxt(filename_data)
- fill_in_missing(proxy)
- return proxy, datalist
- # Get MBH98 proxy data from Climategate archive.
- def get_climategate_proxy_data(t):
- # Get Climategate archive.
- url = "https://sealevel.info/FOIA/2009/FOIA/documents/mbh98-osborn/mbh98.tar"
- filename = get_data_from(url)
- directory = filename.split(".tar")[0]
- untar(filename, directory)
- # Extended proxy list from Climategate dataset.
- datalist = [
- "ICE/MANNETAL97/quelc1-o18.dat",
- "ICE/MANNETAL97/quelc1-accum.dat",
- "ICE/MANNETAL97/quelc2-o18.dat",
- "ICE/MANNETAL97/quelc2-accum.dat",
- "ICE/MANNETAL97/svalbard.dat",
- "ICE/FISHER/westgreen-o18.dat",
- "TREE/MANNETAL97/tasmania.dat",
- "TREE/MANNETAL97/npatagonia.dat",
- "TREE/MANNETAL97/treeline11.dat",
- "TREE/MANNETAL97/seprecip-nc.dat",
- "TREE/MANNETAL97/seprecip-sc.dat",
- "TREE/MANNETAL97/seprecip-ga.dat",
- "TREE/MISC/yakut.dat",
- "TREE/MISC/fenno-new.dat",
- "TREE/MISC/urals-new.dat",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc01.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc02.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc03.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc04.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc05.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc06.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc07.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc08.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc09.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc10.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc11.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc12.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc13.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc14.out",
- "TREE/ITRDB/NOAMER/BACKTO_1400/pc15.out",
- "TREE/ITRDB/MISC/fran009.txt",
- "TREE/ITRDB/MISC/fran010.txt",
- "TREE/ITRDB/MISC/moro007.txt",
- "TREE/ITRDB/MISC/moro008.txt",
- "TREE/STAHLE/SWM/BACKTO_1400/pc01.out",
- ]
- # Load proxy data and fill in missing values.
- proxy = np.full((t.size, len(datalist) + 1), np.nan)
- proxy[:, 0] = t
- for i, filename in enumerate(datalist):
- path = os.path.join(directory, filename)
- p = pd.read_table(path, header=None)
- p = p[0].str.split(expand=True).astype(float).to_numpy()
- p = submatrix(p, t)
- index = p[:, 0] - t[0]
- proxy[index.astype(int), i + 1] = p[:, 1]
- fill_in_missing(proxy)
- return proxy, datalist
- # Standardize data matrix x (time in first column) over interval t using MBH98
- # or WA standardization (detrended versus nondetrended standard deviation).
- def standardize(x, t, std):
- x_z = x.copy()
- x_t = submatrix(x, t)
- mu = x_t[:, 1:].mean(axis=0)
- x_z[:, 1:] -= mu
- if std == "MBH":
- sigma = sps.detrend(x_t[:, 1:], axis=0).std(axis=0, ddof=0)
- x_z[:, 1:] /= sigma
- if std == "WA":
- sigma = x_t[:, 1:].std(axis=0, ddof=1)
- x_z[:, 1:] /= sigma
- return x_z
- # Regress the second column of y on all except the first column of x
- # over interval t.
- def regress(y, x, t):
- y_t = submatrix(y, t)
- x_t = submatrix(x, t)
- beta_ols = np.linalg.lstsq(x_t[:, 1:], y_t[:, 1], rcond=None)[0]
- y_ols = np.full((x.shape[0], 2), np.nan)
- y_ols[:, 0] = x[:, 0]
- y_ols[:, 1] = x[:, 1:] @ beta_ols
- return beta_ols, y_ols
- # Make lineplot.
- def lineplot(x, y, fmt, ms, labels, title, figname):
- fig, ax = plt.subplots(figsize=(8, 4.5), dpi=200)
- for i in range(len(y)):
- ax.plot(x, submatrix(y[i], x)[:, 1],
- fmt[i], ms=ms[i], label=labels[i])
- ax.set_xlabel("Year", fontsize=10, labelpad=5)
- ax.set_ylabel("Temperature anomaly ($\degree$C)", fontsize=10)
- ax.set_title(title, loc="left", fontsize=12)
- ax.tick_params(axis="both", labelsize=10)
- ax.grid(b=True)
- ax.legend(fontsize=10)
- ax.set_facecolor((0.95, 0.95, 0.95))
- fig.tight_layout(rect=(0, 0, 0.95, 1))
- fig.savefig(figname)
- plt.close(fig)
- # Make barplot.
- def barplot(beta, datalist, title, figname):
- fig, ax = plt.subplots(figsize=(8, 10), dpi=200)
- y = np.arange(len(datalist))
- ax.barh(y, beta, height=0.8)
- ax.axvline(color="black")
- ax.invert_yaxis()
- ax.set_yticks(y)
- ax.set_yticklabels(datalist)
- ax.set_ylim([len(y), -1])
- ax.set_xlabel("Coefficient", fontsize=12, labelpad=5)
- ax.set_title(title, fontsize=18, loc="left")
- ax.tick_params(axis="both", labelsize=10)
- ax.grid(b=True)
- ax.set_axisbelow(b=True)
- ax.set_facecolor((0.95, 0.95, 0.95))
- fig.tight_layout()
- fig.savefig(figname)
- plt.close()
- # Make two barplots.
- def two_barplots(betas, datalists, titles, suptitle, figname):
- fig, ax = plt.subplots(1, 2, figsize=(8, 5), dpi=200, sharex=True)
- h = []
- for i in range(2):
- y = np.arange(len(datalists[i]))
- h.append(ax[i].barh(y, betas[i], height=0.8))
- ax[i].axvline(color="black")
- ax[i].invert_yaxis()
- ax[i].set_yticks(y)
- ax[i].set_yticklabels(datalists[i])
- ax[i].set_ylim([len(y), -1])
- ax[i].set_xlabel("Coefficient", fontsize=8, labelpad=5)
- ax[i].set_title(titles[i], fontsize=12, loc="left")
- ax[i].tick_params(axis="both", labelsize=6)
- ax[i].grid(b=True)
- ax[i].set_axisbelow(b=True)
- ax[i].set_facecolor((0.95, 0.95, 0.95))
- ax[1].yaxis.tick_right()
- for i, proxy in enumerate(zip(datalists[0], datalists[1])):
- if not proxy[1].endswith(proxy[0].split("/")[-1]):
- h[0][i].set_color("r")
- h[1][i].set_color("r")
- fig.tight_layout(rect=(0, 0, 1, 0.94))
- fig.subplots_adjust(wspace=0.1)
- x = np.mean([ax[0].get_position().xmax, ax[1].get_position().xmin])
- fig.suptitle(suptitle, x=x, ha="center", fontsize=14)
- fig.savefig(figname)
- plt.close()
- # Perform the regression analysis.
- def main():
- # Time intervals for reconstructions, calibrations and regressions.
- t = np.arange(1400, 1981)
- t_cal = np.arange(1902, 1981)
- t_reg = np.arange(1400, 1450)
- # Get data.
- mbh = get_mbh98_reconstruction()
- wa = get_wahl_and_ammann_reconstruction()
- proxy_wa, datalist_wa = get_wahl_and_ammann_proxy_data()
- proxy_cg, datalist_cg = get_climategate_proxy_data(t)
- # MBH98 reconstruction regressed on WA proxy data.
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- _, mbh_reg_on_wa = regress(mbh, proxy_wa_zscores, t_reg)
- x = t_reg
- y = [mbh, mbh_reg_on_wa]
- fmt = ["-"] * 2
- ms = [None] * 2
- labels = ["MBH98", "Regression"]
- title = "MBH98 reconstruction regressed on WA proxy data"
- figname = "fig1_mbh98_regressed_on_wa_data.png"
- lineplot(x, y, fmt, ms, labels, title, figname)
- # MBH98 reconstruction regressed on WA proxy data,
- # with WA reconstruction included for comparison.
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- _, mbh_reg_on_wa = regress(mbh, proxy_wa_zscores, t_reg)
- x = t_reg
- y = [mbh, mbh_reg_on_wa, wa]
- fmt = ["-"] * 3
- ms = [None] * 3
- labels = ["MBH98", "MBH98 regressed on WA proxy data", "WA"]
- title = "MBH98 and WA reconstructions"
- figname = "fig2_mbh98_and_wa.png"
- lineplot(x, y, fmt, ms, labels, title, figname)
- # WA reconstruction regressed on WA proxy data.
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- _, wa_reg_on_wa = regress(wa, proxy_wa_zscores, t_reg)
- x = t_reg
- y = [wa, wa_reg_on_wa]
- fmt = ["-o", "-D"]
- ms = [6, 3]
- labels = ["WA", "Regression"]
- title = "WA reconstruction regressed on WA proxy data"
- figname = "fig3_wa_regressed_on_wa_data.png"
- lineplot(x, y, fmt, ms, labels, title, figname)
- # MBH98 reconstruction regressed on Climategate proxy data.
- proxy_cg_zscores = standardize(proxy_cg, t_cal, "MBH")
- _, mbh_reg_on_cg = regress(mbh, proxy_cg_zscores, t_reg)
- x = t_reg
- y = [mbh, mbh_reg_on_cg]
- fmt = ["-o", "-D"]
- ms = [6, 3]
- labels = ["MBH98", "Regression"]
- title = "MBH98 reconstruction regressed on Climategate proxy data"
- figname = "fig4_mbh98_regressed_on_climategate_data.png"
- lineplot(x, y, fmt, ms, labels, title, figname)
- # Coefficients for WA regressed on WA proxy data.
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- beta_wa, _ = regress(wa, proxy_wa_zscores, t_reg)
- title = "WA regression coefficients\nAD 1400–1449 interval"
- figname = "fig5_coefficients_wa.png"
- barplot(beta_wa, datalist_wa, title, figname)
- # Coefficients for MBH98 regressed on Climategate data.
- proxy_cg_zscores = standardize(proxy_cg, t_cal, "MBH")
- beta_mbh, _ = regress(mbh, proxy_cg_zscores, t_reg)
- title = "MBH98 regression coefficients\nAD 1400–1449 interval"
- figname = "fig6_coefficients_mbh98.png"
- barplot(beta_mbh, datalist_cg, title, figname)
- # Regression coefficients for MBH98 and WA.
- proxy_cg_zscores = standardize(proxy_cg, t_cal, "MBH")
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- beta_mbh, _ = regress(mbh, proxy_cg_zscores, t_reg)
- beta_wa, _ = regress(wa, proxy_wa_zscores, t_reg)
- zip_mbh = zip(datalist_cg, beta_mbh)
- zip_mbh_nonzero = [x for x in zip_mbh if abs(x[1]) > 1e-6]
- datalist_mbh, beta_mbh = zip(*zip_mbh_nonzero)
- betas = [beta_mbh, beta_wa]
- datalists = [datalist_mbh, datalist_wa]
- titles = ["MBH98", "WA"]
- suptitle = "AD 1400–1449 regression coefficients"
- figname = "fig7_coefficients_mbh98_and_wa.png"
- two_barplots(betas, datalists, titles, suptitle, figname)
- # Regression coefficients for MBH98 and WA using MBH98 standardization.
- proxy_cg_zscores = standardize(proxy_cg, t_cal, "MBH")
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "MBH")
- beta_mbh, _ = regress(mbh, proxy_cg_zscores, t_reg)
- beta_wa, _ = regress(wa, proxy_wa_zscores, t_reg)
- zip_mbh = zip(datalist_cg, beta_mbh)
- zip_mbh_nonzero = [x for x in zip_mbh if abs(x[1]) > 1e-6]
- datalist_mbh, beta_mbh = zip(*zip_mbh_nonzero)
- betas = [beta_mbh, beta_wa]
- datalists = [datalist_mbh, datalist_wa]
- titles = ["MBH98", "WA"]
- suptitle = "AD 1400–1449 regression coefficients (MBH98 standardization)"
- figname = "fig8_coefficients_mbh98_standardization.png"
- two_barplots(betas, datalists, titles, suptitle, figname)
- # Regression coefficients for MBH98 and WA using WA standardization.
- proxy_cg_zscores = standardize(proxy_cg, t_cal, "WA")
- proxy_wa_zscores = standardize(proxy_wa, t_cal, "WA")
- beta_mbh, _ = regress(mbh, proxy_cg_zscores, t_reg)
- beta_wa, _ = regress(wa, proxy_wa_zscores, t_reg)
- zip_mbh = zip(datalist_cg, beta_mbh)
- zip_mbh_nonzero = [x for x in zip_mbh if abs(x[1]) > 1e-6]
- datalist_mbh, beta_mbh = zip(*zip_mbh_nonzero)
- betas = [beta_mbh, beta_wa]
- datalists = [datalist_mbh, datalist_wa]
- titles = ["MBH98", "WA"]
- suptitle = "AD 1400–1449 regression coefficients (WA standardization)"
- figname = "fig9_coefficients_wa_standardization.png"
- two_barplots(betas, datalists, titles, suptitle, figname)
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment