Supplement#

from adjustText import adjust_text
import matplotlib.pyplot as plt
from matplotlib import table
import numpy as np
import pandas as pd
from scipy import signal
import gcsfs
from scipy.signal import detrend
from scipy.stats import linregress
import scipy.signal as signal
import matplotlib as mpl
from statsmodels.api import tsa
import statsmodels.api as sm
import xarray as xr
import zarr
import warnings
warnings.filterwarnings('ignore')

mpl.rcParams['figure.facecolor'] = 'white'
mpl.rcParams['figure.dpi']= 150
mpl.rcParams['font.size'] = 12

Supplemental Figure 1 (S1)#

removed_color = '#2738ad'
volcano_color = '#de1f1f'

ec_data = pd.read_csv('data/ec_data.csv').drop('Unnamed: 0', axis=1)
fig, axs = plt.subplots(1, 2, figsize = (8.5, 4), sharey=True)
axs[0].scatter(ec_data['cox'], ec_data['ecs'], color = volcano_color, marker = '^')
slope, intercept, r, p, se = linregress(ec_data['cox'], ec_data['ecs'])
r_cox_original = r

axs[0].scatter(ec_data['rv_cox'], ec_data['ecs'], color = removed_color, marker = 'o')
slope, intercept, r, p, se = linregress(ec_data['rv_cox'], ec_data['ecs'])
r_cox_rv = r

for i in range(len(ec_data)):
    x_i = ec_data['rv_cox'][i]
    y_i = ec_data['ecs'][i]
    dx_i = x_i - ec_data['cox'][i]
    dy_i = 0
    axs[0].arrow(x_i, y_i, -1*dx_i, dy_i, linewidth = 0.05)

axs[0].text(0.1, 4.5, 'r='+str(np.round(r_cox_original,2)), color = volcano_color)
axs[0].text(0.25, 2.5, 'r='+str(np.round(r_cox_rv,2)), color = removed_color)

axs[1].scatter(ec_data['nijsse'], ec_data['ecs'], color = volcano_color, marker = '^', label = 'unadulterated\nseries')
slope, intercept, r, p, se = linregress(ec_data['nijsse'], ec_data['ecs'])
r_nijsse_original = r

axs[1].scatter(ec_data['rv_nijsse'], ec_data['ecs'], color = removed_color, marker = 'o', label = 'major eruptions\nremoved')
slope, intercept, r, p, se = linregress(ec_data['rv_nijsse'], ec_data['ecs'])
r_nijsse_rv = r

for i in range(len(ec_data)):
    x_i = ec_data['rv_nijsse'][i]
    y_i = ec_data['ecs'][i]
    dx_i = x_i - ec_data['nijsse'][i]
    dy_i = 0
    axs[1].arrow(x_i, y_i, -1*dx_i, dy_i, linewidth = 0.05)

axs[1].text(0.15, 4.5, 'r='+str(np.round(r_nijsse_original,2)), color = volcano_color)
axs[1].text(0.3, 2.5, 'r='+str(np.round(r_nijsse_rv,2)), color = removed_color)

fig.suptitle('Effect of Major Volcanic Forcing on ECS Emergent Relationship')

axs[1].legend(loc = (1.05, 0), edgecolor = 'black', framealpha = 1)

axs[0].set_ylabel('ECS (K)')
axs[0].set_xlabel(r'$\psi$ (K)')
axs[1].set_xlabel(r'$\sigma_{b}$ (K/decade)')

plt.tight_layout()
# plt.savefig('figures/figure_s1.png', dpi=3000)
../../_images/8a235d68ddb5991ea4dd9fc500638c02eebee65d8523c91608ada00c85b91410.png

Supplemental Figure 2 (S2)#

from adjustText import adjust_text
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import signal
from scipy.stats import linregress
import matplotlib as mpl
from statsmodels.api import tsa
import statsmodels.api as sm
import xarray as xr

mpl.rcParams['figure.facecolor'] = 'white'
mpl.rcParams['figure.dpi']= 150
mpl.rcParams['font.size'] = 12

def get_forcing(saod):
    '''
    Takes in a timeseries of SAOD (numpy array) and outputs a forcing profile (in W/m^2)
    '''
    return np.multiply(-20.7, np.subtract(1, np.exp(np.multiply(-1, saod))))

def remove_volcano(timeseries, temp_anomaly):
    X = temp_anomaly # independent variable
    y = timeseries   # dependent variable

    # to get intercept -- this is optional
    X = sm.add_constant(X)

    # fit the regression model
    reg = sm.OLS(y, X).fit()
    
    return reg.resid.values

# take a spatial average
def weighted_mean(da):
    
    # make 2d array of weights in case that lat is 1d
    if len(da.lat.shape)==2:
        weights=np.cos(np.deg2rad(da.lat))
    elif len(da.lat.shape)==1:
        weights = xr.ones_like(da)* (np.cos(np.deg2rad((da.lat))).values)
        
    # turn weights into nan where da is nan
    weights = weights*da/da
    
    if 'lat' in da.dims:
        wm = (da*weights).sum(dim=['lat'], skipna=True) / weights.sum(dim=['lat'], skipna=True)

    elif 'i' in da.dims:
        wm = (da*weights).sum(dim=['i','j'], skipna=True) / weights.sum(dim=['i','j'], skipna=True)
    elif 'nlat' in da.dims:
        wm = (da*weights).sum(dim=['nlat','nlon'], skipna=True) / weights.sum(dim=['nlat','nlon'], skipna=True)
    elif 'x' in da.dims:
        wm = (da*weights).sum(dim=['x','y'], skipna=True) / weights.sum(dim=['x','y'], skipna=True)
    return wm

def compute_cox(x):
    x = x[~np.isnan(x)]
    psi_vals=[]
    for i in np.arange(0, len(x)-55):
        y = signal.detrend(x[i:i+55])
        auto_m1 = tsa.acf(y,nlags=1) # autocorrelation function from statsmodels
        auto_m1b = auto_m1[1]    # select 1 lag autocorrelation value
        sigma_m1= np.std(y)
        log_m1= np.log(auto_m1b)
        log_m1b = np.abs(log_m1)   # take absolute value
        sqrt_m1 = np.sqrt(log_m1b)
        psi = sigma_m1/sqrt_m1
        psi_vals.append(psi)
    return np.nanmean(psi_vals)

def compute_nijsse(x, length=10):
    # remove NaNs from the timeseries
    x = np.array(x)
    mask = ~np.isnan(x)
    x = x[mask]
    # fill it with slopes
    slopes = []
    i = 0
    while i < len(x)-length:
        slope, intercept, r, p, se = linregress(np.arange(0,length), x[i:i+length])
        slopes.append(length*slope)
        i+=length
    return np.nanstd(slopes)

evolv2k_ts = weighted_mean(xr.open_dataset('data/evolv2k.nc')).sel(time=slice(850,1850))
evolv2k_saod = []
i=850
while i <= 1850:
    evolv2k_saod.append(float(evolv2k_ts.sel(time=slice(i, i+1)).mean(dim='time').aod550.values))
    i+=1
evolv2k_forcing = get_forcing(evolv2k_saod)

gao_2008_saod = np.divide(pd.read_csv('data/gao_2008.csv')['gm'].values.astype(float), 1.2*10**3)
gao_2008_forcing = get_forcing(gao_2008_saod)
crowley_2000_forcing = pd.read_csv('data/crowley_2000.txt', delimiter = '\t')['Vol.hl.cct'].values

crowley_2008_saod = pd.read_csv('data/crowley_2008.txt', delimiter = '\t')['AOD'].values
crowley_2008_forcing = get_forcing(crowley_2008_saod)

df = pd.read_csv('data/ts.csv')
model_keys = df.keys()[2:]
forcings = ['gao', 'gao', 'crowley_08', 'crowley_00',
            'evolv2k', 'crowley_08', 'gao', 'evolv2k',
            'crowley_08', 'gao', 'evolv2k', 'gao', 
            'evolv2k', 'evolv2k', 'evolv2k', 'crowley_08',
            'evolv2k', 'evolv2k']
