SECOORA Notebook 2

Sea Surface Temperature time-series model skill

This notebook calculates several skill scores for the SECOORA models weekly time-series saved by 00-fetch_data.ipynb.

Load configuration


In [1]:
import os
try:
    import cPickle as pickle
except ImportError:
    import pickle


run_name = '2014-07-07'
fname = os.path.join(run_name, 'config.pkl')
with open(fname, 'rb') as f:
    config = pickle.load(f)

In [2]:
import numpy as np
from pandas import DataFrame, read_csv
from utilities import (load_secoora_ncs, to_html,
                       save_html, apply_skill)


fname = '{}-all_obs.csv'.format(run_name)
all_obs = read_csv(os.path.join(run_name, fname), index_col='name')


def rename_cols(df):
    columns = dict()
    for station in df.columns:
        mask = all_obs['station'] == station
        name = all_obs['station'][mask].index[0]
        columns.update({station: name})
    return df.rename(columns=columns)

Skill 1: Model Bias (or Mean Bias)

The bias skill compares the model mean temperature against the observations. It is possible to introduce a Mean Bias in the model due to a mismatch of the boundary forcing and the model interior.

$$ \text{MB} = \mathbf{\overline{m}} - \mathbf{\overline{o}}$$

In [3]:
from utilities import mean_bias

dfs = load_secoora_ncs(run_name)

df = apply_skill(dfs, mean_bias, remove_mean=False, filter_tides=False)
df = rename_cols(df)
skill_score = dict(mean_bias=df.copy())

# Filter out stations with no valid comparison.
df.dropna(how='all', axis=1, inplace=True)
df = df.applymap('{:.2f}'.format).replace('nan', '--')

html = to_html(df.T)
fname = os.path.join(run_name, 'mean_bias.html'.format(run_name))
save_html(fname, html)
html


Out[3]:
COAWST_4 HYCOM ROMS_ESPRESSO SABGOM_ARCHIVE USF_ROMS
Duck, NC -- -- -- 5.20 --
Oregon Inlet Marina, NC 2.92 3.67 2.18 2.70 --
USCG Station Hatteras, NC 0.84 -- -- 1.36 --
Beaufort, NC 0.45 -- 1.00 -- --
Wrightsville Beach, NC 0.39 0.91 -- 1.81 --
Springmaid Pier, SC 0.33 -- -- 1.91 --
Charleston, SC 0.37 -- -- -- --
Trident Pier, FL -- -- -- 3.21 --
Lake Worth Pier, FL 0.43 0.41 -- 2.47 --
Virginia Key, FL 1.99 -- -- 4.79 --
Vaca Key, FL -- -- -- 4.17 --
Key West, FL 1.71 -- -- 3.39 --
Naples, FL 2.85 -- -- -- 3.58
Fort Myers, FL -- -- -- -- 2.31
Port Manatee, FL -- -- -- 4.42 1.60
St Petersburg, FL 3.06 -- -- 5.01 --
Old Port Tampa, FL 1.99 -- -- 3.98 1.09
Apalachicola, FL 0.64 -- -- -- 0.23
Panama City, FL 0.58 -- -- -- 0.85
Panama City Beach, FL 4.87 -- -- 4.60 5.98
Pensacola, FL 0.77 -- -- -- --
CAROCOOPS CAP2, Capers Island Nearshore, SC 0.86 0.87 -- 0.78 --
CAROCOOPS FRP2, Fripp Nearshore, SC 0.25 0.17 -- 1.47 --
CAROCOOPS SUN2, Sunset Nearshore, NC 0.29 0.36 -- 1.36 --
CORMP ILM2, Wrightsville Beach Nearshore, NC 0.55 -- -- 0.79 --
Broad River, FL -- -- -- -- 1.29
Butternut Key, FL 2.58 -- -- 4.13 2.56
Bob Allen, FL 2.53 -- -- 4.08 3.63
Blackwater Sound, FL 2.39 -- -- -- 3.00
Cane Patch, FL -- -- -- -- 2.09
Cannon Bay, FL -- -- -- -- 0.96
Clear Water Pass, FL 1.68 -- -- -- 0.45
Duck Key, FL 2.12 -- -- 3.95 --
Garfield Bight, FL 4.09 -- -- -- --
Highway Creek, FL -- -- -- -- 3.59
Johnson Key, FL 2.10 -- -- 3.77 2.34
Broad River Lower, FL -- -- -- -- 0.44
Little Blackwater, FL -- -- -- -- 2.97
Little Madeira, FL 1.98 -- -- 4.09 --
Lane River, FL -- -- -- -- 0.58
Little Rabbit Key, FL 1.98 -- -- 4.00 --
Murray Key, FL 2.21 -- -- 3.89 0.90
Peterson Key, FL 2.32 -- -- 4.11 3.21
Trout Cove, FL -- -- -- 4.03 --
Willy Willy, FL -- -- -- -- 0.11
Whipray Basin, FL 2.50 -- -- 4.18 --
White Water -West, FL 1.98 -- -- -- --
fau_lobo_1 0.68 -- -- -- --
Sebastian Inlet met station 28.95 -- -- 25.78 --
St Lucie Inlet station 1.45 -- -- 4.54 --
2nd Ave North Pier, Myrtle Beach, SC 4.28 -- -- 2.16 --
Gulf of Mexico station 0.99 -- -- 3.92 0.71
Redfish Pass station 1.30 -- -- 4.08 1.11
Shell Point station 1.54 -- -- -- 1.33
Tarpon Bay station 1.36 -- -- 4.40 1.45

