Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Overview

Spaghetti plots are a tool typically used to visualize movement. Essentially they are many line plots displayed on the same axes. By drawing the same path at different times or from different forecasts, we can see the patterns and chaos associated with the plotted variable.

  1. Spaghetti Hurricane Plot

  2. Spaghetti Contour Plot

Prerequisites

ConceptsImportanceNotes
MatplotlibNecessary
CartopyNecessary
  • Time to learn: 10 minutes


Spaghetti Hurricane Plot

Visualizing the predicted path of an incoming hurricane is both complicated and important. There are many plots that meteorologists are trained to read, but when shared with the public can be confusing or alarming. There are strengths and weaknesses to each hurricane visualization approach. The cone of uncertainty, for example, is often misinterpreted to suggest the hurricane growth in time rather than widening of path possibilities. Spaghetti plots on the other hand, clearly show hurricane paths but show them as equal to each other.

In this example we will plot some forecasted paths from the 2012 North-Atlantic storm Hurricane Sandy. Each forecast is from the Global Ensemble Forecast System (GEFS) provided by the National Centers for Environmental Prediction at NOAA.

We’ll use the package tropycal to easily access HURDAT2 and IBTrACS reanalysis data and operational National Hurricane Center (NHC) Best Track data. tropycal has a lot of great features for real time hurricane visualization, but since this Cookbook is comparatively static we’re using a past hurricane and only using this package to access the data. Our plotting will be done with matplotlib and cartopy.

Read in Data

First, to grab our hurricane data from tropycal we need to specify a basin:

--> Starting to read in HURDAT2 data
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 1
----> 1 basin = tracks.TrackDataset(basin='north_atlantic')

File ~/micromamba/envs/advanced-viz-cookbook/lib/python3.14/site-packages/tropycal/tracks/dataset.py:222, in TrackDataset.__init__(self, basin, source, include_btk, interpolate_data, **kwargs)
    220 # Read in from specified data source
    221 if source == 'hurdat':
--> 222     self.__read_hurdat()
    223 elif source == 'ibtracs':
    224     self.__read_ibtracs()

File ~/micromamba/envs/advanced-viz-cookbook/lib/python3.14/site-packages/tropycal/tracks/dataset.py:450, in TrackDataset.__read_hurdat(self, override_basin)
    447 mslp = int(mslp)
    449 # Fix longitude for Atlantic storms east of the prime meridian
--> 450 if add_basin == 'north_atlantic' and lon < -180:
    451     lon += 360.0
    453 # Handle missing data

TypeError: '<' not supported between instances of 'str' and 'int'

Find your storm by name and year:

And we can grab any of a number of forecasts:

Each key represents a forecast model, we’ll select the GFS AP01 forecast which has many initializations. These initializations are named by time in YYYYMMDDHH format:

Spaghetti Plot of One Esemble Member

Let’s set up our Cartopy grid to plot one ensemble member of the hurricane model.

These steps might be familiar to you but we:

  • create our axes with a Plate Caree projection

  • add land features

  • add grid lines

  • edit our gridline labels to not duplicate on all four sides

  • edit our gridline label fontsize

Looking at GFS Ensemble Member Forecast AP01, we can make a spaghetti plot of each of these initializations.

The crux of the visualization is that we loop through and plot each initialization (in for i in forecasts_AP01), plot the true hurricane path, and add a legend (plt.legend()).

This plot is a great example of a spaghetti plot, but is it super useful? Is it confusing? Each line looks like it carries the same weight, when some of these possible paths are from hours before Sandy hit the Northeastern United States and others are from days before.

Maybe it is better to show the user some indication of how the forecast for this ensemble converged on the true path with later and later initialization times.

Spaghetti Plot of One Esemble Member with Temporal Colormapping