ecs_vals = []
original_cox = []
rv_cox = []
original_nijsse = []
rv_nijsse = []

t = pd.read_csv('data/ecs.csv')

for i in range(len(model_keys)):
    ts = df[model_keys[i]].values
    ts_past1000 = ts[:1001]
    if model_keys[i] == 'HadCM3':
        X = crowley_2008_forcing
        y = ts_past1000
        X = sm.add_constant(X)
        reg = sm.OLS(y, X).fit()
        ecs_vals.append(t[t['model name']==model_keys[i]]['ecs'].values[0])
        original_cox.append(compute_cox(ts_past1000))
        rv_cox.append(compute_cox(np.add(reg.resid, reg.params[0])))
        original_nijsse.append(compute_nijsse(ts_past1000))
        rv_nijsse.append(compute_nijsse(np.add(reg.resid, reg.params[0])))
    elif forcings[i] == 'gao':
        X = gao_2008_forcing
        y = ts_past1000
        X = sm.add_constant(X)
        reg = sm.OLS(y, X).fit()
        ecs_vals.append(t[t['model name']==model_keys[i]]['ecs'].values[0])
        original_cox.append(compute_cox(ts_past1000))
        rv_cox.append(compute_cox(np.add(reg.resid, reg.params[0])))
        original_nijsse.append(compute_nijsse(ts_past1000))
        rv_nijsse.append(compute_nijsse(np.add(reg.resid, reg.params[0])))
    elif forcings[i] == 'crowley_00':
        X = crowley_2000_forcing
        y = ts[151:]
        X = sm.add_constant(X)
        reg = sm.OLS(y, X).fit()
        ecs_vals.append(t[t['model name']==model_keys[i]]['ecs'].values[0])
        original_cox.append(compute_cox(ts))
        rv_cox.append(compute_cox(np.add(reg.resid, reg.params[0])))
        original_nijsse.append(compute_nijsse(ts))
        rv_nijsse.append(compute_nijsse(np.add(reg.resid, reg.params[0])))
    elif forcings[i] == 'crowley_08':
        X = crowley_2008_forcing
        y = ts_past1000
        X = sm.add_constant(X)
        reg = sm.OLS(y, X).fit()
        ecs_vals.append(t[t['model name']==model_keys[i]]['ecs'].values[0])
        original_cox.append(compute_cox(ts_past1000))
        rv_cox.append(compute_cox(np.add(reg.resid, reg.params[0])))
        original_nijsse.append(compute_nijsse(ts_past1000))
        rv_nijsse.append(compute_nijsse(np.add(reg.resid, reg.params[0])))
    elif forcings[i] == 'evolv2k':
        X = evolv2k_forcing
        y = ts_past1000
        X = sm.add_constant(X)
        reg = sm.OLS(y, X).fit()
        ecs_vals.append(t[t['model name']==model_keys[i]]['ecs'].values[0])
        original_cox.append(compute_cox(ts_past1000))
        rv_cox.append(compute_cox(np.add(reg.resid, reg.params[0])))
        original_nijsse.append(compute_nijsse(ts_past1000))
        rv_nijsse.append(compute_nijsse(np.add(reg.resid, reg.params[0])))
        
sigma_1850 = rv_nijsse
sigma_full = ec_data['rv_nijsse']

cox_1850 = rv_cox
cox_full = ec_data['rv_cox']

ecs = ec_data['ecs']

df = pd.read_csv('data/ts.csv')
model_keys = df.keys()[2:]

sigma_historical = []
cox_historical = []

t = pd.read_csv('data/ec_data.csv')

for i in range(len(model_keys)):
    ecs_vals.append(t[t['model']==model_keys[i]]['ecs'].values[0])
    ts = df[model_keys[i]].values
    ts_historical = ts[1000:]
    if t[t['model']==model_keys[i]]['generation'].values[0] == 'CMIP5':
        cox_historical.append(compute_cox(ts_historical))
        sigma_historical.append(compute_nijsse(ts_historical))
    if t[t['model']==model_keys[i]]['generation'].values[0] == 'CMIP6':
        cox_historical.append(compute_cox(ts_historical))
        sigma_historical.append(compute_nijsse(ts_historical))
    if t[t['model']==model_keys[i]]['generation'].values[0] == 'na':
        cox_historical.append(compute_cox(ts_historical))
        sigma_historical.append(compute_nijsse(ts_historical))
        
color_1850 = 'blue'
color_historical = 'red'
color_full = 'purple'


fig, axs = plt.subplots(1, 2, figsize = (8.5, 4), sharey=True)

axs[0].scatter(cox_1850, ecs, color = color_1850, marker = 'o', label = '850-1850')
slope, intercept, r, p, se = linregress(cox_1850, ecs)
r_1850_psi = r
x=np.linspace(0.05, 0.35, 100)
axs[0].plot(x,slope*x+intercept, color = color_1850)

axs[0].scatter(cox_historical, ecs, color = color_historical, marker = 'o', label = '1850-1999')
slope, intercept, r, p, se = linregress(cox_historical, ecs)
r_historical_psi = r
x=np.linspace(0.05, 0.35, 100)
axs[0].plot(x,slope*x+intercept, color = color_historical)

axs[0].scatter(cox_full, ecs, color = color_full, marker = 'o', label = '850-1999')
slope, intercept, r, p, se = linregress(cox_full, ecs)
r_full_psi = r
x=np.linspace(0.05, 0.35, 100)
axs[0].plot(x,slope*x+intercept, color = color_full)

axs[0].set_xlim(0.06,0.28)
axs[0].set_ylim(1,5.5)

axs[1].scatter(sigma_1850, ecs, color = color_1850, marker = 'o', label = '850-1850')
slope, intercept, r, p, se = linregress(sigma_1850, ecs)
r_1850_sigma = r
x=np.linspace(0.05, 0.35, 100)
axs[1].plot(x,slope*x+intercept, color = color_1850)

axs[1].scatter(sigma_historical, ecs, color = 'red', marker = 'o', label = '1850-1999')
slope, intercept, r, p, se = linregress(sigma_historical, ecs)
r_historical_sigma = r
x=np.linspace(0.05, 0.35, 100)
axs[1].plot(x,slope*x+intercept, color = color_historical)

axs[1].scatter(sigma_full, ecs, color = color_full, marker = 'o', label = '850-1999')
slope, intercept, r, p, se = linregress(sigma_full, ecs)
r_full_sigma = r
x=np.linspace(0.05, 0.35, 100)
axs[1].plot(x,slope*x+intercept, color = color_full)

axs[1].set_xlim(0.05, 0.35)
axs[1].set_ylim(1, 5.5)


axs[1].text(0.26, 1.5, 'r='+str(np.round(r_full_sigma,2)), color = color_full)
axs[1].text(0.26, 2.1, 'r='+str(np.round(r_1850_sigma,2)), color = color_1850)
axs[1].text(0.26, 1.8, 'r='+str(np.round(r_historical_sigma,2)), color = color_historical)

axs[0].text(0.22, 1.5, 'r='+str(np.round(r_full_psi,2)), color = color_full)
axs[0].text(0.22, 2.1, 'r='+str(np.round(r_1850_psi,2)), color = color_1850)
axs[0].text(0.22, 1.8, 'r='+str(np.round(r_historical_psi,2)), color = color_historical)

fig.suptitle('ECS Emergent Relationship by Time Period Analyzed')

axs[1].legend(loc = (1.05, 0), edgecolor = 'black', framealpha = 1)

axs[0].set_ylabel('ECS (K)')
axs[0].set_xlabel(r'$\psi$ (K)')
axs[1].set_xlabel(r'$\sigma_{b}$ (K/decade)')

