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 😀
# 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
.
# 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.
!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.
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()