Open In Colab

1.8. (Exercises) Replicating plots#

Hint: For this entire notebook, rather than directly filling out the entire code snippet in one go, we recommend copy-pasting hints line-by-line to progressively replicate the target figure. That way, you can learn by trial-and-error.

To add a new cell above, press “Escape” and type ‘a’

To add a new cell below, press “Escape” and type ‘b’

To delete a cell, press Ctrl-M + D

1.8.1. Exercise 1: Replicating Plots using Matplotlib and Numpy#

The goal here is to replicate the figures you see as closely as possible.

In order to get some data, you will have to run the code in the cells below. There is no need to focus on how this code exactly works. In the end, it will give you some numpy arrays, which you will use in your plots.

This exercise should be done using only numpy and matplotlib.

1.8.1.1. Part I: Line and Contour Plots to Visualize Global Temperature Data#

The temperature data are from the NCEP/NCAR atmospheric reanalysis 1.

# We'll need to load the data from the UNIL sharepoint using pooch, so let's
# import the library
import pooch
import numpy as np
import matplotlib.pyplot as plt

# In order to keep our code concise and readable, we'll use a variable that
# stores the common part of the URL where the data is hosted. Then, we'll store
# the full URL for each file in its own variable
base_url = 'https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/'
lon_url =  f'{base_url}EQtSkmHdXlZAuZcDljeHXuMBIVGfXP4lkR56RX6vuCDh1Q?download=1'
lat_url =  f'{base_url}EbMAwn26etZPjxw4F3akRt8BmPef3PMQPn751e6tF0Xi-Q?download=1'
temp_url = f'{base_url}EfUOMGrJNtVMgJUtfP9137sB9d64M_osBRPa0iQRSCHKGg?download=1'

# Let's go ahead and cache each file
lon_filename  = pooch.retrieve(lon_url,
                               known_hash='eaf54b88dd89279d3034da17fe8470dc2c841bf9fa89b2aa741dacff9c326cdb'
                               )

lat_filename  = pooch.retrieve(lat_url,
                               known_hash='af1f438080460e1fca4583b2ec19b44285a3d3776e4d21b8da9b6e162906c88a'
                               )
temp_filename = pooch.retrieve(temp_url,
                               known_hash='e040ca257334708b43e86398e09a5669fcf051179ecf5dcd278f758d67beed20'
                               )

# And then load each file into a numpy array. You can now use these variables
# to continue with the exercise :)
lon = np.load(lon_filename)
lat = np.load(lat_filename)
temp = np.load(temp_filename)
Downloading data from 'https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/EQtSkmHdXlZAuZcDljeHXuMBIVGfXP4lkR56RX6vuCDh1Q?download=1' to file 'C:\Users\tbeucler\AppData\Local\pooch\pooch\Cache\9696209cc913dc81dec9af64a3d0a93f-EQtSkmHdXlZAuZcDljeHXuMBIVGfXP4lkR56RX6vuCDh1Q'.
Downloading data from 'https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/EbMAwn26etZPjxw4F3akRt8BmPef3PMQPn751e6tF0Xi-Q?download=1' to file 'C:\Users\tbeucler\AppData\Local\pooch\pooch\Cache\b4b741a0a6c91105d7d149ecde89b508-EbMAwn26etZPjxw4F3akRt8BmPef3PMQPn751e6tF0Xi-Q'.
Downloading data from 'https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/EfUOMGrJNtVMgJUtfP9137sB9d64M_osBRPa0iQRSCHKGg?download=1' to file 'C:\Users\tbeucler\AppData\Local\pooch\pooch\Cache\540317aa038b40cf203f679d42c06920-EfUOMGrJNtVMgJUtfP9137sB9d64M_osBRPa0iQRSCHKGg'.

Below is the figure to replicate using the numpy variables temp, lon, and lat.

Hint 1: Zonal-mean is synonymous with longitudinal-mean, i.e. the mean must be taken along the axis corresponding to lon.

Hint 2: To create subplots of different sizes, consider reading the plt.subplots documentation.

Hint 3: For the left subplot, check out the 2D Plotting Methods section.

Hint 4: For the right subplot, check out the Label, Ticks, and Gridlines subsection.

Hint 5: Don’t spend too too much time making your figure perfect as there is still a lot of ground to cover in the next notebooks 😀

Unknown.png