plt.tight_layout()
# plt.savefig('figures/figure_s2.png', dpi=3000)
../../_images/d5eb2ae55d9aad5b5f590e9679641060c488b1a30f7a0ae7dd1c827284d59d8e.png

Supplemental Figure 3 (S3)#

from matplotlib import pyplot as plt
from statsmodels.api import tsa
from scipy import signal
from scipy.stats import linregress
from scipy import stats
import numpy as np
import pandas as pd
import warnings
import xarray as xr
import random
from scipy.stats import chi2

warnings.filterwarnings('ignore')

"""Operations on cartesian geographical grid."""
import numpy as np

EARTH_RADIUS = 6371000.0  # m


def _guess_bounds(points, bound_position=0.5):
    """
    Guess bounds of grid cells.
    
    Simplified function from iris.coord.Coord.
    
    Parameters
    ----------
    points: numpy.array
        Array of grid points of shape (N,).
    bound_position: float, optional
        Bounds offset relative to the grid cell centre.
    Returns
    -------
    Array of shape (N, 2).
    """
    diffs = np.diff(points)
    diffs = np.insert(diffs, 0, diffs[0])
    diffs = np.append(diffs, diffs[-1])

    min_bounds = points - diffs[:-1] * bound_position
    max_bounds = points + diffs[1:] * (1 - bound_position)

    return np.array([min_bounds, max_bounds]).transpose()


def _quadrant_area(radian_lat_bounds, radian_lon_bounds, radius_of_earth):
    """
    Calculate spherical segment areas.
    Taken from SciTools iris library.
    Area weights are calculated for each lat/lon cell as:
        .. math::
            r^2 (lon_1 - lon_0) ( sin(lat_1) - sin(lat_0))
    The resulting array will have a shape of
    *(radian_lat_bounds.shape[0], radian_lon_bounds.shape[0])*
    The calculations are done at 64 bit precision and the returned array
    will be of type numpy.float64.
    Parameters
    ----------
    radian_lat_bounds: numpy.array
        Array of latitude bounds (radians) of shape (M, 2)
    radian_lon_bounds: numpy.array
        Array of longitude bounds (radians) of shape (N, 2)
    radius_of_earth: float
        Radius of the Earth (currently assumed spherical)
    Returns
    -------
    Array of grid cell areas of shape (M, N).
    """
    # ensure pairs of bounds
    if (
        radian_lat_bounds.shape[-1] != 2
        or radian_lon_bounds.shape[-1] != 2
        or radian_lat_bounds.ndim != 2
        or radian_lon_bounds.ndim != 2
    ):
        raise ValueError("Bounds must be [n,2] array")

    # fill in a new array of areas
    radius_sqr = radius_of_earth ** 2
    radian_lat_64 = radian_lat_bounds.astype(np.float64)
    radian_lon_64 = radian_lon_bounds.astype(np.float64)

    ylen = np.sin(radian_lat_64[:, 1]) - np.sin(radian_lat_64[:, 0])
    xlen = radian_lon_64[:, 1] - radian_lon_64[:, 0]
    areas = radius_sqr * np.outer(ylen, xlen)

    # we use abs because backwards bounds (min > max) give negative areas.
    return np.abs(areas)


def grid_cell_areas(lon1d, lat1d, radius=EARTH_RADIUS):
    """
    Calculate grid cell areas given 1D arrays of longitudes and latitudes
    for a planet with the given radius.
    
    Parameters
    ----------
    lon1d: numpy.array
        Array of longitude points [degrees] of shape (M,)
    lat1d: numpy.array
        Array of latitude points [degrees] of shape (M,)
    radius: float, optional
        Radius of the planet [metres] (currently assumed spherical)
    Returns
    -------
    Array of grid cell areas [metres**2] of shape (M, N).
    """
    lon_bounds_radian = np.deg2rad(_guess_bounds(lon1d))
    lat_bounds_radian = np.deg2rad(_guess_bounds(lat1d))
    area = _quadrant_area(lat_bounds_radian, lon_bounds_radian, radius)
    return area


def calc_spatial_mean(
    xr_da, lon_name="longitude", lat_name="latitude", radius=EARTH_RADIUS
):
    """
    Calculate spatial mean of xarray.DataArray with grid cell weighting.
    
    Parameters
    ----------
    xr_da: xarray.DataArray
        Data to average
    lon_name: str, optional
        Name of x-coordinate
    lat_name: str, optional
        Name of y-coordinate
    radius: float
        Radius of the planet [metres], currently assumed spherical (not important anyway)
    Returns
    -------
    Spatially averaged xarray.DataArray.
    """
    lon = xr_da[lon_name].values
    lat = xr_da[lat_name].values

    area_weights = grid_cell_areas(lon, lat, radius=radius)
    aw_factor = area_weights / area_weights.max()

    return (xr_da * aw_factor).mean(dim=[lon_name, lat_name])


def calc_spatial_integral(
    xr_da, lon_name="longitude", lat_name="latitude", radius=EARTH_RADIUS
):
    """
    Calculate spatial integral of xarray.DataArray with grid cell weighting.
    
    Parameters
    ----------
    xr_da: xarray.DataArray
        Data to average
    lon_name: str, optional
        Name of x-coordinate
    lat_name: str, optional
        Name of y-coordinate
    radius: float
        Radius of the planet [metres], currently assumed spherical (not important anyway)
    Returns
    -------
    Spatially averaged xarray.DataArray.
    """
    lon = xr_da[lon_name].values
    lat = xr_da[lat_name].values

    area_weights = grid_cell_areas(lon, lat, radius=radius)

    return (xr_da * area_weights).sum(dim=[lon_name, lat_name])

# computes the Cox metric for a given window
def cox_metric(x):
    auto_m1 = tsa.acf(x,nlags=1) # autocorrelation function from statsmodels
    auto_m1b = auto_m1[1]    # select 1 lag autocorrelation value
    sigma_m1= np.std(x, ddof=1) # take the sample standard deviation (fixed from previous code)
    log_m1= np.log(auto_m1b)   # compute the base-e logarithm
    log_m1b = np.abs(log_m1)   # take absolute value
    sqrt_m1 = np.sqrt(log_m1b) # take square root
    theta = sigma_m1/sqrt_m1 # compute theta
    return theta # return theta

# computes the average Cox metric across all windows, given a whole time series
def calc_cox(x):
    # x=x[1000:] # takes the most recent 1000 years
    cox_vals=np.zeros(len(x)-55) # builds array to store the cox estimates
    i=0 # window counter
    j=0 # storage counter
    while i<len(x)-55: # while loop cycles through timeseries to build windows/cox estimates
        cox_vals[j]=(cox_metric(signal.detrend(x[i:i+55])))  # detrend, compute and store the Cox estimate
        i+=1 # increment window counter
        j+=1 # increment storage counter
    # return np.mean(cox_vals) # return the mean Cox estimate across the timeseries
    return cox_vals

def calc_nijsse(x, length=10):
    x = np.array(x)
    mask = ~np.isnan(x)
    x = x[mask]
    slopes = []
    i = 0
    while i < len(x) - length:
        slope, intercept, r, p, se = linregress(np.arange(0, length), x[i:i+length])
        slopes.append(length*slope)
        i+=length
    return slopes

cox_new_1850_2000 = []
nijsse_new_1850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(ds_cps[key]))
    nijsse_vals_cps.append(calc_nijsse(ds_cps[key]))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(ds_pcr[key]))
    nijsse_vals_pcr.append(calc_nijsse(ds_pcr[key]))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(ds_m08[key]))
    nijsse_vals_m08.append(calc_nijsse(ds_m08[key]))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(ds_oie[key]))
    nijsse_vals_oie.append(calc_nijsse(ds_oie[key]))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(ds_bhm[key]))
    nijsse_vals_bhm.append(calc_nijsse(ds_bhm[key]))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(ds_pai[key]))
    nijsse_vals_pai.append(calc_nijsse(ds_pai[key]))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(ds_da[key]))
    nijsse_vals_da.append(calc_nijsse(ds_da[key]))
