↖ CPA Weather Lab

Four clocks over Pennsylvania

Every teleconnection is a clock running at its own speed. Decadal oceans set the odds, ENSO and the QBO turn them year by year, the MJO and the polar vortex push for weeks, and the fast atmospheric patterns deliver the weather. This page shows how they couple to each other and what each one does to Pennsylvania, using correlations computed from 1950–2026 source data.

Fast (days–weeks)Intraseasonal / stratosphereInterannualDecadal ocean

Where the clocks read today

Latest value in each source file (month shown). El Niño is building while the PDO is strongly negative and the North Atlantic is near record warmth — an unusual mix with few historical analogs.

Every index against every other

Correlation of detrended monthly anomalies (equal to their standardized covariance). Tap any cell for the full lead–lag curve, the strongest, median and weakest coupling across seasons and lags, and how stable the link has been across 30-year windows.

Select a cell.

What each index does to Pennsylvania

Monthly index against PA anomalies (NOAA nClimDiv, 1950–2026, detrended). Rows are target seasons, columns are how many months earlier the index was measured. A star marks 95% significance after accounting for autocorrelation.

Across the ten climate divisions

All indices together

Seasonal least-squares model with ten standardized indices. Cross-validated r is the honest skill; betas for AO and NAO are entangled because they share a mode.

When the effect arrives, peaks and fades

Daily indices against daily temperature at Harrisburg International (1950–67 and 1991–2026). Positive lag means the index was observed that many days before the temperature. The shaded band is roughly the 95% significance level.

The MJO phase clock

RMM amplitude ≥ 1, BoM record 1974–2024. Wedge colour: mean Harrisburg temperature anomaly (°F) that many days after a day in that phase.

ENSO strength, not sign

PA statewide DJF anomaly by winter RONI category, 1951–2026. The straight-line correlation is near zero because very strong El Niños run warm while weak ones run cold.

The catalog, most to least well known

What each index is, how it is calculated, when records begin, which time resolutions exist, how it persists, how it relates to the others and what it does in the Mid-Atlantic — with every data link.

Calculating the effects on Pennsylvania yourself

  1. Download the indices from the primary links in the catalog. Use RONI for ENSO from 2026 on; ERSST-based indices switched from v5 to v6 on 3 Aug 2026, so archive the version you used.
  2. Download PA targets: nClimDiv statewide and divisional monthly data, PRISM grids, or GHCN-Daily stations.
  3. Make anomalies: subtract each calendar month's mean and remove a per-month trend from both series; express precipitation as percent of normal or a standardized (SPI-style) value.
  4. Correlate by season and lag (index 0–6 months earlier), store r, slope per +1σ, and significance using the effective sample size.
  5. Composite the nonlinear drivers (ENSO by strength, MJO by phase with amplitude ≥ 1, QBO phase, post-SSW windows).
  6. Combine indices in a regression, one per family, and judge it only by cross-validated skill.
  7. Go daily for sub-monthly timing: lagged correlation from −10 to +30 days gives onset, peak, end and the half-power window.
  8. Control false discoveries across the hundreds of tests (Benjamini–Hochberg) and check 30-year rolling windows for stability.

The bundle delivered with this page reproduces every number here. From the folder the zip is in:

unzip -o teleconnections_pa_bundle.zip -d tele_pa && cd tele_pa && pip install -q pandas numpy matplotlib && python3 teleconnections_pa.py && python3 catalog.py && python3 figs.py && python3 gen_md.py && ls out out/img

The core of the method, if you want to drop it into your own PRISM or station pipeline:

import numpy as np, pandas as pd

def anom_detrend(s):                      # s: monthly pd.Series with DatetimeIndex
    out = s.astype(float).copy()
    for m in range(1, 13):
        x = s[s.index.month == m]; t = np.arange(len(x))
        out[x.index] = x.values - np.polyval(np.polyfit(t, x.values, 1), t)
    return out

def r1(x): x = x.dropna(); return np.corrcoef(x[:-1], x[1:])[0, 1]

def corr_sig(a, b):                       # Pearson r with Bretherton effective N
    j = pd.concat([a, b], axis=1).dropna(); n = len(j)
    r = np.corrcoef(j.iloc[:, 0], j.iloc[:, 1])[0, 1]
    ra, rb = r1(j.iloc[:, 0]), r1(j.iloc[:, 1])
    neff = n * (1 - ra * rb) / (1 + ra * rb)
    t = abs(r) * np.sqrt((neff - 2) / (1 - r * r))
    return r, neff, t > 2.0

SEAS = {"DJF": (12, 1, 2), "MAM": (3, 4, 5), "JJA": (6, 7, 8), "SON": (9, 10, 11)}
def pa_response(index, target, season, max_lag=6):
    idx, y = anom_detrend(index), anom_detrend(target)
    y = y[y.index.month.isin(SEAS[season])]
    rows = []
    for L in range(max_lag + 1):
        x = idx.copy(); x.index = x.index + pd.DateOffset(months=L)   # index L months earlier
        r, neff, sig = corr_sig(x, y); rows.append((L, round(r, 3), round(neff), sig))
    return pd.DataFrame(rows, columns=["lag_months", "r", "n_eff", "sig95"])

Code that already does this

ProjectUse
ajdawson/eofsEOF analysis — build NAO/AO/PNA/PDO-type indices yourself.
xarray-contrib/xeofsxarray EOF, rotated EOF (CPC-style RPCA), MCA, CCA.
PCMDI/pcmdi_metricsModes-of-variability and ENSO metrics for models and observations.
CLIVAR-PRP/ENSO_metricsENSO performance, process and teleconnection metrics.
ESMValGroup/ESMValToolModel evaluation recipes including teleconnections.
cghoffmann/mjoindicesOMI / REOMI MJO index calculation.
sandrolubis/read_RMM_BIMODAL_indicesRMM and bimodal ISO readers.
wy2136/climate_indexNOAA index downloaders and plots.
boshek/rsoiR: SOI, ONI, NPGO, NAO, AO, AAO.
decadeneo/SkybornFast climate-index regression and trend fields.
pangeo-data/climpredVerification of ENSO / S2S forecasts.
xarray-contrib/xskillscoreCorrelation and skill with effective-N significance.
← CPA Weather Lab · Learn