# Replicate this figure
# Temperature values are originally in Kelvin units -> Convert to degree celsius by subtracting 273.15
fig, (__,__) = plt.subplots(__, __, figsize=(___,___),gridspec_kw={'width_ratios': [5, 1.5]})
ctemp = __.________(___,__,_________,cmap='magma',levels=np.linspace(-30,40,15),extend='both')
___._________(___,___,_________,colors='w',levels=[-10],extend='both')
___.set_xlabel('________')
___.set_ylabel('________')
___.set_title('______________________')
___._______(_____,ax=___,____='$^o$C')

___.____(np.__________(___________,axis=_),lat,lw=2,c='k')
___.set_xlabel(r'$^{o}$C')
___.set_ylabel(r'Latitude')
___.set_title('Zonal Mean Temperature')
plt.____()
plt.show()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 3
      1 # Replicate this figure
      2 # Temperature values are originally in Kelvin units -> Convert to degree celsius by subtracting 273.15
----> 3 fig, (__,__) = plt.subplots(__, __, figsize=(___,___),gridspec_kw={'width_ratios': [5, 1.5]})
      4 ctemp = __.________(___,__,_________,cmap='magma',levels=np.linspace(-30,40,15),extend='both')
      5 ___._________(___,___,_________,colors='w',levels=[-10],extend='both')

File ~\.conda\envs\JB\lib\site-packages\matplotlib\pyplot.py:1501, in subplots(nrows, ncols, sharex, sharey, squeeze, width_ratios, height_ratios, subplot_kw, gridspec_kw, **fig_kw)
   1355 def subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True,
   1356              width_ratios=None, height_ratios=None,
   1357              subplot_kw=None, gridspec_kw=None, **fig_kw):
   1358     """
   1359     Create a figure and a set of subplots.
   1360 
   (...)
   1499 
   1500     """
-> 1501     fig = figure(**fig_kw)
   1502     axs = fig.subplots(nrows=nrows, ncols=ncols, sharex=sharex, sharey=sharey,
   1503                        squeeze=squeeze, subplot_kw=subplot_kw,
   1504                        gridspec_kw=gridspec_kw, height_ratios=height_ratios,
   1505                        width_ratios=width_ratios)
   1506     return fig, axs

File ~\.conda\envs\JB\lib\site-packages\matplotlib\_api\deprecation.py:454, in make_keyword_only.<locals>.wrapper(*args, **kwargs)
    448 if len(args) > name_idx:
    449     warn_deprecated(
    450         since, message="Passing the %(name)s %(obj_type)s "
    451         "positionally is deprecated since Matplotlib %(since)s; the "
    452         "parameter will become keyword-only %(removal)s.",
    453         name=name, obj_type=f"parameter of {func.__name__}()")
--> 454 return func(*args, **kwargs)

File ~\.conda\envs\JB\lib\site-packages\matplotlib\pyplot.py:840, in figure(num, figsize, dpi, facecolor, edgecolor, frameon, FigureClass, clear, **kwargs)
    830 if len(allnums) == max_open_warning >= 1:
    831     _api.warn_external(
    832         f"More than {max_open_warning} figures have been opened. "
    833         f"Figures created through the pyplot interface "
   (...)
    837         f"Consider using `matplotlib.pyplot.close()`.",
    838         RuntimeWarning)
--> 840 manager = new_figure_manager(
    841     num, figsize=figsize, dpi=dpi,
    842     facecolor=facecolor, edgecolor=edgecolor, frameon=frameon,
    843     FigureClass=FigureClass, **kwargs)
    844 fig = manager.canvas.figure
    845 if fig_label:

File ~\.conda\envs\JB\lib\site-packages\matplotlib\pyplot.py:384, in new_figure_manager(*args, **kwargs)
    382 """Create a new figure manager instance."""
    383 _warn_if_gui_out_of_main_thread()
--> 384 return _get_backend_mod().new_figure_manager(*args, **kwargs)

File ~\.conda\envs\JB\lib\site-packages\matplotlib_inline\backend_inline.py:27, in new_figure_manager(num, FigureClass, *args, **kwargs)
     21 def new_figure_manager(num, *args, FigureClass=Figure, **kwargs):
     22     """
     23     Return a new figure manager for a new figure instance.
     24 
     25     This function is part of the API expected by Matplotlib backends.
     26     """
---> 27     return new_figure_manager_given_figure(num, FigureClass(*args, **kwargs))

File ~\.conda\envs\JB\lib\site-packages\matplotlib\_api\deprecation.py:454, in make_keyword_only.<locals>.wrapper(*args, **kwargs)
    448 if len(args) > name_idx:
    449     warn_deprecated(
    450         since, message="Passing the %(name)s %(obj_type)s "
    451         "positionally is deprecated since Matplotlib %(since)s; the "
    452         "parameter will become keyword-only %(removal)s.",
    453         name=name, obj_type=f"parameter of {func.__name__}()")