print('da')

cox_vals_new_1850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_1850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k = np.zeros((7, 3))
for i in range(len(cox_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_1850_2000[0])):
        u=np.nanmean(cox_vals_new_1850_2000[i][j])
        s=np.std(cox_vals_new_1850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_1850_2000[0])):
        s = np.std(nijsse_vals_new_1850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_1850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k[i][2] = np.mean(upper_bounds)

cox_new_850_2000 = []
nijsse_new_850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(ds_cps[key]))
    nijsse_vals_cps.append(calc_nijsse(ds_cps[key]))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(ds_pcr[key]))
    nijsse_vals_pcr.append(calc_nijsse(ds_pcr[key]))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(ds_m08[key]))
    nijsse_vals_m08.append(calc_nijsse(ds_m08[key]))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(ds_oie[key]))
    nijsse_vals_oie.append(calc_nijsse(ds_oie[key]))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(ds_bhm[key]))
    nijsse_vals_bhm.append(calc_nijsse(ds_bhm[key]))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(ds_pai[key]))
    nijsse_vals_pai.append(calc_nijsse(ds_pai[key]))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(ds_da[key]))
    nijsse_vals_da.append(calc_nijsse(ds_da[key]))
print('da')

cox_vals_new_850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(cox_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_850_2000[0])):
        u=np.nanmean(cox_vals_new_850_2000[i][j])
        s=np.std(cox_vals_new_850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_850_2000[0])):
        s = np.std(nijsse_vals_new_850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)
cps
pcr
m08
oie
bhm
pai
da
cps
pcr
m08
oie
bhm
pai
da
print('Cox, Original, 850-1850',np.mean(cox_estimates_pages2k.T[1]))
print('Cox, Original, 850-2000',np.mean(cox_estimates_pages2k_850_2000.T[1]))
print('Nijsse, Original, 850-1850',np.mean(nijsse_estimates_pages2k.T[1]))
print('Nijsse, Original, 850-2000',np.mean(nijsse_estimates_pages2k_850_2000.T[1]))

print('Cox, Original, 850-1850 (Spread)',np.std(cox_estimates_pages2k.T[1]))
print('Cox, Original, 850-2000 (Spread)',np.std(cox_estimates_pages2k_850_2000.T[1]))
print('Nijsse, Original, 850-1850 (Spread)',np.std(nijsse_estimates_pages2k.T[1]))
print('Nijsse, Original, 850-2000 (Spread)',np.std(nijsse_estimates_pages2k_850_2000.T[1]))
Cox, Original, 850-1850 0.09695419307044373
Cox, Original, 850-2000 0.09853884476474513
Nijsse, Original, 850-1850 0.14857331295794465
Nijsse, Original, 850-2000 0.14873197126887322
Cox, Original, 850-1850 (Spread) 0.022394243251457023
Cox, Original, 850-2000 (Spread) 0.020106711340539315
Nijsse, Original, 850-1850 (Spread) 0.036307284813961054
Nijsse, Original, 850-2000 (Spread) 0.034638026746437865
evolv2k_ts = weighted_mean(xr.open_dataset('data/evolv2k.nc')).sel(time=slice(850,1850))
evolv2k_saod = []
i=850
while i <= 1850:
    evolv2k_saod.append(float(evolv2k_ts.sel(time=slice(i, i+1)).mean(dim='time').aod550.values))
    i+=1
evolv2k_forcing = get_forcing(evolv2k_saod)

def remove_forcing(ts):
    X = evolv2k_forcing
    y = ts[:1001]
    X = sm.add_constant(X)
    reg = sm.OLS(y, X).fit()
    ts_past1000 = reg.resid
    ts_hist = np.subtract(np.array(ts[1001:]),ts[1001])
    return np.append(ts_past1000,ts_hist)

# computes the Cox metric for a given window
def cox_metric(x):
    auto_m1 = tsa.acf(x,nlags=1) # autocorrelation function from statsmodels
    auto_m1b = auto_m1[1]    # select 1 lag autocorrelation value
    sigma_m1= np.std(x, ddof=1) # take the sample standard deviation (fixed from previous code)
    log_m1= np.log(auto_m1b)   # compute the base-e logarithm
    log_m1b = np.abs(log_m1)   # take absolute value
    sqrt_m1 = np.sqrt(log_m1b) # take square root
    theta = sigma_m1/sqrt_m1 # compute theta
    return theta # return theta

# computes the average Cox metric across all windows, given a whole time series
def calc_cox(x):
    # x=x[1000:] # takes the most recent 1000 years
    cox_vals=np.zeros(len(x)-55) # builds array to store the cox estimates
    i=0 # window counter
    j=0 # storage counter
    while i<len(x)-55: # while loop cycles through timeseries to build windows/cox estimates
        cox_vals[j]=(cox_metric(signal.detrend(x[i:i+55])))  # detrend, compute and store the Cox estimate
        i+=1 # increment window counter
        j+=1 # increment storage counter
    # return np.mean(cox_vals) # return the mean Cox estimate across the timeseries
    return cox_vals

def calc_nijsse(x, length=10):
    x = np.array(x)
    mask = ~np.isnan(x)
    x = x[mask]
    slopes = []
    i = 0
    while i < len(x) - length:
        slope, intercept, r, p, se = linregress(np.arange(0, length), x[i:i+length])
        slopes.append(length*slope)
        i+=length
    return slopes

cox_new_1850_2000 = []
nijsse_new_1850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(remove_forcing(ds_cps[key])))
    nijsse_vals_cps.append(calc_nijsse(remove_forcing(ds_cps[key])))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(remove_forcing(ds_pcr[key])))
    nijsse_vals_pcr.append(calc_nijsse(remove_forcing(ds_pcr[key])))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(remove_forcing(ds_m08[key])))
    nijsse_vals_m08.append(calc_nijsse(remove_forcing(ds_m08[key])))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(remove_forcing(ds_oie[key])))
    nijsse_vals_oie.append(calc_nijsse(remove_forcing(ds_oie[key])))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(remove_forcing(ds_bhm[key])))
    nijsse_vals_bhm.append(calc_nijsse(remove_forcing(ds_bhm[key])))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(remove_forcing(ds_pai[key])))
    nijsse_vals_pai.append(calc_nijsse(remove_forcing(ds_pai[key])))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<=1850)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(remove_forcing(ds_da[key])))
    nijsse_vals_da.append(calc_nijsse(remove_forcing(ds_da[key])))
print('da')

cox_vals_new_1850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_1850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k = np.zeros((7, 3))
for i in range(len(cox_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_1850_2000[0])):
        u=np.nanmean(cox_vals_new_1850_2000[i][j])
        s=np.std(cox_vals_new_1850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_1850_2000[0])):
        s = np.std(nijsse_vals_new_1850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_1850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k[i][2] = np.mean(upper_bounds)

cox_new_850_2000 = []
nijsse_new_850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(remove_forcing(ds_cps[key])))
    nijsse_vals_cps.append(calc_nijsse(remove_forcing(ds_cps[key])))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(remove_forcing(ds_pcr[key])))
    nijsse_vals_pcr.append(calc_nijsse(remove_forcing(ds_pcr[key])))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(remove_forcing(ds_m08[key])))
    nijsse_vals_m08.append(calc_nijsse(remove_forcing(ds_m08[key])))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(remove_forcing(ds_oie[key])))
    nijsse_vals_oie.append(calc_nijsse(remove_forcing(ds_oie[key])))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(remove_forcing(ds_bhm[key])))
    nijsse_vals_bhm.append(calc_nijsse(remove_forcing(ds_bhm[key])))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(remove_forcing(ds_pai[key])))
    nijsse_vals_pai.append(calc_nijsse(remove_forcing(ds_pai[key])))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(remove_forcing(ds_da[key])))
    nijsse_vals_da.append(calc_nijsse(remove_forcing(ds_da[key])))
print('da')