Some additions to look out for:

  • we grab the time information from the initialization name using datetime

  • normalize a colormap by the time information with cmap = mpl.colors.ListedColormap(plt.cm.autumn_r(normalized_times))

  • loop through the colormap as we loop through the time steps within the ensemble member

  • add a colorbar with plt.colorbar(plt.cm.ScalarMappable(cmap=cmap)), where ScalarMappable is used to map scalar data to RGBA.

Now we can see that as the storm progressed, the AP01 GFS Forecast Ensemble Member converges on Sandy’s true path as the storm progresses through October, 2012.

Alternatively, we may want to plot the possible hurricane paths from multiple GFS Forecast Ensemble members from the same iteration timestamp as a spaghetti plot.

Spaghetti Plot of All Esemble Members at One Point in Time

First, we need to grab all of the relevant forecast keys to GFS models (the ones that are titled AP## from 0 to 20):

Hurricane Sandy hit the Northeast on October 29, 2012. From this spaghetti plot we can see that by the 27th most ensemble members of the GFS forecast predicted a similar behavior for the storm.

There is more analysis that could be done on hurriane trajectories. We have covered some plotting customization that might be useful for your analysis and data visualization.

Spaghetti Contour Plot

In this example we will read in the geopotential height datafile HGT500_MON_1958-1997.nc from using geocat-datafiles. Then we will look at different timesteps of the HGT geopotential height variable at the 5500 gpm level, plotting this contour’s locations through time. This example is adapted from GeoCAT’s NCL_conOncon_5 script.

Read in data:

Initial Spaghetti Plot on North Polar Stereographic Projection

Again, first let’s set up our Cartopy axes. This time setting our projection to NorthPolarStereo.

Then let’s add our data to this plot.

We will iterate through every 12th timestep

  • handling any artifacts of the global wrapping at 0 or 360 degrees with gv.xr_add_cyclic_longitudes(p, "lon")

  • and calling a contour plot on a single level.

Adding Directional Labels to Polar Stereographic Projection

Adding labels to a map projection that aren’t lat/lon coordinates is less than intuitive. In this example we manually add labels and select their locations so that you can see NESW labels.

For this we use mticker.FixedLocator() to manipulare gridline spacing, manipulate East and West tick labels separately, and specify tick locations with ax.text(transform=ccrs.Geodetic()).

Now in this example, there isn’t necessarily a temporal progression of geopotential height, but to be sure let’s add a colormap component to each of our loops.

This is also useful because for your data visualization application there might be, and the commands are slightly different for a contour plot as for a line plot in the above example.

Contour Spaghetti Plot Temporal Colorbar Manipulation

Let’s update add a discrete colorbar that has yearly ticklabels. One challenge addressed in this example is setting the ticklabels to be in the center of each discrete color box.

New code lines here are:

  • creating a discrete colormap with plt.get_cmap('winter_r', n) and color bounds with np.linspace()

  • specifying the color in each contour call with colors=[cmap(bounds)[x]]

  • adjusting the time unit for the colorbar ticks

  • adding a colorbar for the normalized colormap, calling the orientation, shrink, and pad keyword arguments to make it display well

  • setting colorbar tick location to be at color midpoints with cbar.set_ticks(), yet forcing their labels to be years (not year midpoints) with cbar.set_ticklabels()

Contour Spaghetti Plot with Hand-Picked Colors

If you want your plot to be visually appealing it might be worth selecting different colors for each contour plot in the for-loop, however these do not have to be sequentially ordered or time-aware. It is actually simplest to hand-pick colors for each loop. In this iteration of the plot we hand pick colors in a colorlist and plot the first time step on its own to demonstrate plotting one loop unlike the others.


Summary

Spaghetti plots are many lines drawn on the same figure. They have pros and cons. They are visually stunning but can be confusing, so it is important to make sure your data visualization conveys accurate information either by manipulating color or linewidth. Since the manipulation of spaghetti plots have their own considerations, this chapter shows several design choices that you can use to jumpstart your visualization needs.

What’s next?

Next up let’s discuss Animation.