Weekly Sea Surface Temperature Patterns from NOAA

Atmospheric Data: Nino 3 SST Index


Overview

Generating a wavelet power and phase spectrum from the time-series data Nino 3 SST Index

  1. Prerequisties

  2. Background

  3. Download and Organize Nino 3 SST Data

  4. Wavelet Input Values

  5. PyWavelets

  6. Power Spectrum

  7. Phase Spectrum

Prerequisites

Concepts

Importance

Notes

Intro to Matplotlib

Necessary

Plotting on a data

Intro to Pandas

Necessary

Familiarity with working with dataframes

Intro to Numpy

Necessary

Familiarity with working with arrays

Intro to SciPy

Helpful

Familiarity with working with wave files and FFT

  • Time to learn: 45 minutes


Background

What is an El Niño?

Learn more!

Wavelets and Atmospheric Data

Weather is a great example of time-series data. Weather varies in cycles of temperature over weeks due to a huge vareity of variables. Wavelet analysis can be used to find patterns in temperature by analyzing both the temperature and the time when the temperature occurs.

Imports

import geocat.datafiles as gcd              # accessing nino3 data file
import xarray as xr                         # working with geocat-datafiles

import numpy as np                          # working with arrays
import pandas as pd                         # working with dataframes
import matplotlib.pyplot as plt             # plot data

import pywt                                 # PyWavelets
Downloading file 'registry.txt' from 'https://github.com/NCAR/GeoCAT-datafiles/raw/main/registry.txt' to '/home/runner/.cache/geocat'.

Download Nino 3 SST Data

Download Nino 3 data from geocat-datafiles

nino3_data = gcd.get('ascii_files/sst_nino3.dat')
nino3_data = np.loadtxt(nino3_data)
Downloading file 'ascii_files/sst_nino3.dat' from 'https://github.com/NCAR/GeoCAT-datafiles/raw/main/ascii_files/sst_nino3.dat' to '/home/runner/.cache/geocat'.

Plot and View Data

fig, ax = plt.subplots(figsize=(8, 8))
plt.title("El Niño Sea Surface Temperature")
plt.xlabel("Years (from 1871)")
plt.ylabel("Sea Surface Temparture Changes")
plt.plot(nino3_data)
plt.show()
../../_images/e4f95fe06e685df6981618082eaca0259ced17375f5bf00bb96026207f99de27.png

Update the X-Axis

By default, the data loaded in lists the year as time since 1871, we can add a new x-axis to view the years along the x-axis

# Convert default X-axis from time steps of 0-504 (0-len(nino3_data)) to Years
dt = 0.25  # sampling period
start_year = 1871
end_year = 1871 + (len(nino3_data) * dt)
x_tickrange = np.arange(start_year, end_year, dt)
start = int(9 / dt)  # 36, starts the x-axis label at 1880 (9 years after start of data)
display_nth = int(20 / dt)  # 80, display x-axis label every 20 years
fig, ax = plt.subplots(figsize=(8, 8))
plt.title("El Niño Sea Surface Temperature")
plt.xlabel("Year")
plt.ylabel("Sea Surface Temparture Changes")
plt.xticks(range(len(x_tickrange))[start::display_nth], x_tickrange[start::display_nth]) # update x-axis
plt.plot(nino3_data)
plt.show()
../../_images/946a53b3be859b25a2d8a80a7961a5c80a8c72b4395b2253a31e51fe2caeed9f.png

Wavelet Input Values

Wavelet inputs include:

  • x: Input time-series data (for example, the time and temperature data from nino3)

  • wavelet: mother wavelet name

  • dt: sampling period (time between each y-value)

  • s0: smallest scale

  • dj: spacing between each discrete scales

  • jtot: largest scale

dt = 0.25  # sampling period (time between each y-value)
s0 = 0.25  # smallest scale
dj = 0.25  # spacing between each discrete scales
jtot = 64  # largest scale

Define Complex Morlet

TODO: Choosing a Complex Morlet

bandwidth = 1.5
center_freq = 1
wavelet_mother = f"cmor{bandwidth}-{center_freq}"
print(wavelet_mother)
cmor1.5-1

Applying Wavelets

scales = np.arange(1, jtot + 1, dj)
wavelet_coeffs, freqs = pywt.cwt(
    data=nino3_data, scales=scales, wavelet=wavelet_mother, sampling_period=dt
)

Power Spectrum

The power spectrum is the real component of the wavelet_coeffs. We can find this value by squaring the absolute value of the wavelet_coeffs

power = np.power((abs(wavelet_coeffs)), 2)
fig, ax = plt.subplots(figsize=(10, 10))

plt.imshow(
    power, vmax=(power).max(), vmin=(power).min(), aspect="auto"
)

plt.xticks(range(len(x_tickrange))[start::display_nth], x_tickrange[start::display_nth])
plt.title("El Niño Wavelet Power Spectrum")
plt.xlabel("Year")
plt.ylabel("Scale")
plt.colorbar()
plt.show()
../../_images/82a532ec5585e06da2e4449d74025f147be6907972c77eb29bbd698281eaba85.png

Better Match Original Graph

We can clean up this diagram to better match the original Torrence and Compo paper by modifying the y-axis and plotting a contour around the data to better view the differences in the color bar

fig, ax = plt.subplots(figsize=(10, 10))

# Plot contour around data
plt.contour(
    power, vmax=(power).max(), vmin=(power).min(), levels=10
)
plt.contour(power, levels=10, colors="k", linewidths=0.5, alpha=0.75)

# Plot Scalogram
plt.imshow(
    power, vmax=(power).max(), vmin=(power).min(), aspect="auto"
)

plt.xticks(range(len(x_tickrange))[start::display_nth], x_tickrange[start::display_nth])
plt.title("El Niño Wavelet Power Spectrum")
plt.xlabel("Year")
plt.ylabel("Scale")
plt.colorbar()
plt.show()
../../_images/a4a07b8abc2af56628e865c516eb0fe29fd99bdeca4f50bb65400d610b0fd9bb.png

Cone of Influence

TODO

Phase Spectrum

# compare the phase spectrum
phase = np.angle(wavelet_coeffs)
fig, ax = plt.subplots(figsize=(10, 10))

# Convert Y-Axis from default to symmetrical log (symlog) with labels
ax.set_yscale("symlog")
ax.invert_yaxis()
ax.set_yticks([10, 20, 30, 40, 50])
ax.set_yticklabels([10, 20, 30, 40, 50])

# Plot scalogram
plt.imshow(
    phase, vmax=(phase).max(), vmin=(phase).min(), aspect="auto"
)

# Convert default X-axis from time steps of 0-504 (0-len(sst_data)) to Years
start_year = 1871
end_year = 1871 + (len(nino3_data) * dt)
x_tickrange = np.arange(start_year, end_year, dt)
start = int(9 / dt)  # 36, starts the x-axis label at 1880 (9 years after start of data)
display_nth = int(20 / dt)  # 80, display x-axis label every 20 years
plt.xticks(range(len(x_tickrange))[start::display_nth], x_tickrange[start::display_nth])

plt.title("El Niño Wavelet Phase Spectrum")
plt.xlabel("Year")
plt.ylabel("Scale")
plt.colorbar()
plt.show()
../../_images/20b889de643e32d8e6cd785c14f2ade265b7d4484f0814feb52b3068d5d7f66d.png

Summary

Frequency signals appear in more than just audio! A frequency analysis of weather data can inform us about how weather trends change through a year and over a decades worth of data

What’s next?

Resources and references