Usage Example

Spy4Cast: Preprocess, MCA and Crossvalidation

Here is an example of how you can use Spy4Cast API to RUN the full Spy4Cast methodology and use the included plotting functions.

Click here to download

from spy4cast import Dataset, Region, Month
from spy4cast.spy4cast import Preprocess, MCA, Crossvalidation


DATASETS_FOLDER = '/Users/Shared/datasets'


PREDICTOR_NAME = "oisst_v2_mean_monthly.nc"
PREDICTOR_VAR = "sst"

PREDICTAND_NAME = "chl_1km_monthly_Sep1997_Dec2020.nc"
PREDICTAND_VAR = "CHL"

predictor = Dataset(PREDICTOR_NAME, DATASETS_FOLDER).open(PREDICTOR_VAR)
oisst_region = Region(
    lat0=5, latf=25,
    lon0=-75, lonf=-20,
    month0=Month.AUG, monthf=Month.SEP,
    year0=1997, yearf=2019,
)  # PREDICTOR: Y
predictor.slice(oisst_region, skip=3)

predictand = Dataset(PREDICTAND_NAME, DATASETS_FOLDER).open(PREDICTAND_VAR)
chl_region = Region(
    lat0=36, latf=37,
    lon0=-5.3, lonf=-2,
    month0=Month.MAR, monthf=Month.APR,
    year0=1998, yearf=2020,
)  # PRECITAND: Z
predictand.slice(chl_region, skip=0)

DATA_FOLDER = 'data-03122022'
PLOTS_FOLDER = 'plots-03122022'

PREDICTOR_PREPROCESSED_PREFIX = 'predictor_'
PREDICTAND_PREPROCESSED_PREFIX = 'predictand_'
MCA_PREFIX = 'mca_'
MCA_PLOT_NAME = 'mca.png'
CROSS_PREFIX = 'cross_'
CROSS_PLOT_NAME = 'cross.png'

LOAD_PREPROCESSED = False
LOAD_MCA = False
LOAD_CROSS = False

if LOAD_PREPROCESSED:
    predictor_preprocessed = Preprocess.load(PREDICTOR_PREPROCESSED_PREFIX, DATA_FOLDER)
    predictand_preprocessed = Preprocess.load(PREDICTAND_PREPROCESSED_PREFIX, DATA_FOLDER)
else:
    predictor_preprocessed = Preprocess(predictor)
    predictand_preprocessed = Preprocess(predictand)
    predictor_preprocessed.save(PREDICTOR_PREPROCESSED_PREFIX, DATA_FOLDER)
    predictand_preprocessed.save(PREDICTAND_PREPROCESSED_PREFIX, DATA_FOLDER)

nm = 3
alpha = .1

if LOAD_MCA:
    mca = MCA.load(MCA_PREFIX, DATA_FOLDER, dsy=predictor_preprocessed, dsz=predictand_preprocessed)
else:
    mca = MCA(predictor_preprocessed, predictand_preprocessed, nm, alpha)
    mca.save(MCA_PREFIX, DATA_FOLDER)

mca.plot(save_fig=True, cmap='viridis', name=MCA_PLOT_NAME, 
         figsize=(14, 8),
         width_ratios=[1, 1, 1],
         folder=PLOTS_FOLDER,)

if LOAD_CROSS:
    cross = Crossvalidation.load(CROSS_PREFIX, DATA_FOLDER, dsy=predictor_preprocessed, dsz=predictand_preprocessed)
else:
    cross = Crossvalidation(predictor_preprocessed, predictand_preprocessed, nm, alpha)
    cross.save(CROSS_PREFIX, DATA_FOLDER)

# cross.plot(save_fig=True, folder=PLOTS_FOLDER, name=CROSS_PLOT_NAME, version='default', mca=mca)
cross.plot(save_fig=True, folder=PLOTS_FOLDER, name=CROSS_PLOT_NAME, version=2, mca=mca)


import matplotlib.pyplot as plt

plt.show()

Here is an example of how you can use Spy4Cast API to PLOT the previously ran Spy4Cast methodology.

Click here to download

from spy4cast import spy4cast
import matplotlib.pyplot as plt

PLOTS_DIR = 'plots'
SAVED_DATA_DIR = 'saved_tna'