Skill 2: Central Root Mean Squared Error

Root Mean Squared Error of the deviations from the mean.

$$ \text{CRMS} = \sqrt{\left(\mathbf{m'} - \mathbf{o'}\right)^2}$$

where: $\mathbf{m'} = \mathbf{m} - \mathbf{\overline{m}}$ and $\mathbf{o'} = \mathbf{o} - \mathbf{\overline{o}}$


In [4]:
from utilities import rmse

dfs = load_secoora_ncs(run_name)

df = apply_skill(dfs, rmse, remove_mean=True, filter_tides=False)
df = rename_cols(df)
skill_score['rmse'] = df.copy()

# Filter out stations with no valid comparison.
df.dropna(how='all', axis=1, inplace=True)
df = df.applymap('{:.2f}'.format).replace('nan', '--')

html = to_html(df.T)
fname = os.path.join(run_name, 'rmse.html'.format(run_name))
save_html(fname, html)
html


Out[4]:
COAWST_4 HYCOM ROMS_ESPRESSO SABGOM_ARCHIVE USF_ROMS
Duck, NC -- -- -- 4.57 --
Oregon Inlet Marina, NC 3.14 3.76 2.53 1.52 --
USCG Station Hatteras, NC 0.97 -- -- 1.03 --
Beaufort, NC 0.49 -- 0.58 -- --
Wrightsville Beach, NC 0.44 0.48 -- 0.86 --
Springmaid Pier, SC 0.39 -- -- 1.54 --
Charleston, SC 0.22 -- -- -- --
Trident Pier, FL -- -- -- 0.68 --
Lake Worth Pier, FL 0.51 0.44 -- 1.36 --
Virginia Key, FL 0.57 -- -- 1.05 --
Vaca Key, FL -- -- -- 0.54 --
Key West, FL 0.31 -- -- 0.49 --
Naples, FL 0.50 -- -- -- 0.44
Fort Myers, FL -- -- -- -- 0.03
Port Manatee, FL -- -- -- 1.16 0.64
St Petersburg, FL 0.36 -- -- 0.78 --
Old Port Tampa, FL 0.43 -- -- 1.06 0.23
Apalachicola, FL 0.50 -- -- -- 0.25
Panama City, FL 0.48 -- -- -- 0.64
Panama City Beach, FL 1.37 -- -- 3.10 0.24
Pensacola, FL 0.91 -- -- -- --
CAROCOOPS CAP2, Capers Island Nearshore, SC 0.38 0.77 -- 0.41 --
CAROCOOPS FRP2, Fripp Nearshore, SC 0.31 0.22 -- 0.53 --
CAROCOOPS SUN2, Sunset Nearshore, NC 0.27 0.36 -- 0.45 --
CORMP ILM2, Wrightsville Beach Nearshore, NC 0.19 -- -- 0.36 --
Broad River, FL -- -- -- -- 0.08
Butternut Key, FL 0.55 -- -- 0.60 0.19
Bob Allen, FL 0.97 -- -- 0.78 0.14
Blackwater Sound, FL 0.66 -- -- -- 0.18
Cane Patch, FL -- -- -- -- 0.07
Cannon Bay, FL -- -- -- -- 0.10
Clear Water Pass, FL 0.61 -- -- -- 0.11
Duck Key, FL 0.58 -- -- 0.53 --
Garfield Bight, FL 1.89 -- -- -- --
Highway Creek, FL -- -- -- -- 0.17
Johnson Key, FL 1.19 -- -- 0.89 0.18
Broad River Lower, FL -- -- -- -- 0.44
Little Blackwater, FL -- -- -- -- 0.15
Little Madeira, FL 0.69 -- -- 0.68 --
Lane River, FL -- -- -- -- 0.22
Little Rabbit Key, FL 1.02 -- -- 1.00 --
Murray Key, FL 1.34 -- -- 0.79 0.28
Peterson Key, FL 0.71 -- -- 0.69 0.24
Trout Cove, FL -- -- -- 0.83 --
Willy Willy, FL -- -- -- -- 0.01
Whipray Basin, FL 1.05 -- -- 1.04 --
White Water -West, FL 0.73 -- -- -- --
fau_lobo_1 0.60 -- -- -- --
Sebastian Inlet met station 0.30 -- -- 0.92 --
St Lucie Inlet station 1.40 -- -- 1.13 --
2nd Ave North Pier, Myrtle Beach, SC 9.48 -- -- 3.52 --
Gulf of Mexico station 0.25 -- -- 0.31 0.21
Redfish Pass station 0.29 -- -- 0.45 0.18
Shell Point station 0.31 -- -- -- 0.04
Tarpon Bay station 0.41 -- -- 0.50 0.25