cox_vals_new_850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(cox_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_850_2000[0])):
        u=np.nanmean(cox_vals_new_850_2000[i][j])
        s=np.std(cox_vals_new_850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_850_2000[0])):
        s = np.std(nijsse_vals_new_850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)
cps
pcr
m08
oie
bhm
pai
da
cps
pcr
m08
oie
bhm
pai
da
print(np.mean(cox_estimates_pages2k.T[1]),
      np.mean(cox_estimates_pages2k_850_2000.T[1]),
      np.mean(nijsse_estimates_pages2k.T[1]),
      np.mean(nijsse_estimates_pages2k_850_2000.T[1]))

print(np.std(cox_estimates_pages2k.T[1]),
      np.std(cox_estimates_pages2k_850_2000.T[1]),
      np.std(nijsse_estimates_pages2k.T[1]),
      np.std(nijsse_estimates_pages2k_850_2000.T[1]))
0.09485802708326348 0.09702184436805132 0.14567803698816997 0.14686811787714496
0.022357431751678373 0.020184056441559982 0.03584102093823073 0.03430060336507687
from matplotlib import pyplot as plt
from statsmodels.api import tsa
from scipy import signal
from scipy.stats import linregress
from scipy import stats
import numpy as np
import pandas as pd
import warnings
import xarray as xr
import random
from scipy.stats import chi2

warnings.filterwarnings('ignore')

"""Operations on cartesian geographical grid."""
import numpy as np

EARTH_RADIUS = 6371000.0  # m


def _guess_bounds(points, bound_position=0.5):
    """
    Guess bounds of grid cells.
    
    Simplified function from iris.coord.Coord.
    
    Parameters
    ----------
    points: numpy.array
        Array of grid points of shape (N,).
    bound_position: float, optional
        Bounds offset relative to the grid cell centre.
    Returns
    -------
    Array of shape (N, 2).
    """
    diffs = np.diff(points)
    diffs = np.insert(diffs, 0, diffs[0])
    diffs = np.append(diffs, diffs[-1])

    min_bounds = points - diffs[:-1] * bound_position
    max_bounds = points + diffs[1:] * (1 - bound_position)

    return np.array([min_bounds, max_bounds]).transpose()


def _quadrant_area(radian_lat_bounds, radian_lon_bounds, radius_of_earth):
    """
    Calculate spherical segment areas.
    Taken from SciTools iris library.
    Area weights are calculated for each lat/lon cell as:
        .. math::
            r^2 (lon_1 - lon_0) ( sin(lat_1) - sin(lat_0))
    The resulting array will have a shape of
    *(radian_lat_bounds.shape[0], radian_lon_bounds.shape[0])*
    The calculations are done at 64 bit precision and the returned array
    will be of type numpy.float64.
    Parameters
    ----------
    radian_lat_bounds: numpy.array
        Array of latitude bounds (radians) of shape (M, 2)
    radian_lon_bounds: numpy.array
        Array of longitude bounds (radians) of shape (N, 2)
    radius_of_earth: float
        Radius of the Earth (currently assumed spherical)
    Returns
    -------
    Array of grid cell areas of shape (M, N).
    """
    # ensure pairs of bounds
    if (
        radian_lat_bounds.shape[-1] != 2
        or radian_lon_bounds.shape[-1] != 2
        or radian_lat_bounds.ndim != 2
        or radian_lon_bounds.ndim != 2
    ):
        raise ValueError("Bounds must be [n,2] array")

    # fill in a new array of areas
    radius_sqr = radius_of_earth ** 2
    radian_lat_64 = radian_lat_bounds.astype(np.float64)
    radian_lon_64 = radian_lon_bounds.astype(np.float64)

    ylen = np.sin(radian_lat_64[:, 1]) - np.sin(radian_lat_64[:, 0])
    xlen = radian_lon_64[:, 1] - radian_lon_64[:, 0]
    areas = radius_sqr * np.outer(ylen, xlen)

    # we use abs because backwards bounds (min > max) give negative areas.
    return np.abs(areas)


def grid_cell_areas(lon1d, lat1d, radius=EARTH_RADIUS):
    """
    Calculate grid cell areas given 1D arrays of longitudes and latitudes
    for a planet with the given radius.
    
    Parameters
    ----------
    lon1d: numpy.array
        Array of longitude points [degrees] of shape (M,)
    lat1d: numpy.array
        Array of latitude points [degrees] of shape (M,)
    radius: float, optional
        Radius of the planet [metres] (currently assumed spherical)
    Returns
    -------
    Array of grid cell areas [metres**2] of shape (M, N).
    """
    lon_bounds_radian = np.deg2rad(_guess_bounds(lon1d))
    lat_bounds_radian = np.deg2rad(_guess_bounds(lat1d))
    area = _quadrant_area(lat_bounds_radian, lon_bounds_radian, radius)
    return area


def calc_spatial_mean(
    xr_da, lon_name="longitude", lat_name="latitude", radius=EARTH_RADIUS
):
    """
    Calculate spatial mean of xarray.DataArray with grid cell weighting.
    
    Parameters
    ----------
    xr_da: xarray.DataArray
        Data to average
    lon_name: str, optional
        Name of x-coordinate
    lat_name: str, optional
        Name of y-coordinate
    radius: float
        Radius of the planet [metres], currently assumed spherical (not important anyway)
    Returns
    -------
    Spatially averaged xarray.DataArray.
    """
    lon = xr_da[lon_name].values
    lat = xr_da[lat_name].values

    area_weights = grid_cell_areas(lon, lat, radius=radius)
    aw_factor = area_weights / area_weights.max()

    return (xr_da * aw_factor).mean(dim=[lon_name, lat_name])


def calc_spatial_integral(
    xr_da, lon_name="longitude", lat_name="latitude", radius=EARTH_RADIUS
):
    """
    Calculate spatial integral of xarray.DataArray with grid cell weighting.
    
    Parameters
    ----------
    xr_da: xarray.DataArray
        Data to average
    lon_name: str, optional
        Name of x-coordinate
    lat_name: str, optional
        Name of y-coordinate
    radius: float
        Radius of the planet [metres], currently assumed spherical (not important anyway)
    Returns
    -------
    Spatially averaged xarray.DataArray.
    """
    lon = xr_da[lon_name].values
    lat = xr_da[lat_name].values

    area_weights = grid_cell_areas(lon, lat, radius=radius)

    return (xr_da * area_weights).sum(dim=[lon_name, lat_name])

# computes the Cox metric for a given window
def cox_metric(x):
    auto_m1 = tsa.acf(x,nlags=1) # autocorrelation function from statsmodels
    auto_m1b = auto_m1[1]    # select 1 lag autocorrelation value
    sigma_m1= np.std(x, ddof=1) # take the sample standard deviation (fixed from previous code)
    log_m1= np.log(auto_m1b)   # compute the base-e logarithm
    log_m1b = np.abs(log_m1)   # take absolute value
    sqrt_m1 = np.sqrt(log_m1b) # take square root
    theta = sigma_m1/sqrt_m1 # compute theta
    return theta # return theta

# computes the average Cox metric across all windows, given a whole time series
def calc_cox(x):
    # x=x[1000:] # takes the most recent 1000 years
    cox_vals=np.zeros(len(x)-55) # builds array to store the cox estimates
    i=0 # window counter
    j=0 # storage counter
    while i<len(x)-55: # while loop cycles through timeseries to build windows/cox estimates
        cox_vals[j]=(cox_metric(signal.detrend(x[i:i+55])))  # detrend, compute and store the Cox estimate
        i+=1 # increment window counter
        j+=1 # increment storage counter
    # return np.mean(cox_vals) # return the mean Cox estimate across the timeseries
    return cox_vals

def calc_nijsse(x, length=10):
    x = np.array(x)
    mask = ~np.isnan(x)
    x = x[mask]
    slopes = []
    i = 0
    while i < len(x) - length:
        slope, intercept, r, p, se = linregress(np.arange(0, length), x[i:i+length])
        slopes.append(length*slope)
        i+=length
    return slopes