--> 454 return func(*args, **kwargs)

File ~\.conda\envs\JB\lib\site-packages\matplotlib\figure.py:2568, in Figure.__init__(self, figsize, dpi, facecolor, edgecolor, linewidth, frameon, subplotpars, tight_layout, constrained_layout, layout, **kwargs)
   2565 if frameon is None:
   2566     frameon = mpl.rcParams['figure.frameon']
-> 2568 if not np.isfinite(figsize).all() or (np.array(figsize) < 0).any():
   2569     raise ValueError('figure size must be positive finite not '
   2570                      f'{figsize}')
   2571 self.bbox_inches = Bbox.from_bounds(0, 0, *figsize)

TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

1.8.1.2. Part II: Scatter Plots to Visualize Earthquake Data#

Here, we will make a map plot of earthquakes from a USGS catalog of historic large earthquakes. Color the earthquakes by log10(depth) and adjust the marker size to be magnitude

fname = pooch.retrieve(
    "https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/EW1bnM3elHpAtjb1KtiEw0wB9Pl5w_FwrCvVRlnilXHCtg?download=1",
    known_hash='22b9f7045bf90fb99e14b95b24c81da3c52a0b4c79acf95d72fbe3a257001dbb',
    processor=pooch.Unzip()
)[0]

earthquakes = np.genfromtxt(fname, delimiter='\t')
depth = earthquakes[:, 8]
magnitude = earthquakes[:, 9]
latitude = earthquakes[:, 20]
longitude = earthquakes[:, 21]

Below is the figure to replicate using the numpy variables earthquake, depth, magnitude, latitude, and longitude.

Hint: Check out the Scatter Plots subsection and consider reading the documentation for plt.scatter and plt.colorbar.

Unknown-2.png

# This is the function to the set the ticks in the colorbar to be in scientific notations.
# You can directly use this function and don't need to change anything in this cell.
import matplotlib.ticker as ticker
def fmt(x, pos):
    a, b = '{:.2e}'.format(x).split('e')
    b = int(b)
    return r'$10^{{{}}}$'.format(a)
# Replicate the figure here
___,___ = _____._________(_, _, figsize=(__,__))
ctemp=________._______(_______,_______,s=________,c=np._____(_____),marker='o',cmap='viridis',vmin=0,vmax=2.5)
___.________('__________')
___.________('__________')
plt.grid()
___._________(____,ax____,label=_________,format=ticker.FuncFormatter(fmt))
___.____('_________________________')
plt.show()

1.8.2. Exercise 2: Cartopy#

The goal of this exercise (congratulations for making it that far!! 😃) is to replicate the figures you see as closely as possible.

1.8.2.1. Part I: Antarctic Sea Ice#

Q1) Download the file below and use it to plot the concentration of Antarctic Sea Ice.

Try to recreate the plot below.

Hint: Explore the file contents in order to determine the correct projection.

cartopy_pic1.png

!pip install --no-binary 'shapely==1.6.4' 'shapely==1.6.4' --force
!pip install cartopy
import pooch
import cartopy
import cartopy.crs as ccrs
#import xarray as xr
from netCDF4 import Dataset
######################################################################################################################################################
# Download Sea Ice files
url1 = "https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/EREWA38Rs-FFslQd4yKZBAsBF8m9yLzeJKEAN5gSz7LLFw?download=1"
fname1 = pooch.retrieve(url1, known_hash='1ff50bca1e6249a9b2fcd9d9466e31bdb5be650243f99c7319ab2ce625b87ce7')
url2 = "https://unils-my.sharepoint.com/:u:/g/personal/tom_beucler_unil_ch/Ea_2umrDTkhCrN--th4nuokBMcnVlxGshiyUq2eSpvhlTQ?download=1"
fname2 = pooch.retrieve(url2, known_hash='309418969ad09f42b8104589bcb86de4ed353a5742fef9385baec174c7d55e66')
######################################################################################################################################################
# Run these as is. We are reading the sea ice concentration values from our files
seaice1,seaice2 = Dataset(fname1,'r'),Dataset(fname2,'r')
var_toplot1,var_toplot2 = seaice1.variables['seaice_conc_cdr'][:],seaice2.variables['seaice_conc_cdr'][:]
import matplotlib.pyplot as plt
#import cartopy.feature as cfeature
import numpy as np