In [5]:
from utilities import r2

dfs = load_secoora_ncs(run_name)

df = apply_skill(dfs, r2, remove_mean=True, filter_tides=False)
df = rename_cols(df)
skill_score['r2'] = df.copy()

# Filter out stations with no valid comparison.
df.dropna(how='all', axis=1, inplace=True)
df = df.applymap('{:.2f}'.format).replace('nan', '--')

html = to_html(df.T)
fname = os.path.join(run_name, 'r2.html'.format(run_name))
save_html(fname, html)
html


Out[5]:
COAWST_4 HYCOM ROMS_ESPRESSO SABGOM_ARCHIVE USF_ROMS
Duck, NC -- -- -- -0.61 --
Oregon Inlet Marina, NC 0.21 0.12 0.49 0.41 --
USCG Station Hatteras, NC 0.15 -- -- -0.20 --
Beaufort, NC 0.52 -- 0.32 -- --
Wrightsville Beach, NC 0.49 0.21 -- -1.51 --
Springmaid Pier, SC 0.68 -- -- -3.59 --
Charleston, SC 0.76 -- -- -- --
Trident Pier, FL -- -- -- -17.91 --
Lake Worth Pier, FL 0.19 0.26 -- -12.22 --
Virginia Key, FL -0.55 -- -- -8.03 --
Vaca Key, FL -- -- -- 0.42 --
Key West, FL -0.07 -- -- -0.81 --
Naples, FL 0.45 -- -- -- 0.38
Fort Myers, FL -- -- -- -- 0.00
Port Manatee, FL -- -- -- -0.59 0.29
St Petersburg, FL -0.24 -- -- -3.12 --
Old Port Tampa, FL -0.12 -- -- -5.20 -0.84
Apalachicola, FL -1.05 -- -- -- -0.20
Panama City, FL 0.46 -- -- -- -0.12
Panama City Beach, FL -0.28 -- -- -3.62 -0.00
Pensacola, FL 0.09 -- -- -- --
CAROCOOPS CAP2, Capers Island Nearshore, SC 0.54 -0.60 -- 0.58 --
CAROCOOPS FRP2, Fripp Nearshore, SC 0.45 0.24 -- -0.33 --
CAROCOOPS SUN2, Sunset Nearshore, NC 0.71 0.34 -- 0.28 --
CORMP ILM2, Wrightsville Beach Nearshore, NC 0.70 -- -- -0.38 --
Broad River, FL -- -- -- -- -0.00
Butternut Key, FL 0.40 -- -- -0.11 0.00
Bob Allen, FL 0.20 -- -- 0.34 0.00
Blackwater Sound, FL 0.36 -- -- -- 0.00
Cane Patch, FL -- -- -- -- 0.00
Cannon Bay, FL -- -- -- -- 0.00
Clear Water Pass, FL 0.29 -- -- -- -0.00
Duck Key, FL 0.39 -- -- 0.28 --
Garfield Bight, FL -0.08 -- -- -- --
Highway Creek, FL -- -- -- -- 0.00
Johnson Key, FL 0.17 -- -- 0.18 -0.00
Broad River Lower, FL -- -- -- -- 0.00
Little Blackwater, FL -- -- -- -- 0.00
Little Madeira, FL 0.34 -- -- -0.03 --
Lane River, FL -- -- -- -- 0.00
Little Rabbit Key, FL 0.08 -- -- 0.05 --
Murray Key, FL 0.08 -- -- 0.25 0.00
Peterson Key, FL 0.23 -- -- 0.03 -0.00
Trout Cove, FL -- -- -- 0.28 --
Willy Willy, FL -- -- -- -- 0.00
Whipray Basin, FL 0.20 -- -- 0.06 --
White Water -West, FL 0.20 -- -- -- --
fau_lobo_1 0.22 -- -- -- --
Sebastian Inlet met station 0.00 -- -- 0.00 --
St Lucie Inlet station -4.21 -- -- -2.16 --
2nd Ave North Pier, Myrtle Beach, SC 0.00 -- -- -0.01 --
Gulf of Mexico station 0.62 -- -- 0.39 0.69
Redfish Pass station 0.44 -- -- -0.47 0.46
Shell Point station 0.56 -- -- -- 0.00
Tarpon Bay station 0.36 -- -- 0.07 -0.60