cox_new_1850_2000 = []
nijsse_new_1850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(ds_cps[key]))
    nijsse_vals_cps.append(calc_nijsse(ds_cps[key]))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(ds_pcr[key]))
    nijsse_vals_pcr.append(calc_nijsse(ds_pcr[key]))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(ds_m08[key]))
    nijsse_vals_m08.append(calc_nijsse(ds_m08[key]))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(ds_oie[key]))
    nijsse_vals_oie.append(calc_nijsse(ds_oie[key]))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(ds_bhm[key]))
    nijsse_vals_bhm.append(calc_nijsse(ds_bhm[key]))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(ds_pai[key]))
    nijsse_vals_pai.append(calc_nijsse(ds_pai[key]))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=1850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(ds_da[key]))
    nijsse_vals_da.append(calc_nijsse(ds_da[key]))
print('da')

cox_vals_new_1850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_1850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k = np.zeros((7, 3))
for i in range(len(cox_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_1850_2000[0])):
        u=np.nanmean(cox_vals_new_1850_2000[i][j])
        s=np.std(cox_vals_new_1850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_1850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_1850_2000[0])):
        s = np.std(nijsse_vals_new_1850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_1850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k[i][2] = np.mean(upper_bounds)

cox_estimate_hadcrut4 = np.zeros(3)
for i in range(1,101):
    ts = calc_cox(calc_spatial_mean(xr.open_dataset('data/HadCRUT4/HadCRUT.4.6.0.0.anomalies.'+str(i)+'.nc').groupby('time.year').mean('time').sel(year=slice(1850,2000)).temperature_anomaly))
    u = np.mean(ts)
    s = np.std(ts, ddof=1)
    cox_estimate_hadcrut4[0] = u-0.95*s
    cox_estimate_hadcrut4[1] = u
    cox_estimate_hadcrut4[2] = u+0.95*s

nijsse_estimate_hadcrut4 = np.zeros(3)
for i in range(1,101):
    ts = calc_nijsse(calc_spatial_mean(xr.open_dataset('data/HadCRUT4/HadCRUT.4.6.0.0.anomalies.'+str(i)+'.nc').groupby('time.year').mean('time').sel(year=slice(1850,2000)).temperature_anomaly))
    s = np.std(ts, ddof=1)
    n = len(ts)
    nijsse_estimate_hadcrut4[0] = np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n))
    nijsse_estimate_hadcrut4[1] = s
    nijsse_estimate_hadcrut4[2] = np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n))

cox_new_850_2000 = []
nijsse_new_850_2000 = []

PAGES=pd.read_csv('data/pages_2k/CPS.txt',sep='\t',header=(0))
ds_cps=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_cps = []
nijsse_vals_cps = []
for key in ds_cps.keys():
    cox_vals_cps.append(calc_cox(ds_cps[key]))
    nijsse_vals_cps.append(calc_nijsse(ds_cps[key]))
print('cps')
PAGES=pd.read_csv('data/pages_2k/PCR.txt',sep='\t',header=(0))
ds_pcr=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pcr = []
nijsse_vals_pcr = []
for key in ds_pcr.keys():
    cox_vals_pcr.append(calc_cox(ds_pcr[key]))
    nijsse_vals_pcr.append(calc_nijsse(ds_pcr[key]))
print('pcr')
PAGES=pd.read_csv('data/pages_2k/M08.txt',sep='\t',header=(0))
ds_m08=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_m08 = []
nijsse_vals_m08 = []
for key in ds_m08.keys():
    cox_vals_m08.append(calc_cox(ds_m08[key]))
    nijsse_vals_m08.append(calc_nijsse(ds_m08[key]))
print('m08')
PAGES=pd.read_csv('data/pages_2k/OIE.txt',sep='\t',header=(0))
ds_oie=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_oie = []
nijsse_vals_oie = []
for key in ds_oie.keys():
    cox_vals_oie.append(calc_cox(ds_oie[key]))
    nijsse_vals_oie.append(calc_nijsse(ds_oie[key]))
print('oie')
PAGES=pd.read_csv('data/pages_2k/BHM.txt',sep='\t',header=(0))
ds_bhm=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_bhm = []
nijsse_vals_bhm = []
for key in ds_bhm.keys():
    cox_vals_bhm.append(calc_cox(ds_bhm[key]))
    nijsse_vals_bhm.append(calc_nijsse(ds_bhm[key]))
print('bhm')
PAGES=pd.read_csv('data/pages_2k/PAI.txt',sep='\t',header=(0))
ds_pai=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_pai = []
nijsse_vals_pai = []
for key in ds_pai.keys():
    cox_vals_pai.append(calc_cox(ds_pai[key]))
    nijsse_vals_pai.append(calc_nijsse(ds_pai[key]))
print('pai')
PAGES=pd.read_csv('data/pages_2k/DA.txt',sep='\t',header=(0))
ds_da=PAGES[(PAGES['Year_CE']>=850) & (PAGES['Year_CE']<2000)].drop('Year_CE', axis=1)
cox_vals_da = []
nijsse_vals_da = []
for key in ds_da.keys():
    cox_vals_da.append(calc_cox(ds_da[key]))
    nijsse_vals_da.append(calc_nijsse(ds_da[key]))
print('da')

cox_vals_new_850_2000 = [
    cox_vals_cps,
    cox_vals_pcr,
    cox_vals_m08,
    cox_vals_oie,
    cox_vals_bhm,
    cox_vals_pai,
    cox_vals_da,
]

nijsse_vals_new_850_2000 = [
    nijsse_vals_cps,
    nijsse_vals_pcr,
    nijsse_vals_m08,
    nijsse_vals_oie,
    nijsse_vals_bhm,
    nijsse_vals_pai,
    nijsse_vals_da,
]

cox_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(cox_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(cox_vals_new_850_2000[0])):
        u=np.nanmean(cox_vals_new_850_2000[i][j])
        s=np.std(cox_vals_new_850_2000[i][j], ddof=1)
        central_estimates.append(u)
        lower_bounds.append(u-0.95*s)
        upper_bounds.append(u+0.95*s)
    cox_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    cox_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    cox_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)

nijsse_estimates_pages2k_850_2000 = np.zeros((7, 3))
for i in range(len(nijsse_vals_new_850_2000)):
    lower_bounds = []
    central_estimates = []
    upper_bounds = []
    for j in range(len(nijsse_vals_new_850_2000[0])):
        s = np.std(nijsse_vals_new_850_2000[i][j], ddof=1)
        n = len(nijsse_vals_new_850_2000[i][j])
        central_estimates.append(s)
        lower_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(1-(0.34/2), n)))
        upper_bounds.append(np.sqrt((n-1)*s**2/chi2.ppf(0.34/2, n)))
    nijsse_estimates_pages2k_850_2000[i][0] = np.mean(lower_bounds)
    nijsse_estimates_pages2k_850_2000[i][1] = np.mean(central_estimates)
    nijsse_estimates_pages2k_850_2000[i][2] = np.mean(upper_bounds)
cps
pcr
m08
oie
bhm
pai
da
cps
pcr
m08
oie
bhm
pai
da
fig, axs = plt.subplots(2, 2, figsize = (8.5, 7), sharey=True, sharex=True)
methods = ['CPS', 'PCR', 'M08', 'OIE', 'BHM', 'PAI', 'DA']
x = methods
axs[0][0].set_title(r'$\psi$'+' from 1850-1999')
axs[0][0].axhline(y=np.mean(cox_estimates_pages2k.T[1]), color='blue', linestyle = '--')
axs[0][0].axhspan(np.mean(cox_estimates_pages2k.T[1])-0.95*np.std(cox_estimates_pages2k.T[1]),
                  np.mean(cox_estimates_pages2k.T[1])+0.95*np.std(cox_estimates_pages2k.T[1]),
                  color = 'blue', alpha = 0.1)