fig,ax = plt.subplots(_,_,figsize=(18, 12),subplot_kw={'projection': ccrs.___________________()})
# The rest doesn't change:
ax[_].set_extent([-180, 180, -90, -55], ccrs.PlateCarree())

######################################################################################################################################################
# Add Land and gridlines
# Hint: Check edgecolor to show coast, and facecolor to color the continent
# Hint: https://matplotlib.org/stable/gallery/color/named_colors.html for the names of the colors available
######################################################################################################################################################
ax[_].______________(cartopy.______.____, edgecolor='___',facecolor='________')
ax[_].gridlines(lw=_,color='_')
######################################################################################################################################################
# Add Contours
# Hint: X (Y) data in the pcolormesh should be the longitude and latitude of the sea ice data.
# Hint: Check the shape of the var_toplot1, var_toplot2 before plotting
# Hint: longitude and latitude can be accessed with seaice1.variables['longitude'][:], seaice1.variables['latitude'][:]
# Hint: Check this website: https://matplotlib.org/stable/tutorials/colors/colormaps.html for available colormaps
######################################################################################################################################################
____ = ax[0].pcolormesh(___________, ____________, np.ma.masked_greater(var_toplot1[_________],1), transform=__________,vmin=0,vmax=1,cmap=____)
plt.colorbar(____,ax=ax[_],fraction=0.046, pad=0.04,____='__________________')
ax[_].set_title('2017-08-01')

ax[_].set_extent([-180, 180, -90, -55], ccrs.PlateCarree())
ax[_].add_feature(cartopy._______._________, edgecolor='______',facecolor='_______________')
ax[_].gridlines(lw=_,color='____')

____ = ax[____].pcolormesh(____, ____, np.ma.masked_greater(var_toplot2[____],1), transform=____,vmin=0,vmax=1,cmap=____)
plt.colorbar(____,ax=__________________,label='__________________',fraction=0.046, pad=0.04)
ax[____].set_title('__________________')
plt.show()

1.8.2.2. Part II: 2014 Earthquakes#

Q2) Download the file below and use it to plot the location of >4 Richter Scale earthquakes in the US during 2014.

Hint: Explore the file contents in order to determine the correct projection.

cartopy_pic2.png

import pandas as pd
fname = pooch.retrieve(
    "https://unils-my.sharepoint.com/:x:/g/personal/tom_beucler_unil_ch/Ea9h1j2_wYpEtuX5waZZWpsBt2zh3lvGUBFisvA8dFG5Eg?download=1",
    known_hash='84d455fb96dc8f782fba4b5fbe56cb8970cab678f07c766fcba1b1c4674de1b1')
usgs_2014 = pd.read_csv(fname)
######################################################################################################################################################
# Run these as is. We are removing missing values in the data and filter data points with stronger earthquakes (magnitude > 4)
usgs_2014_nonan = usgs_2014.dropna()
usgs_2014_large = usgs_2014_nonan[usgs_2014_nonan['mag']>4]
fig,ax = plt.subplots(1,1,figsize=(12, 12),subplot_kw={'projection': ccrs.Robinson()})
# The rest doesn't change:
ax.set_extent([-140, -60, 12,70], ccrs.PlateCarree())

######################################################################################################################################################
# Add Land, Ocean, Rivers, and State Lines
# Hint: Land linewidth = 3; States: edgecolor='gray',linewidth=1.25; lakes/ocean/rivers: no change
# Hint: https://matplotlib.org/stable/gallery/color/named_colors.html for the names of the colors available
######################################################################################################################################################
____.add_feature(cartopy._____.____, edgecolor='____',linewidths=____)
____.add_feature(cartopy._____.____)
____.add_feature(cartopy._____.____)
____.add_feature(cartopy._____.____)
____.add_feature(cartopy._____.____,edgecolor='____',linewidth=____)
____.gridlines(lw=____,color='____')

######################################################################################################################################################
# Add Scatter points for earthquake data
# Hint: usgs_2014_large['longitude'] to access logitude data, usgs_2014_large['latitude'] to access latitude data, usgs_2014_large['mag'] to access Earthquake magnitudes
# Hint: https://matplotlib.org/stable/gallery/color/named_colors.html for the names of the colors available
######################################################################################################################################################
____ = ax.scatter(____,____,s=62,c=____,marker='o',cmap='____',edgecolors='____',linewidths=2, transform=____)
plt.colorbar(____,ax=____,fraction=0.016, pad=0.04,label='____')
ax.set_title('____')
plt.show()