In [6]:
from utilities import r2

dfs = load_secoora_ncs(run_name)

df = apply_skill(dfs, r2, remove_mean=True, filter_tides=True)
df = rename_cols(df)
skill_score['low_pass_r2'] = df.copy()

# Filter out stations with no valid comparison.
df.dropna(how='all', axis=1, inplace=True)
df = df.applymap('{:.2f}'.format).replace('nan', '--')

html = to_html(df.T)
fname = os.path.join(run_name, 'low_pass_r2.html'.format(run_name))
save_html(fname, html)
html


Out[6]:
COAWST_4 HYCOM ROMS_ESPRESSO SABGOM_ARCHIVE USF_ROMS
Duck, NC -- -- -- -0.41 --
Oregon Inlet Marina, NC 0.22 0.12 0.61 0.50 --
USCG Station Hatteras, NC 0.15 -- -- 0.44 --
Beaufort, NC 0.53 -- 0.48 -- --
Wrightsville Beach, NC 0.53 0.45 -- -1.15 --
Springmaid Pier, SC 0.73 -- -- -1.79 --
Charleston, SC 0.91 -- -- -- --
Trident Pier, FL -- -- -- -34.18 --
Lake Worth Pier, FL 0.11 0.13 -- -16.62 --
Virginia Key, FL -0.81 -- -- -9.65 --
Vaca Key, FL -- -- -- 0.59 --
Key West, FL 0.51 -- -- -1.73 --
Naples, FL 0.58 -- -- -- 0.67
Port Manatee, FL -- -- -- -0.70 0.75
St Petersburg, FL -1.01 -- -- -3.68 --
Old Port Tampa, FL -0.79 -- -- -4.58 0.55
Apalachicola, FL -3.85 -- -- -- 0.14
Panama City, FL 0.66 -- -- -- 0.01
Panama City Beach, FL -0.66 -- -- -4.21 --
Pensacola, FL 0.25 -- -- -- --
CAROCOOPS CAP2, Capers Island Nearshore, SC 0.69 0.21 -- 0.75 --
CAROCOOPS FRP2, Fripp Nearshore, SC 0.48 0.74 -- -0.31 --
CAROCOOPS SUN2, Sunset Nearshore, NC 0.79 0.46 -- 0.72 --
CORMP ILM2, Wrightsville Beach Nearshore, NC 0.82 -- -- -0.05 --
Butternut Key, FL 0.21 -- -- -0.28 --
Bob Allen, FL 0.13 -- -- 0.65 --
Blackwater Sound, FL 0.29 -- -- -- --
Clear Water Pass, FL -0.17 -- -- -- --
Duck Key, FL 0.37 -- -- 0.43 --
Garfield Bight, FL -0.17 -- -- -- --
Johnson Key, FL 0.05 -- -- 0.43 --
Little Madeira, FL 0.24 -- -- 0.14 --
Little Rabbit Key, FL -0.16 -- -- -0.26 --
Murray Key, FL 0.02 -- -- 0.54 --
Peterson Key, FL 0.11 -- -- -0.41 --
Trout Cove, FL -- -- -- 0.45 --
Whipray Basin, FL 0.11 -- -- 0.10 --
White Water -West, FL -0.20 -- -- -- --
fau_lobo_1 0.26 -- -- -- --
Sebastian Inlet met station 0.00 -- -- 0.00 --
St Lucie Inlet station -10.01 -- -- -5.42 --
2nd Ave North Pier, Myrtle Beach, SC 0.00 -- -- 0.13 --
Gulf of Mexico station 0.62 -- -- 0.57 0.67
Redfish Pass station 0.15 -- -- -3.26 0.47
Shell Point station 0.68 -- -- -- --
Tarpon Bay station 0.38 -- -- -0.42 -0.66

