HoloViews Streams for Exploring Multidimensional Data

holoviews
streams
Explores a 4D dataset (time, level, lat, lon) dataset using HoloViews and Panel.
Author

Andrew Huang

Published

March 20, 2024

Follow along to build an app that uses a 4D dataset (level, time, lat, lon) and explore it by

Basics

Import the necessary libraries

Most of the time, using Python is just knowing what’s out there and importing it!

import param
import numpy as np
import xarray as xr
import panel as pn
import hvplot.xarray
import geoviews as gv
import holoviews as hv
from geoviews.streams import PolyDraw
from metpy.interpolate import cross_section
import cartopy.crs as ccrs

pn.extension()
gv.extension("bokeh")

Getting something working

Below I show three ways to download a piece of the NCEP Reanalysis dataset from NOAA.

It’s one of my favorite datasets for testing and writing examples because it’s so straightforward to use: - no API key required, which means no need to sign up, verify email, etc. - can be small or large, if 4X daily, concatenated across times, variables, etc - is multi-dimensional (time, level, lat, lon)

Below are three variations of downloading a dataset. Note, 1 only works in notebooks; 2 and 3 work in both notebooks and scripts.

Since I usually work in a Jupyter notebook, I like to use 1 due to its simplicity–just a ! + wget + copied url and an optional --no-clobber, -nc flag.

# 1.
!wget -nc https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/Dailies/pressure/air.2024.nc

# 2.
# import subprocess
# subprocess.run("wget https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/Dailies/pressure/air.2024.nc", shell=True)

# 3.
# import requests
# with requests.get("https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/Dailies/pressure/air.2024.nc") as response:
#     response.raise_for_status()
#     with open("air.2024.nc", "wb") as f:
#         f.write(response.content)
File ‘air.2024.nc’ already there; not retrieving.

The hardest part for any projects is getting started (something about static friction > kinetic friction).

However, once you get started, things get easier, so what I usually do is take baby steps and get something shown up front immediately.

Fortunately, XArray + hvPlot makes it possible!

ds = xr.open_dataset("air.2024.nc", drop_variables=["time_bnds"])

ds
<xarray.Dataset>
Dimensions:  (level: 17, lat: 73, lon: 144, time: 80)
Coordinates:
  * level    (level) float32 1e+03 925.0 850.0 700.0 ... 50.0 30.0 20.0 10.0
  * lat      (lat) float32 90.0 87.5 85.0 82.5 80.0 ... -82.5 -85.0 -87.5 -90.0
  * lon      (lon) float32 0.0 2.5 5.0 7.5 10.0 ... 350.0 352.5 355.0 357.5
  * time     (time) datetime64[ns] 2024-01-01 2024-01-02 ... 2024-03-20
Data variables:
    air      (time, level, lat, lon) float32 ...
Attributes:
    Conventions:    COARDS
    title:          mean daily NMC reanalysis (2014)
    history:        created 2013/12 by Hoop (netCDF2.3)
    description:    Data is from NMC initialized reanalysis\n(4x/day).  It co...
    platform:       Model
    dataset_title:  NCEP-NCAR Reanalysis 1
    References:     http://www.psl.noaa.gov/data/gridded/data.ncep.reanalysis...
base_map = ds.hvplot("lon", "lat")
base_map

Customizing

Add keywords such as coastline, cmap, and framewise=False (for consistent colorbar) to the call for a much more polished plot!

For better compatibility, I convert longitudes from 0:360 to -180:180 and sort–many packages just work better that way.

# for interactivity purposes on the blog, limit the number of times and levels
ds_sel = ds.isel(time=slice(0, 3), level=slice(0, 8))
ds_sel["lon"] = (ds_sel["lon"] + 180) % 360 - 180
ds_sel = ds_sel.sortby("lon")

map_plot = ds_sel.hvplot(
    "lon",
    "lat",
    coastline=True,
    cmap="RdYlBu_r",
    clabel="Air Temperature [K]",
    framewise=False,
    dynamic=False,
)
map_plot