axs[0][0].errorbar(x, cox_estimates_pages2k.T[1],
                   yerr=[np.abs(np.subtract(cox_estimates_pages2k.T[1],cox_estimates_pages2k.T[0])), np.abs(np.subtract(cox_estimates_pages2k.T[2],cox_estimates_pages2k.T[1]))],
                   fmt='o', capsize=5)
axs[0][0].axhline(y=cox_estimate_hadcrut4[1], color='green')
axs[0][0].axhspan(cox_estimate_hadcrut4[0], cox_estimate_hadcrut4[2], color = 'green', alpha = 0.1)

axs[0][1].set_title(r'$\psi$'+' from 850-1999')
axs[0][1].axhline(y=np.mean(cox_estimates_pages2k_850_2000.T[1]), color='blue', linestyle = '--')
axs[0][1].axhspan(np.mean(cox_estimates_pages2k_850_2000.T[1])-0.95*np.std(cox_estimates_pages2k_850_2000.T[1]),
                  np.mean(cox_estimates_pages2k_850_2000.T[1])+0.95*np.std(cox_estimates_pages2k_850_2000.T[1]),
                  color = 'blue', alpha = 0.1)
axs[0][1].errorbar(x, cox_estimates_pages2k_850_2000.T[1],
                   yerr=[np.abs(np.subtract(cox_estimates_pages2k_850_2000.T[1],cox_estimates_pages2k_850_2000.T[0])), np.abs(np.subtract(cox_estimates_pages2k_850_2000.T[2],cox_estimates_pages2k_850_2000.T[1]))],
                   fmt='o', capsize=5)

axs[1][0].set_title(r'$\sigma_{b}$'+' from 1850-1999')
axs[1][0].axhline(y=np.mean(nijsse_estimates_pages2k.T[1]), color='blue', linestyle = '--', label = 'PAGES 2k Central Estimate')
axs[1][0].axhspan(np.mean(nijsse_estimates_pages2k.T[1])-0.95*np.std(nijsse_estimates_pages2k.T[1]),
                  np.mean(nijsse_estimates_pages2k.T[1])+0.95*np.std(nijsse_estimates_pages2k.T[1]),
                  color = 'blue', alpha = 0.1, label = 'PAGES 2k 66% Likely Range')
axs[1][0].errorbar(x, nijsse_estimates_pages2k.T[1],
                   yerr=[np.abs(np.subtract(nijsse_estimates_pages2k.T[1],nijsse_estimates_pages2k.T[0])), np.abs(np.subtract(nijsse_estimates_pages2k.T[2],nijsse_estimates_pages2k.T[1]))],
                   fmt='o', capsize=5)
axs[1][0].axhline(y=nijsse_estimate_hadcrut4[1], color='green', label = 'HadCRUT4 Central Estimate')
axs[1][0].axhspan(nijsse_estimate_hadcrut4[0], nijsse_estimate_hadcrut4[2], color = 'green', alpha = 0.1, label = 'HadCRUT4 66% Likely Range')

axs[1][1].set_title(r'$\sigma_{b}$'+' from 850-1999')
axs[1][1].axhline(y=np.mean(nijsse_estimates_pages2k_850_2000.T[1]), color='blue', linestyle = '--')
axs[1][1].axhspan(np.mean(nijsse_estimates_pages2k_850_2000.T[1])-0.95*np.std(nijsse_estimates_pages2k_850_2000.T[1]),
                  np.mean(nijsse_estimates_pages2k_850_2000.T[1])+0.95*np.std(nijsse_estimates_pages2k_850_2000.T[1]),
                  color = 'blue', alpha = 0.1)
axs[1][1].errorbar(x, nijsse_estimates_pages2k_850_2000.T[1],
                   yerr=[np.abs(np.subtract(nijsse_estimates_pages2k_850_2000.T[1],nijsse_estimates_pages2k_850_2000.T[0])), np.abs(np.subtract(nijsse_estimates_pages2k_850_2000.T[2],nijsse_estimates_pages2k_850_2000.T[1]))],
                   fmt='o', capsize=5, label='Individual PAGES 2k Reconstruction\n(Central Estimate + 66% Likely)')

axs[0][0].set_ylabel(r'$\psi$ (K)')
axs[1][0].set_ylabel(r'$\sigma_{b}$ (K/decade)')

axs[1][0].set_xlabel('Methodology')
axs[1][1].set_xlabel('Methodology')

fig.legend(loc='center left', bbox_to_anchor=(1, 0.5))

fig.suptitle('Temperature Variability Metrics according to PAGES 2k\nby Reconstruction Methodology and Period')

plt.tight_layout()
plt.savefig('figures/figure_s3.png', dpi=1000, bbox_inches='tight')
../../_images/e8af548da21ec9fdaf095bba0a6a228caa6a093e90eda4b4119f9b6ea02194ee.png

Supplemental Figure 4 (S4)#

ec_data = pd.read_csv('data/ec_data.csv').drop('Unnamed: 0', axis=1)

# read in the timeseries dataframe
timeseries_df = pd.read_csv('data/ts.csv').drop('Unnamed: 0', axis=1)

# the columns of the dataframe are the model names
model_names = timeseries_df.columns[1:]

fig, axs = plt.subplots(1, 2, sharey=True, figsize=(8.5, 4))

fig.suptitle('ECS Emergent Relationship (Models Labeled)')
axs[0].set_ylabel('ECS (K)')
axs[0].set_xlabel(r'$\psi$ (K)')
axs[1].set_xlabel(r'$\sigma_{b}$ (K/decade)')
axs[0].set_xlim((0.05,0.27))
axs[0].set_ylim((1.1,5.5))
axs[1].set_ylim((1.1,5.5))
axs[1].set_xlim((0.11,0.35))

alphabet = ['A', 'B', 'C', 'D',
            'E', 'F', 'G', 'H',
            'I', 'J', 'K', 'L',
            'M', 'N', 'O', 'P',
            'Q', 'R']

j = 5.6
for i in range(len(ec_data)):
    axs[0].text(ec_data['rv_cox'][i],
               ec_data['ecs'][i],
               alphabet[i])
    
    axs[1].text(ec_data['rv_nijsse'][i],
           ec_data['ecs'][i],
           alphabet[i])
    
    axs[1].text(0.4, j, alphabet[i] + ' - '+model_names[i])
    j -= 0.3

plt.tight_layout()
plt.savefig('figures/figure_s4.png', dpi=3000)
../../_images/88734d047c97a7b38b0481d693e9a9cc84a9c717c8683701bb1ea95c659cef48.png

Supplemental Figure 5 (S5)#

available in the notebook, ‘pp2k-ppe-pda.ipynb’

Supplemental Figure 6 (S6)#

def get_lat_name(ds):
    """Figure out what is the latitude coordinate for each dataset."""
    for lat_name in ['lat', 'latitude']:
        if lat_name in ds.coords:
            return lat_name
    raise RuntimeError("Couldn't find a latitude coordinate")

def fix_up_dat(dat):
    try:
        dat=dat.rename({"longitude":"lon", "latitude":"lat"}) #problematic coord names
    except: pass
    
    dat = xr.decode_cf(dat, use_cftime = True)
    return dat

def weighted_mean(da):
    
    # make 2d array of weights in case that lat is 1d
    if len(da.lat.shape)==2:
        weights=np.cos(np.deg2rad(da.lat))
    elif len(da.lat.shape)==1:
        weights = xr.ones_like(da)* (np.cos(np.deg2rad((da.lat))).values)[:,np.newaxis]
        
    # turn weights into nan where da is nan
    weights = weights*da/da
    
    
    if 'lat' in da.dims:
        wm = (da*weights).sum(dim=['lat','lon']) / weights.sum(dim=['lat','lon'])

    elif 'i' in da.dims:
        wm = (da*weights).sum(dim=['i','j']) / weights.sum(dim=['i','j'])
    elif 'nlat' in da.dims:
        wm = (da*weights).sum(dim=['nlat','nlon']) / weights.sum(dim=['nlat','nlon'])
    elif 'x' in da.dims:
        wm = (da*weights).sum(dim=['x','y']) / weights.sum(dim=['x','y'])
    return wm