Skill 4: Low passed and re-sampled (3H) R$^2$

https://github.com/ioos/secoora/issues/183


In [7]:
from utilities import r2

dfs = load_secoora_ncs(run_name)

# SABGOM dt = 3 hours.
dfs = dfs.swapaxes('items', 'major').resample('3H').swapaxes('items', 'major')

df = apply_skill(dfs, r2, remove_mean=True, filter_tides=False)
df = rename_cols(df)
skill_score['low_pass_resampled_3H_r2'] = df.copy()

# Filter out stations with no valid comparison.
df.dropna(how='all', axis=1, inplace=True)
df = df.applymap('{:.2f}'.format).replace('nan', '--')

html = to_html(df.T)
fname = os.path.join(run_name, 'low_pass_resampled_3H_r2.html'.format(run_name))
save_html(fname, html)
html


Out[7]:
COAWST_4 HYCOM ROMS_ESPRESSO SABGOM_ARCHIVE USF_ROMS
Duck, NC -- -- -- -0.55 --
Oregon Inlet Marina, NC 0.21 0.12 0.50 0.41 --
USCG Station Hatteras, NC 0.15 -- -- -0.14 --
Beaufort, NC 0.53 -- 0.34 -- --
Wrightsville Beach, NC 0.51 0.20 -- -1.31 --
Springmaid Pier, SC 0.71 -- -- -3.33 --
Charleston, SC 0.81 -- -- -- --
Trident Pier, FL -- -- -- -20.24 --
Lake Worth Pier, FL 0.23 0.26 -- -11.14 --
Virginia Key, FL -0.53 -- -- -9.44 --
Vaca Key, FL -- -- -- 0.35 --
Key West, FL -0.09 -- -- -1.03 --
Naples, FL 0.45 -- -- -- 0.34
Fort Myers, FL -- -- -- -- 0.00
Port Manatee, FL -- -- -- -0.57 0.28
St Petersburg, FL -0.24 -- -- -2.67 --
Old Port Tampa, FL -0.13 -- -- -4.52 -0.40
Apalachicola, FL -1.27 -- -- -- -0.21
Panama City, FL 0.49 -- -- -- -0.12
Panama City Beach, FL -0.30 -- -- -3.49 0.00
Pensacola, FL 0.12 -- -- -- --
CAROCOOPS CAP2, Capers Island Nearshore, SC 0.57 -0.46 -- 0.57 --
CAROCOOPS FRP2, Fripp Nearshore, SC 0.47 -0.27 -- -0.33 --
CAROCOOPS SUN2, Sunset Nearshore, NC 0.73 0.45 -- 0.32 --
CORMP ILM2, Wrightsville Beach Nearshore, NC 0.72 -- -- -0.28 --
Broad River, FL -- -- -- -- 0.00
Butternut Key, FL 0.39 -- -- -0.20 0.00
Bob Allen, FL 0.19 -- -- 0.29 0.00
Blackwater Sound, FL 0.36 -- -- -- 0.00
Cane Patch, FL -- -- -- -- 0.00
Cannon Bay, FL -- -- -- -- 0.00
Clear Water Pass, FL 0.26 -- -- -- 0.00
Duck Key, FL 0.38 -- -- 0.18 --
Garfield Bight, FL -0.06 -- -- -- --
Highway Creek, FL -- -- -- -- -0.00
Johnson Key, FL 0.16 -- -- 0.16 0.00
Broad River Lower, FL -- -- -- -- 0.00
Little Blackwater, FL -- -- -- -- 0.00
Little Madeira, FL 0.33 -- -- -0.08 --
Lane River, FL -- -- -- -- 0.00
Little Rabbit Key, FL 0.07 -- -- 0.01 --
Murray Key, FL 0.07 -- -- 0.22 0.00
Peterson Key, FL 0.23 -- -- -0.05 0.00
Trout Cove, FL -- -- -- 0.22 --
Willy Willy, FL -- -- -- -- 0.00
Whipray Basin, FL 0.19 -- -- 0.06 --
White Water -West, FL 0.19 -- -- -- --
fau_lobo_1 0.22 -- -- -- --
Sebastian Inlet met station 0.00 -- -- 0.00 --
St Lucie Inlet station -3.76 -- -- -1.85 --
2nd Ave North Pier, Myrtle Beach, SC 0.00 -- -- 0.04 --
Gulf of Mexico station 0.62 -- -- 0.35 0.72
Redfish Pass station 0.40 -- -- -0.67 0.42
Shell Point station 0.57 -- -- -- 0.00
Tarpon Bay station 0.33 -- -- -0.01 0.20

