Visualization

Automatic interactive plots for almost any netCDF file, plus publication-quality static figures with pub_plot.

Interactive plotting

Call plot() on a dataset for an automatic interactive plot, similar in spirit to the command-line tool ncview — but in Jupyter or a browser. Illustrated below with a sea-surface temperature dataset:

python
ds = nc.open_data("sst.mon.mean.nc")
ds.subset(year=2000)
ds.plot()
ds.plot() — interactive output Open interactive
Mean sea surface temperature for 2000

The plot type is chosen automatically from the shape of the data. A zonal mean gives a zonal profile:

python
ds = nc.open_data("sst.mon.mean.nc")
ds.subset(year=2000)
ds.tmean()
ds.zonal_mean()
ds.plot()          # zonal profile
ds.plot() — interactive output Open interactive
Zonal mean sea surface temperature

A zonal mean tracked over time renders as a Hovmöller-style heatmap — here, the change in zonal-mean temperature relative to an 1850–1869 baseline:

python
ds = nc.open_data("sst.mon.mean.nc")
ds.zonal_mean()
ds.annual_anomaly(baseline=[1850, 1869], window=20)
ds.plot()
ds.plot() — interactive output Open interactive
Zonal-mean temperature anomaly over time

Once the data has a single spatial value per time step (e.g. after spatial_mean()), it plots as a time series — here, global mean sea surface temperature since 1850:

python
ds = nc.open_data("sst.mon.mean.nc")
ds.spatial_mean()
ds.plot()          # time series
ds.plot() — interactive output Open interactive
Global mean sea surface temperature time series since 1850

Publication-quality plots with pub_plot

pub_plot (introduced in v0.9.2) produces a static plot suitable for a paper or presentation, currently restricted to regular lon/lat grids with a limited set of customizations (such as the colour scale):

python
ds.tmean()
ds.pub_plot()

Publication-quality plot of mean sea surface temperature produced by pub_plot()

All pub_plot options
  • var

    Variable to plot. Only needed when the dataset has more than one.

  • extent

    [lon_min, lon_max, lat_min, lat_max] in degrees, to plot part of the map.

  • title

    Plot title.

  • legend

    Colour bar label. Built from the variable's long name and units by default.

  • size

    [width, height] of the figure in inches.

  • land

    Colour to fill the land with, e.g. "grey".

  • colours

    Colour map, e.g. "viridis". Add "_r" to reverse it.

  • norm

    "log" for a logarithmic colour scale, or a matplotlib normalisation.

  • limits

    [min, max] of the colour scale. Each end can be a number, None or a percentile such as "2%".

  • projection

    A cartopy projection, e.g. ccrs.Mollweide().

  • coast

    Coastline detail: "auto", or GSHHS coastlines from "coarse" to "full". None draws none.

  • scale

    Resolution of the land fill: "low", "medium" or "high".

  • grid

    Whether to draw the dashed grid lines. Default True.

  • grid_colour

    Colour of the grid lines.

  • grid_labels

    Whether to show the lon/lat labels around the map. Default True.

  • legend_position

    "right" or "bottom" for the colour bar, or None to drop it.

  • robust

    Set the colour limits to the 2nd and 98th percentiles, so outliers don't wash out the scale.

  • breaks

    Tick positions on the colour bar.

  • font

    Font size for the title and colour bar label.

  • out

    File to save the figure to, e.g. "sst.png".

  • dpi

    Resolution of the saved file, e.g. 300 for print.

See the API reference for the formats, defaults and remaining options.

Each example below changes one of those options. Everything else is left at its default, and projection needs import cartopy.crs as ccrs.

Panel plots with panel_plot

nc.panel_plot draws a grid of pub_plot-style maps from a dictionary of datasets, with the keys as the panel titles. Every panel shares one colour scale and one colour bar by default, worked out from the data of all of them, so they can be compared directly. The examples below use monthly COBE-SST2 sea surface temperature, opened straight from a THREDDS server.