y_ppcessed = spy4cast.Preprocess.load('save_preprocessed_y_', folder=SAVED_DATA_DIR)
z_ppcessed = spy4cast.Preprocess.load('save_preprocessed_z_', folder=SAVED_DATA_DIR)
mca = spy4cast.MCA.load('save_mca_', SAVED_DATA_DIR, dsy=y_ppcessed, dsz=z_ppcessed)
cross = spy4cast.Crossvalidation.load('save_cross_', SAVED_DATA_DIR, dsy=y_ppcessed, dsz=z_ppcessed)

# FAST PLOTS
y_ppcessed.plot(save_fig=True, selected_year=2005, folder=PLOTS_DIR, name='sst-2005.png')
z_ppcessed.plot(cmap='viridis', selected_year=2006, folder=PLOTS_DIR, name='chl-2006.png')
mca.plot(save_fig=True, cmap='viridis', folder=PLOTS_DIR, name='mca-sst-chl.png')
cross.plot(save_fig=True, folder=PLOTS_DIR, name='crossvalidation-chl-sst.png')
cross.plot_zhat(2005, folder=PLOTS_DIR, name='zhat-2005.png')
cross.plot_zhat(2006, folder=PLOTS_DIR, name='zhat-2006.png')
cross.plot_zhat(2007, folder=PLOTS_DIR, name='zhat-2007.png')

# Show all the created plots
plt.show()

Here is an example of how you can use Spy4Cast API to RUN the Spy4Cast methodology and plot it using CUSTOM PLOTTING FUNCTIONS.

Click here to download

# mypy: ignore-errors

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.ticker import MaxNLocator

from spy4cast import Dataset, Month, Region
from spy4cast.meteo import Clim
from spy4cast.spy4cast import Preprocess, MCA, Crossvalidation
import cartopy.crs as ccrs


DATASET_DIR = '/Users/Shared/datasets'

PREDICTOR_NAME = "oisst_v2_mean_monthly.nc"
PREDICTOR_VAR = "sst"

PREDICTAND_NAME = "chl_1km_monthly_Sep1997_Dec2020.nc"
PREDICTAND_VAR = "CHL"


def plot_ts(
    time, arr, ax, title, ylabel=None, xlabel=None, color=None, xtickslabels=None, only_int_xlabels=False, label=None,
):
    ax.plot(time, arr, linewidth=3, color=color, label=label)
    ax.set_xlim(time[0], time[-1])
    if xtickslabels is not None:
        ax.set_xticklabels(xtickslabels)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    if title is not None:
        ax.set_title(title)
    if only_int_xlabels:
        ax.xaxis.set_major_locator(MaxNLocator(integer=True))