Save scores


In [8]:
fname = os.path.join(run_name, 'skill_score.pkl')
with open(fname,'wb') as f:
    pickle.dump(skill_score, f)

Normalized Taylor diagrams

The radius is model standard deviation error divided by observations deviation, azimuth is arc-cosine of cross correlation (R), and distance to point (1, 0) on the abscissa is Centered RMS.


In [9]:
%matplotlib inline
import matplotlib.pyplot as plt
from utilities.taylor_diagram import TaylorDiagram


def make_taylor(samples):
    fig = plt.figure(figsize=(9, 9))
    dia = TaylorDiagram(samples['std']['OBS_DATA'],
                        fig=fig,
                        label="Observation")
    colors = plt.matplotlib.cm.jet(np.linspace(0, 1, len(samples)))
    # Add samples to Taylor diagram.
    samples.drop('OBS_DATA', inplace=True)
    for model, row in samples.iterrows():
        dia.add_sample(row['std'], row['corr'], marker='s', ls='',
                       label=model)
    # Add RMS contours, and label them.
    contours = dia.add_contours(colors='0.5')
    plt.clabel(contours, inline=1, fontsize=10)
    # Add a figure legend.
    kw = dict(prop=dict(size='small'), loc='upper right')
    leg = fig.legend(dia.samplePoints,
                     [p.get_label() for p in dia.samplePoints],
                     numpoints=1, **kw)
    return fig

In [10]:
dfs = load_secoora_ncs(run_name)

# Bin and interpolate all series to 1 hour.
freq = '3H'
for station, df in list(dfs.iteritems()):
    df = df.resample(freq).interpolate().dropna(axis=1)
    if 'OBS_DATA' in df:
        samples = DataFrame.from_dict(dict(std=df.std(),
                                           corr=df.corr()['OBS_DATA']))
    else:
        continue
    samples[samples < 0] = np.NaN
    samples.dropna(inplace=True)
    if len(samples) <= 2:  # 1 obs 1 model.
        continue
    fig = make_taylor(samples)
    fig.savefig(os.path.join(run_name, '{}.png'.format(station)))
    plt.close(fig)