A seasonal climatology, two panels per row:

python
ds = nc.open_thredds("https://psl.noaa.gov/thredds/dodsC/Datasets/COBE2/sst.mon.mean.nc")

panels = {}
for season in ["DJF", "MAM", "JJA", "SON"]:
    seasonal = ds.copy()
    seasonal.subset(years=range(1991, 2021), seasons=season)
    seasonal.tmean()
    panels[season] = seasonal

nc.panel_plot(panels, ncol=2, limits=[0, 30], legend="Sea surface temperature (°C)")

Four panels of seasonal mean sea surface temperature sharing one colour bar

Panels do not have to share a scale. With shared_colourbar=False each panel gets its own colour bar, and the plotting options can be given as a list with one value per panel — useful when a map of values sits beside a map of differences:

python
early = nc.open_thredds("https://psl.noaa.gov/thredds/dodsC/Datasets/COBE2/sst.mon.mean.nc")
early.subset(years=range(1850, 1880))
early.tmean()

recent = nc.open_thredds("https://psl.noaa.gov/thredds/dodsC/Datasets/COBE2/sst.mon.mean.nc")
recent.subset(years=range(1995, 2025))
recent.tmean()

change = recent.copy()
change.subtract(early)

nc.panel_plot(
    {"Mean SST, 1995-2024": recent, "Change since 1850-1879": change},
    ncol=2,
    shared_colourbar=False,
    colours=["viridis", "RdBu_r"],
    limits=[[0, 30], [-2, 2]],
    legend=["Sea surface temperature (°C)", "Temperature change (°C)"],
    legend_position="bottom",
)

Mean sea surface temperature beside the change since 1850-1879, each with its own colour bar

panel_plot takes every option pub_plot does. A single value applies to all panels; a list applies one value per panel, in the order of the dictionary.

All panel_plot options
  • ncol

    Number of panel columns. Chosen automatically if not given.

  • nrow

    Number of panel rows. Chosen automatically if not given.

  • shared_colourbar

    True (default) for one shared colour bar, False for one colour bar per panel.

  • size

    [width, height] of the whole figure in inches.

  • out

    File to save the whole figure to.

  • dpi

    Resolution of the saved file.

  • var, extent, land, projection, coast, scale, grid, grid_colour, grid_labels

    Same as pub_plot. A single value applies to every panel; a list applies one value per panel, whether or not the colour bar is shared.

  • colours, norm, limits, robust, legend, legend_position, breaks, font

    Same as pub_plot. With shared_colourbar=True these must be single values, since one colour bar needs one scale. With shared_colourbar=False they can be single values or a per-panel list too.

See the API reference for the formats, defaults and remaining options.

The seasonal climatology from above, with land, grid, colours and limits all varied per panel:

python
ds = nc.open_thredds("https://psl.noaa.gov/thredds/dodsC/Datasets/COBE2/sst.mon.mean.nc")

panels = {}
for season in ["DJF", "MAM", "JJA", "SON"]:
    seasonal = ds.copy()
    seasonal.subset(years=range(1991, 2021), seasons=season)
    seasonal.tmean()
    panels[season] = seasonal

nc.panel_plot(
    panels,
    ncol=2,
    shared_colourbar=False,
    land=["grey", "tan", "lightgrey", None],
    grid=[True, False, True, False],
    colours=["viridis", "plasma", "cividis", "magma"],
    limits=[[0, 25], [0, 28], [5, 30], [0, 25]],
    legend_position="bottom",
)

Seasonal sea surface temperature with land colour, grid lines, colour map and colour limits varied per panel

Plotting internals

Interactive plotting is delegated to the companion ncplot package, which inspects the dataset and picks a suitable plot built on hvplot. It favours rapid exploratory analysis over deep customization, but most hvplot customization options — title, logz, clim, and so on — can be passed straight to plot() and are forwarded automatically.