def plot_map(
    arr, lat, lon, fig, ax, title=None, levels=None, xlim=None, ylim=None, cmap=None, ticks=None
):
    if levels is None:
        n = 30
        _std = np.nanstd(arr)
        _m = np.nanmean(arr)

        levels = np.unique(np.linspace(_m - _std, _m + _std, n))

        if len(levels) <= 1:
            levels = None

    if ticks is None and levels is not None:
        nticks = 6
        ticks = levels[np.arange(0, len(levels), len(levels) // nticks)]

    cmap = 'bwr' if cmap is None else cmap
    xlim = sorted((lon[0], lon[-1])) if xlim is None else xlim
    ylim = sorted((lat[-1], lat[0])) if ylim is None else ylim

    im = ax.contourf(
        lon, lat, arr, cmap=cmap, levels=levels,
        extend='both', transform=ccrs.PlateCarree()
    )
    cb = fig.colorbar(
        im, ax=ax, orientation='horizontal', pad=0.02,
        ticks=ticks,
    )
    ax.coastlines()
    cb.ax.tick_params(labelsize=11)
    ax.set_xlim(*xlim)
    ax.set_ylim(*ylim)
    # # axs.margins(0)
    if title is not None:
        ax.set_title(title)


def plot_cross(
    cross, title, cmap, map_ticks,
):
    fig = plt.figure(figsize=(18, 8))
    nrows = 3
    ncols = 2
    axs = (
        fig.add_subplot(nrows, ncols, 1, projection=ccrs.PlateCarree(00)),
        fig.add_subplot(nrows, ncols, 2),
        fig.add_subplot(nrows, ncols, 3),
        fig.add_subplot(nrows, ncols, 4),
        fig.add_subplot(nrows, ncols, 5),
        fig.add_subplot(nrows, ncols, 6),
    )

    nzlat = len(cross.dsz.lat)
    nzlon = len(cross.dsz.lon)
    # nztime = len(ts)

    # ------ r_z_zhat_s and p_z_zhat_s ------ #
    # Correlation map
    d = cross.r_z_zhat_s_accumulated_modes[-1, :].transpose().reshape((nzlat, nzlon))
    _mean = np.nanmean(d)
    _std = np.nanstd(d)
    mx = _mean + _std
    mn = _mean - _std
    plot_map(
        d, cross.dsz.lat, cross.dsz.lon, fig, axs[0],
        'Correlation in space between z and zhat',
        cmap=cmap,
        ticks=(np.arange(round(mn * 10) / 10, np.floor(mx * 10) / 10 + .05, .1) if map_ticks is None and not np.isnan(
            _mean) and not np.isnan(_std) else map_ticks)
    )
    hatches = d.copy()
    hatches[((cross.p_z_zhat_s_accumulated_modes[-1, :] > cross.alpha) | (
            cross.r_z_zhat_s_accumulated_modes[-1, :] < 0)).transpose().reshape((nzlat, nzlon))] = np.nan

    axs[0].contourf(
        cross.dsz.lon, cross.dsz.lat, hatches,
        colors='none', hatches='..', extend='both',
        transform=ccrs.PlateCarree()
    )
    # ^^^^^^ r_z_zhat_s and p_z_zhat_s ^^^^^^ #

    # ------ r_z_zhat_t and p_z_zhat_t ------ #
    axs[1].bar(cross.dsz.time.values, cross.r_z_zhat_t_accumulated_modes[-1, :])
    axs[1].xaxis.set_major_locator(MaxNLocator(integer=True))

    axs[1].scatter(
        cross.dsz.time[cross.p_z_zhat_t_accumulated_modes[-1, :] <= cross.alpha],
        cross.p_z_zhat_t_accumulated_modes[-1, :][cross.p_z_zhat_t_accumulated_modes[-1, :] <= cross.alpha]
    )
    axs[1].set_title('Correlation in space between z and zhat')
    axs[1].grid(True)
    # ^^^^^^ r_z_zhat_t and p_z_zhat_t ^^^^^^ #

    # ------ scf ------ #
    for mode in range(cross.scf.shape[0]):
        plot_ts(
            time=cross.dsy.time.values,
            arr=cross.scf[mode],
            ax=axs[2],
            label=f'Mode {mode + 1}',
            title='Squared convariance fraction',
            only_int_xlabels=True,
        )
    axs[2].legend()
    axs[2].grid(True)
    # ^^^^^^ scf ^^^^^^ #

    # ^^^^^^ Us ^^^^^^ #
    mean = cross.us.mean(2)
    std = np.std(cross.us, axis=2)
    for mode in range(mean.shape[0]):
        axs[3 + mode].grid(True)
        axs[3 + mode].errorbar(
            cross.dsy.time, mean[mode], yerr=np.abs(std[mode]), label='std', color='orange', linewidth=3,
            ecolor='purple'
        )
        axs[3 + mode].set_title(
            f'Us for mode {mode + 1}'
        )
        axs[3 + mode].legend()

    fig.suptitle(title)

    fig.subplots_adjust(hspace=.4)


def plot_mca(mca, title, cmap, suy_ticks, suz_ticks, signs):
    # EXPLICADO EN PARTE FINAL DEL VIDEO
    # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    # fig2 = plt.figure()
    # ax2 = fig2.add_subplot(projection=ccrs.PlateCarree())
    # _std = np.nanstd(mca.SUY)
    # _m = np.nanmean(mca.SUY)
    # bound = max(abs(_m - _std), abs(_m + _std))
    # levels = np.linspace(-bound, bound, 20)
    # xlim = sorted((mca.dsy.lon.values[0], mca.dsy.lon.values[-1]))
    # ylim = sorted((mca.dsy.lat.values[-1], mca.dsy.lat.values[0]))
    #
    # t = mca.SUY[:, 1].transpose().reshape((len(mca.dsy.lat), len(mca.dsy.lon)))
    # th = mca.RUY[:, 1].transpose().reshape((len(mca.dsy.lat), len(mca.dsy.lon)))
    #
    # plot_map(
    #     t, mca.dsy.lat, mca.dsy.lon, fig2, ax2, None,
    #     levels=levels, xlim=xlim, ylim=ylim, cmap='bwr', ticks=suy_ticks,
    # )
    # ax2.contourf(
    #     mca.dsy.lon, mca.dsy.lat, th, colors='none', hatches='..', extend='both',
    #     transform=ccrs.PlateCarree()
    # )
    # fig2.savefig('suy2.png')
    # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    nrows = 3
    ncols = 3

    fig: plt.Figure = plt.figure(figsize=(18, 5))

    axs = (
        fig.add_subplot(nrows, ncols, 1),
        fig.add_subplot(nrows, ncols, 2),
        fig.add_subplot(nrows, ncols, 3),
        fig.add_subplot(nrows, ncols, 4, projection=ccrs.PlateCarree(0)),
        fig.add_subplot(nrows, ncols, 5, projection=ccrs.PlateCarree(0)),
        fig.add_subplot(nrows, ncols, 6, projection=ccrs.PlateCarree(0)),
        fig.add_subplot(nrows, ncols, 7, projection=ccrs.PlateCarree(0)),
        fig.add_subplot(nrows, ncols, 8, projection=ccrs.PlateCarree(0)),
        fig.add_subplot(nrows, ncols, 9, projection=ccrs.PlateCarree(0)),
    )

    # Plot timeseries
    for i, ax in enumerate(axs[:3]):
        # # ax.margins(0)
        plot_ts(
            time=mca.dsy.time.values,
            arr=mca.Us[i, :],
            ax=ax,
            title=f'Us Vs mode {i + 1}',
            color='green',
            label='Us'
        )
        plot_ts(
            time=mca.dsz.time.values,
            arr=mca.Vs[i, :],
            ax=ax,
            title=None,
            color='blue',
            label='Vs'
        )
        ax.legend()
        ax.grid(True)
    axs[0].legend(loc='upper left')

    # suy = SUY
    # suy[suy == 0.0] = np.nan

    n = 20
    for i, (var_name, su, ru, lats, lons, cm, ticks) in enumerate((
        ('SUY', mca.SUY, mca.RUY_sig, mca.dsy.lat, mca.dsy.lon, 'bwr', suy_ticks),
        ('SUZ', mca.SUZ, mca.RUZ_sig, mca.dsz.lat, mca.dsz.lon, cmap, suz_ticks)
    )):
        _std = np.nanstd(su)
        _m = np.nanmean(su)
        bound = max(abs(_m - _std), abs(_m + _std))
        levels = np.linspace(-bound, bound, n)
        xlim = sorted((lons.values[0], lons.values[-1]))
        ylim = sorted((lats.values[-1], lats.values[0]))

        for j, ax in enumerate(axs[3 * (i + 1):3 * (i + 1) + 3]):
            title = f'{var_name} mode {j + 1}. ' \
                    f'SCF={mca.scf[j] * 100:.01f}'

            t = su[:, j].transpose().reshape((len(lats), len(lons)))
            th = ru[:, j].transpose().reshape((len(lats), len(lons)))

            if signs is not None:
                if signs[j]:
                    t *= -1

            plot_map(
                t, lats, lons, fig, ax, title,
                levels=levels, xlim=xlim, ylim=ylim, cmap=cm, ticks=ticks,
            )
            ax.contourf(
                lons, lats, th, colors='none', hatches='..', extend='both',
                transform=ccrs.PlateCarree()
            )

    fig.suptitle(title, fontweight='bold')

    fig.subplots_adjust(hspace=.4)


predictor_region = Region(
    lat0=5, latf=45,
    lon0=-90, lonf=-5,
    month0=Month.JUN, monthf=Month.JUL,
    year0=1997, yearf=2019,
)

predictand_region = Region(
    lat0=36, latf=37,
    lon0=-5.3, lonf=-2,
    month0=Month.MAR, monthf=Month.APR,
    year0=1998, yearf=2020,
)


predictor = Dataset(PREDICTOR_NAME, folder=DATASET_DIR).open(PREDICTOR_VAR).slice(predictor_region, skip=3)
predictand = Dataset(PREDICTAND_NAME, folder=DATASET_DIR).open(PREDICTAND_VAR).slice(predictand_region, skip=3)

predictor_ppcessed = Preprocess(predictor)
predictand_ppcessed = Preprocess(predictand)

nm = 3
alpha = 0.1

# mca = MCA.load('mca_prueba_11062023_', dsy=predictor_ppcessed, dsz=predictand_ppcessed)
mca = MCA(predictor_ppcessed, predictand_ppcessed, nm, alpha)  # Line that takes time
# mca.save('mca_prueba_11062023_')

plot_mca(mca, 'MCA TUTORIAL', 'viridis', np.arange(-0.25, 0.30, 0.1), np.arange(-0.15, 0.20, 0.1), signs=[False, False, False])

cross = Crossvalidation.load('cross_prueba_11062023_', dsy=predictor_ppcessed, dsz=predictand_ppcessed)
# cross = Crossvalidation(predictor_ppcessed, predictand_ppcessed, nm, alpha)
# cross.save('cross_prueba_11062023_')

plot_cross(cross, 'Spy4cast Cross Prueba', 'viridis', None)

plt.show()

Climatology

Here is an example of how you can use Spy4Cast API to plot the climatology of a given .nc dataset

from spy4cast.meteo import Clim
from spy4cast import Month, Region, Dataset

# Define constants ---------------------------------------------------------------------------------- #
DATASET_DIR = '/Users/Shared/datasets/'
PLOTS_DIR = 'plots'
PLOT_DATA_DIR = 'data-clim'

chl_1km_monthly_Sep1997_Dec2020 = 'chl_1km_monthly_Sep1997_Dec2020.nc'
CHL = 'CHL'

chl_region = Region(36, 37, -5.3, -2, Month.MAR, Month.APR, 1998, 2020)

ds = Dataset(chl_1km_monthly_Sep1997_Dec2020, DATASET_DIR).open(CHL).slice(chl_region)

map_clim = Clim(ds, 'map')
map_clim.save('map_climatology', PLOT_DATA_DIR)
map_clim = Clim.load('map_climatology', PLOT_DATA_DIR, type='map')

map_clim.plot(show_plot=True, save_fig=True, name='clim-map-example.png', cmap='jet')

ts_clim = Clim(ds, 'ts')
ts_clim.save('ts_climatology', PLOT_DATA_DIR)
ts_clim = Clim.load('ts_climatology', PLOT_DATA_DIR, type='ts')

ts_clim.plot(show_plot=True, save_fig=True, name='clim-ts-example.png')

Output:

Output for clim map Output for clim ts

Anomaly

Here is an example of how you can use Spy4Cast API to plot the anomaly of a given .nc dataset

from spy4cast.meteo import Anom
from spy4cast import Month, Region, Dataset

# Define constants ---------------------------------------------------------------------------------- #
DATASET_DIR = '/Users/Shared/datasets/'
PLOTS_DIR = 'plots'
PLOT_DATA_DIR = 'data-anom'

chl_1km_monthly_Sep1997_Dec2020 = 'chl_1km_monthly_Sep1997_Dec2020.nc'
CHL = 'CHL'

chl_region = Region(30, 90, -5.3, -2, Month.MAR, Month.APR, 1998, 2020)

ds = Dataset(chl_1km_monthly_Sep1997_Dec2020, DATASET_DIR).open(CHL).slice(chl_region)

map_anom = Anom(ds, 'map')

map_anom.save('map_anomaly', PLOT_DATA_DIR)
# map_anom = Anom.load('map_anomaly', PLOT_DATA_DIR, type='map')
map_anom.plot(show_plot=True, save_fig=True, year=1999, name='anom-map-example.png', cmap='jet')

ts_anom = Anom(ds, 'ts')
ts_anom.save('ts_anomaly', PLOT_DATA_DIR)
# ts_anom = Anom.load('ts_anomaly', PLOT_DATA_DIR, type='ts')
ts_anom.plot(show_plot=True, save_fig=True, name='anom-ts-example.png')

Output:

Output for anom map Output for anom ts