def compute_cox(x):
    x = x[~np.isnan(x)]
    psi_vals=[]
    for i in np.arange(0, len(x)-55):
        y = signal.detrend(x[i:i+55])
        auto_m1 = tsa.acf(y,nlags=1) # autocorrelation function from statsmodels
        auto_m1b = auto_m1[1]    # select 1 lag autocorrelation value
        sigma_m1= np.std(y)
        log_m1= np.log(auto_m1b)
        log_m1b = np.abs(log_m1)   # take absolute value
        sqrt_m1 = np.sqrt(log_m1b)
        psi = sigma_m1/sqrt_m1
        psi_vals.append(psi)
    return np.nanmean(psi_vals)

def compute_nijsse(x, length=10):
    # remove NaNs from the timeseries
    x = np.array(x)
    mask = ~np.isnan(x)
    x = x[mask]
    # fill it with slopes
    slopes = []
    i = 0
    while i < len(x)-length:
        slope, intercept, r, p, se = linregress(np.arange(0,length), x[i:i+length])
        slopes.append(length*slope)
        i+=length
    return np.nanstd(slopes)

def get_ecs(model_name):
    ecs_df = pd.read_csv('ecs.txt', header=7, delim_whitespace=True)
    return ecs_df[ecs_df['MODEL'] == model_name]['ECS'].values[0]

def construct_model_ts(zstore):
    dat = fix_up_dat(xr.open_zarr(gcs.get_mapper(zstore), consolidated=True))
    return weighted_mean(dat.tas.resample(time='1Y').mean('time')).values

cmip5 = {}
for pair in cmip5_schlund:
    model, ecs = pair
    try:
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/'+model+'.nc').resample(time='1Y').mean('time').tas)
        cmip5[model] = ts[:150]
    except:
        continue
        
cmip6 = {}
for pair in cmip6_schlund:
    model, ecs = pair
    try:
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/'+model+'.nc').resample(time='1Y').mean('time').tas)
        cmip6[model] = ts[:450]
    except:
        continue
        
paleomodels = [
    'bcc-csm1-1', 'CCSM4', 'CSIRO-Mk3L-1-2', 'FGOALS-gl',
    'GISS-E2-R', 'HadCM3', 'IPSL-CM5A-LR', 'MIROC-ES2L',
    'MPI-ESM-P', 'MRI-CGCM3', 'MRI-ESM2-0', 'CESM1-LME',
    'CESM2', 'MPI-ESM1-2-LR', 'EC-Earth3-Veg-LR', 'MIROC-ESM',
    'INM-CM4-8', 'ACCESS-ESM1-5'
]

df=pd.read_csv('data/ec_data.csv')

x=[]
y=[]
z=[]
model_names=[]

for model in paleomodels:
    if model=='EC-Earth3-Veg-LR':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/EC-Earth3-Veg-LR.nc').resample(time='1Y').mean('time').tas)
        x.append(compute_cox(ts))
        y.append(float(df[df['model']==model]['rv_cox']))
        z.append(float(df[df['model']==model]['ecs']))
        model_names.append(model)
        continue
    if model=='CESM1-LME':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/CESM1-CAM5.nc').resample(time='1Y').mean('time').tas)
        x.append(compute_cox(ts))
        y.append(float(df[df['model']==model]['rv_cox']))
        z.append(float(df[df['model']==model]['ecs']))
        model_names.append(model)
        continue
    if model=='HadCM3':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/HadCM3.nc').resample(time='1Y').mean('time').tas)
        x.append(compute_cox(ts))
        y.append(float(df[df['model']==model]['rv_cox']))
        z.append(float(df[df['model']==model]['ecs']))
        model_names.append(model)
        continue
    try:
        ts=cmip6[model]
        x.append(compute_cox(ts))
        y.append(float(df[df['model']==model]['rv_cox']))
        z.append(float(df[df['model']==model]['ecs']))   
        model_names.append(model)
        continue
    except:
        try:
            ts=cmip5[model]
            x.append(compute_cox(ts))
            y.append(float(df[df['model']==model]['rv_cox']))
            z.append(float(df[df['model']==model]['ecs']))
            model_names.append(model)
            continue
        except:
            continue
            
x2=[]
y2=[]

for model in paleomodels:
    if model=='EC-Earth3-Veg-LR':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/EC-Earth3-Veg-LR.nc').resample(time='1Y').mean('time').tas)
        x2.append(compute_nijsse(ts))
        y2.append(float(df[df['model']==model]['rv_nijsse']))
        continue
    if model=='CESM1-LME':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/CESM1-CAM5.nc').resample(time='1Y').mean('time').tas)
        x2.append(compute_nijsse(ts))
        y2.append(float(df[df['model']==model]['rv_nijsse']))
        continue
    if model=='HadCM3':
        ts=weighted_mean(xr.open_dataset('../../../pi_control_data/HadCM3.nc').resample(time='1Y').mean('time').tas)
        x2.append(compute_nijsse(ts))
        y2.append(float(df[df['model']==model]['rv_nijsse']))
        continue
    try:
        ts=cmip6[model]
        x2.append(compute_nijsse(ts))
        y2.append(float(df[df['model']==model]['rv_nijsse']))
        continue
    except:
        try:
            ts=cmip5[model]
            x2.append(compute_nijsse(ts))
            y2.append(float(df[df['model']==model]['rv_nijsse']))
            continue
        except:
            continue
            

model = model_names            

# Create a figure with two subplots
# fig, axs = plt.subplots(1, 2, figsize=(15, 6), gridspec_kw={'wspace': 0.0})
fig, axs = plt.subplots(1, 2, figsize=(17, 7),)

# First subplot
scatter1 = axs[0].scatter(x, y, s=np.multiply(7, np.array(z)), c='blue', alpha=0.7)
slope, intercept, r_value, p_value, std_err = linregress(x, y)
correlation_info = f'r = {r_value:.2f}, p = {p_value:.2f}'
axs[0].annotate(correlation_info, (0.05, 0.95), xycoords='axes fraction').set_size(15)
axs[0].set_title('Comparison of $\psi$, piControl vs. past1000 Experiments')
axs[0].set_xlabel('$\psi$ in piControl experiment')
axs[0].set_ylabel('$\psi$ in past1000 experiment')
axs[0].set_xlim(0.05,0.15)
axs[0].set_ylim(0.09,0.26)
texts1 = [axs[0].text(x[i]-0.003, y[i]+0.005, model[i], ha='center', va='center', fontsize=9) for i in range(len(model))]
axs[0].grid(False)

# Second subplot
scatter2 = axs[1].scatter(x2, y2, s=np.multiply(7, np.array(z)), c='blue', alpha=0.7)
slope, intercept, r_value, p_value, std_err = linregress(x2, y2)
correlation_info = f'r = {r_value:.2f}, p = {p_value:.2f}'
axs[1].annotate(correlation_info, (0.05, 0.95), xycoords='axes fraction').set_size(15)
axs[1].set_title('Comparison of $\psi$, piControl vs. past1000 Experiments')
axs[1].set_xlabel('$\sigma_{b}$ in piControl experiment')
axs[1].set_ylabel('$\sigma_{b}$ in past1000 experiment')
axs[1].set_xlim(0.075,0.2)
axs[1].set_ylim(0.13,0.33)
texts2 = [axs[1].text(x2[i]-0.003, y2[i]+0.005, model[i], ha='center', va='center', fontsize=9) for i in range(len(model))]
axs[1].grid(False)

# Adjust layout and show the plot
plt.tight_layout()
plt.savefig('figures/figure_s6.png',dpi=1000)
../../_images/8f899555982a9cbf661fd2c517e1cfbdb0ca09740293eabbcd21446b01a373f4.png