matplotlib

There are many different python packages that can be useful for geophysical analysis. These include reading data from files (e.g., pandas), data handling routines (e.g., xarray), plotting (matplotlib) and others. We will go through several example cases in this section.

Most of our plotting will be done using the Matlab equivalent “matplotlib” package. This is typically imported in scripts, specifically the pyplot routines, as “plt”. In the examples below we will look at plotting data using matplotlib, reading data from a from a file using pandas, and then creating our own data arrays with numpy.

Matplotlib is the most popular plotting library in python. Using matplotlib, you can create pretty much any type of plot and the syntax is much like Matlab (so it should be easy going back and forth between the two).

The standard way to import matplotlib is by just loading the pyplot object. Here we will alias this to simply plot.

To plot a series of numbers, with the independent (x-axis) and dependent variables (y-axis) already defined, use the plot function. Note that if the independent variable is not supplied, the x-axis will be the list position. For example:

[1]:
import matplotlib.pyplot as plt
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
plt.plot(x,y)
[1]:
[<matplotlib.lines.Line2D at 0x7fa8c40b46d0>]
../_images/notebooks_0204_Matplotlib_plotting_2_1.png
[11]:
# change color or line
# add three character sequence in quotes
# first is color, linestyle, third is symbol
#
# e.g., red = 'r'
#plt.plot(x,y,'k')
# e.g., dotted = '.'
#plt.plot(x,y,'rs')
# line style
plt.plot(x,y,'r*:')
[11]:
[<matplotlib.lines.Line2D at 0x7fa8afb85850>]
../_images/notebooks_0204_Matplotlib_plotting_3_1.png
[16]:
# can also use key word arguments (aka kwargs)
plt.plot(x,y,color='cyan',
         linestyle='dashed',
         marker='o',linewidth='4',
         markersize='6');
../_images/notebooks_0204_Matplotlib_plotting_4_0.png
[24]:
#plt.bar(x,y,facecolor='red',
#        edgecolor='black')
plt.bone();
<Figure size 432x288 with 0 Axes>
[21]:
dir(plt)
[21]:
['Annotation',
 'Arrow',
 'Artist',
 'AutoLocator',
 'Axes',
 'Button',
 'Circle',
 'Figure',
 'FigureCanvasBase',
 'FixedFormatter',
 'FixedLocator',
 'FormatStrFormatter',
 'Formatter',
 'FuncFormatter',
 'GridSpec',
 'IndexLocator',
 'Line2D',
 'LinearLocator',
 'Locator',
 'LogFormatter',
 'LogFormatterExponent',
 'LogFormatterMathtext',
 'LogLocator',
 'MaxNLocator',
 'MouseButton',
 'MultipleLocator',
 'Normalize',
 'NullFormatter',
 'NullLocator',
 'Number',
 'PolarAxes',
 'Polygon',
 'Rectangle',
 'ScalarFormatter',
 'Slider',
 'Subplot',
 'SubplotSpec',
 'Text',
 'TickHelper',
 'Widget',
 '_INSTALL_FIG_OBSERVER',
 '_IP_REGISTERED',
 '_IoffContext',
 '_IonContext',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 '_api',
 '_auto_draw_if_interactive',
 '_backend_mod',
 '_copy_docstring_and_deprecators',
 '_get_required_interactive_framework',
 '_interactive_bk',
 '_log',
 '_pylab_helpers',
 '_setup_pyplot_info_docstrings',
 '_warn_if_gui_out_of_main_thread',
 '_xkcd',
 'acorr',
 'angle_spectrum',
 'annotate',
 'arrow',
 'autoscale',
 'autumn',
 'axes',
 'axhline',
 'axhspan',
 'axis',
 'axline',
 'axvline',
 'axvspan',
 'bar',
 'bar_label',
 'barbs',
 'barh',
 'bone',
 'box',
 'boxplot',
 'broken_barh',
 'cbook',
 'cla',
 'clabel',
 'clf',
 'clim',
 'close',
 'cm',
 'cohere',
 'colorbar',
 'colormaps',
 'connect',
 'contour',
 'contourf',
 'cool',
 'copper',
 'csd',
 'cycler',
 'delaxes',
 'disconnect',
 'docstring',
 'draw',
 'draw_all',
 'draw_if_interactive',
 'errorbar',
 'eventplot',
 'figaspect',
 'figimage',
 'figlegend',
 'fignum_exists',
 'figtext',
 'figure',
 'fill',
 'fill_between',
 'fill_betweenx',
 'findobj',
 'flag',
 'functools',
 'gca',
 'gcf',
 'gci',
 'get',
 'get_backend',
 'get_cmap',
 'get_current_fig_manager',
 'get_figlabels',
 'get_fignums',
 'get_plot_commands',
 'get_scale_names',
 'getp',
 'ginput',
 'gray',
 'grid',
 'hexbin',
 'hist',
 'hist2d',
 'hlines',
 'hot',
 'hsv',
 'importlib',
 'imread',
 'imsave',
 'imshow',
 'inferno',
 'inspect',
 'install_repl_displayhook',
 'interactive',
 'ioff',
 'ion',
 'isinteractive',
 'jet',
 'legend',
 'locator_params',
 'logging',
 'loglog',
 'magma',
 'magnitude_spectrum',
 'margins',
 'matplotlib',
 'matshow',
 'minorticks_off',
 'minorticks_on',
 'mlab',
 'new_figure_manager',
 'nipy_spectral',
 'np',
 'pause',
 'pcolor',
 'pcolormesh',
 'phase_spectrum',
 'pie',
 'pink',
 'plasma',
 'plot',
 'plot_date',
 'plotting',
 'polar',
 'prism',
 'psd',
 'quiver',
 'quiverkey',
 'rc',
 'rcParams',
 'rcParamsDefault',
 'rcParamsOrig',
 'rc_context',
 'rcdefaults',
 'rcsetup',
 're',
 'register_cmap',
 'rgrids',
 'savefig',
 'sca',
 'scatter',
 'sci',
 'semilogx',
 'semilogy',
 'set_cmap',
 'set_loglevel',
 'setp',
 'show',
 'specgram',
 'spring',
 'spy',
 'stackplot',
 'stairs',
 'stem',
 'step',
 'streamplot',
 'style',
 'subplot',
 'subplot2grid',
 'subplot_mosaic',
 'subplot_tool',
 'subplots',
 'subplots_adjust',
 'summer',
 'suptitle',
 'switch_backend',
 'sys',
 'table',
 'text',
 'thetagrids',
 'threading',
 'tick_params',
 'ticklabel_format',
 'tight_layout',
 'time',
 'title',
 'tricontour',
 'tricontourf',
 'tripcolor',
 'triplot',
 'twinx',
 'twiny',
 'uninstall_repl_displayhook',
 'violinplot',
 'viridis',
 'vlines',
 'waitforbuttonpress',
 'winter',
 'xcorr',
 'xkcd',
 'xlabel',
 'xlim',
 'xscale',
 'xticks',
 'ylabel',
 'ylim',
 'yscale',
 'yticks']
[ ]:
# put multiple plots on a page
# "subplot", number of rows, number of columns
# and plot id number
#
# Note, python starts counting at 0, but not
# matplotlib in subplots
[28]:
plt.subplot(2,2,1)
plt.plot(x,y,'r')
plt.subplot(2,2,2)
plt.plot(x,y,'g')
plt.subplot(2,2,3)
plt.bar(x,y)
plt.subplot(2,2,4)
plt.bar(x,y,facecolor='red')

# add title to panel 2:
plt.subplot(2,2,2)

[28]:
<BarContainer object of 5 artists>
../_images/notebooks_0204_Matplotlib_plotting_8_1.png
[31]:
# adding titles and labels
# plt.title: plot title
# plt.xlabel: x-axis
# plt.ylabel: y-axis
plt.plot(x,y)
plt.title('My plot')
plt.xlabel('time',color='green')
plt.ylabel('data')
[31]:
Text(0, 0.5, 'data')
../_images/notebooks_0204_Matplotlib_plotting_9_1.png
[44]:
# adding a legend
z = [1, 2, 3, 4, 5]
plt.plot(x,y,'ro:',label='scores')
plt.plot(x,z,'g*:',label='grades')
plt.legend()
plt.grid(linestyle='dashed',color='yellow')
../_images/notebooks_0204_Matplotlib_plotting_10_0.png
[36]:
len(z)
[36]:
6
[ ]:

[15]:
#plt.suplots(nrow=2,ncols=2)
plt.subplot(2,2,1)
plt.plot(x,y)
plt.subplot(2,2,2)
plt.bar(x,y)
plt.subplot(2,2,1)
plt.title("hello")
<ipython-input-15-fe65303fe3fd>:6: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance.  In a future version, a new instance will always be created and returned.  Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
  plt.subplot(2,2,1)
[15]:
Text(0.5, 1.0, 'hello')
../_images/notebooks_0204_Matplotlib_plotting_13_2.png

The plot shows y plotted as a function of x. Just like in matlab, if we just plot y (plt.plot(y), we’d get a plot of y as a function of index (0, 1, 2, …) since the independent variable was not specified.

There are a few things to note. First, in this notebook the plot is shown “in line”. Normally, in an interactive session, the plot doesn’t actually appear until the “show” command is issued (e.g., plt.show()). Second, the generated plot is a line using the default color and style (and no symbol). We will see how to change this next. Finally, to save the figure to an image, the plt.savefig function can be used (more on this later).

The plot can be spruced up a bit by optionally specifying the color, symbol and line style (in that order). These are specified with a single character for each. Colors, for example, can be specified as red (‘r’), green (‘g’), etc.; symbols as circles (‘o’), squares (‘s’), etc.; and lines as solid (‘-‘), dashed (‘- -‘), etc. Further examples:

  • ’r*–’ : ‘red stars with dashed lines’

  • ‘ks.’ : ‘black squares with dotted line’ (‘k’ stands for black)

  • ‘bD-.’ : ‘blue diamonds with dash-dot line’.

For a complete list of colors, markers and linestyles, check out the help(plt.plot) command. Let’s repeat the example above but use green dots:

[2]:
import matplotlib.pyplot as plt
%matplotlib inline
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
plt.plot(x,y,'g.')
[2]:
[<matplotlib.lines.Line2D at 0x7f104547ad30>]
../_images/notebooks_0204_Matplotlib_plotting_15_1.png

Just like Matlab, multiple lines can be plotted in the same panel by calling plt.plot multiple times. With multiple lines, it’s usually helpful to include a legend. Legends, and more generally plot labels are done using specific function calls.

[45]:
#import matplotlib.pyplot as plt
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
z = [ 2, 5, 10, 17, 26 ]
plt.plot(x,y,'go',label='Green Dots')
plt.plot(x,z,'b*',label='Blue Stars')
plt.title('Example plot')
plt.xlabel('time')
plt.ylabel('amplitude')
plt.legend(loc='upper left')
[45]:
<matplotlib.legend.Legend at 0x7fa8aa403a00>
../_images/notebooks_0204_Matplotlib_plotting_17_1.png
[52]:
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
plt.plot(x,y)

plt.plot(x,y,color='red',
         linestyle='dashed',
         marker='o',linewidth='4',
         markersize='12')

plt.bar(x,y,facecolor='red',
        edgecolor='black')

plt.subplot(2,2,1)
plt.plot(x,y)
plt.subplot(2,2,2)
plt.bar(x,y)
plt.subplot(2,2,1)
plt.title("hello")

import matplotlib.pyplot as plt
%matplotlib inline
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
z = [ 2, 5, 10, 17, 26 ]
plt.plot(x,y,'go',label='Green Dots')
plt.plot(x,z,'b*',label='Blue Stars')
plt.title('Example plot')
plt.xlabel('time')
plt.ylabel('amplitude')
plt.legend(loc='upper left')

import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

# x vs. y, sized by variable s, colored by
# variable c
plt.scatter(x, y, s=area,
            c=colors,
            alpha=0.5)

[52]:
<matplotlib.collections.PathCollection at 0x7fa8aa2e7c40>
../_images/notebooks_0204_Matplotlib_plotting_18_1.png
[53]:
import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

# x vs. y, sized by variable s, colored by
# variable c
plt.scatter(x, y, s=area,
            c=colors,
            alpha=0.5)
[53]:
<matplotlib.collections.PathCollection at 0x7fa8aa1e5f40>
../_images/notebooks_0204_Matplotlib_plotting_19_1.png
[49]:
# load numpy package
import numpy as np

# set N to 50 (numbers)
N = 50
# generate "N" number of random numbers
x = np.random.rand(N)
y = np.random.rand(N)
z = np.random.rand(N)
[51]:
# scatter plot of x vs y
plt.scatter(x,y,c=z)
[51]:
<matplotlib.collections.PathCollection at 0x7fa8aa301820>
../_images/notebooks_0204_Matplotlib_plotting_21_1.png

Before proceeding, it should be noted that matplotlib is somewhat different in the sense that is has both a Matlab-like syntax and object-oriented syntax, and this can lead to confusion. Thus, there is no one consistent way to make the same plot (for example). We will see examples of this when making a figure with more than one panel. One way to achieve this is using “axes”, whereby a figure with subplots is defined as an object, and then formatted later. The Matlab syntax, on the other hand, is considered “stateful”. In this case plt keeps track of which subplot is active, and formats that specific one. So whatever you draw with plt.{anything} will reflect only on the current subplot. Practically speaking, the main difference between the two syntaxes is, in Matlab-like syntax, all plotting is done using plt methods instead of the respective axes‘s method as in object oriented syntax.

As an example, let’s draw our two sets of points (green rounds and blue stars) in two separate plots side-by-side instead of the same plot. You can do that by creating two separate subplots, aka, axes, using plt.subplots(1, 2). This creates and returns two objects:

  • the figure

  • the axes (subplots) inside the figure

Previously, I called plt.plot() to draw the points. Since there was only one axes by default, it drew the points on that axes itself. But now, since you want the points drawn on different subplots (axes), you have to call the plot function in the respective axes (ax1 and ax2 in below code) instead of plt. Notice in below code, calls are made to ax1.plot() and ax2.plot() instead of calling plt.plot() twice.

[54]:
# import functions
from matplotlib import pyplot as plt

# Create Figure and Subplots
fig, (ax1, ax2) = plt.subplots(1,2,
                    figsize=(10,4),
                    sharey=True, dpi=120)

# Create variables
x = [ 2, 4, 6, 8, 10 ]
y = [ 1, 4, 9, 16, 25 ]
z = [ 2, 5, 10, 17, 26 ]

# Plot
ax1.plot(x,y, 'go')  # green dots
ax2.plot(x,z, 'b*')  # blue stars

# Title, X and Y labels, X and Y Lim
ax1.set_title('Scatterplot Green dots')
ax2.set_title('Scatterplot Blue stars')
# set x-axis label
ax1.set_xlabel('X1')
ax2.set_xlabel('X2')
# set y-axis label
ax1.set_ylabel('Y')
ax2.set_ylabel('Y')
# set axis limits/ranges
ax1.set_xlim(0, 12)
ax2.set_xlim(0, 12)
ax1.set_ylim(0, 26)
ax2.set_ylim(0, 26)

# ax2.yaxis.set_ticks_position('none')
plt.tight_layout()
../_images/notebooks_0204_Matplotlib_plotting_23_0.png

Setting “sharey=True” in plt.subplots() shares the Y axis between the two subplots.

Note that the ax1 and ax2 objects, like plt, has equivalent set_title, set_xlabel and set_ylabel functions. In fact, the plt.title() actually calls the current axes set_title() to do the job.

  • plt.xlabel() → ax.set_xlabel()

  • plt.ylabel() → ax.set_ylabel()

  • plt.xlim() → ax.set_xlim()

  • plt.ylim() → ax.set_ylim()

  • plt.title() → ax.set_title()

Alternately, you can set multiple things in one go using the ax.set():

ax1.set(title='Scatterplot Green dots', xlabel='X', ylabel='Y', xlim=(0,6), ylim=(0,12))
ax2.set(title='Scatterplot Blue stars', xlabel='X', ylabel='Y', xlim=(0,6), ylim=(0,12))

Just to complete the example we can now remake the sample multi-panel plot using Matlab syntax rather than object oriented. To do this we need to manually create one subplot at a time (using plt.subplot() or plt.add_subplot()) and immediately call plt.plot() or plt.{anything} to modify that specific subplot (axes). Whatever method you call using plt will be drawn in the current axes.

Always remember: plt.plot() or plt.{anything} will always act on the plot in the current axes, whereas, ax.{anything} will modify the plot inside that specific ax. In the following example we will make a four-panel figure using the object oriented approach. The plot will be of random numbers, and to generate these we will use the numpy package. Note in this example we will also use dictionaries to store plot variables.

[57]:
# import packages, numpy for numerics, random for random numbers, and matplotlib for plotting
from matplotlib import pyplot as plt
import numpy as np
from numpy.random import seed, randint
seed(100)


# Create Figure and Subplots
fig, axes = plt.subplots(2,2,
                figsize=(10,6),
                sharex=True, sharey=True,
                         dpi=120)

# Define the colors and markers to use
colors = {0:'g', 1:'b', 2:'r', 3:'y'}
markers = {0:'o', 1:'x', 2:'*', 3:'p'}

# Plot each axes
for i, ax in enumerate(axes.ravel()):
    ax.plot(sorted(randint(0,10,10)),
            sorted(randint(0,10,10)),
            marker=markers[i],
            color=colors[i])
    ax.set_title('Ax: ' + str(i))
    ax.yaxis.set_ticks_position('none')

#plt.suptitle('Four Subplots in One Figure', verticalalignment='bottom', fontsize=16)
../_images/notebooks_0204_Matplotlib_plotting_25_0.png

Note that we can turn off the y-axis tick marks with

ax.yaxis.set_ticks_position(‘none’)

to turn off the Y-axis ticks. This is another advantage of the object-oriented interface. You can actually get a reference to any specific element of the plot and use its methods to manipulate it.

The plt.suptitle() added a main title at figure level title. plt.title() would have done the same for the current subplot (axes). The verticalalignment=‘bottom’ parameter denotes the hinge point should be at the bottom of the title text, so that the main title is pushed slightly upwards.

The next step will be to modify the figure properties such as labels and tick marks. There are 3 basic things you will probably ever need in matplotlib when it comes to manipulating axis ticks:

  1. How to control the position and tick labels? (using plt.xticks() or ax.setxticks() and ax.setxticklabels())

  2. How to control which axis’s ticks (top/bottom/left/right) should be displayed (using plt.tick_params())

  3. Functional formatting of tick labels

If you are using ax syntax, you can use ax.set_xticks() and ax.set_xticklabels() to set the positions and label texts respectively. If you are using the plt syntax, you can set both the positions as well as the label text in one call using the plt.xticks(). Actually, if you look at the code of plt.xticks() method (by typing plt.xticks in jupyter notebook), it calls ax.set_xticks() and ax.set_xticklabels() to do the job. plt.xticks takes the ticks and labels as required parameters but you can also adjust the label’s fontsize, rotation, ‘horizontalalignment’ and ‘verticalalignment’ of the hinge points on the labels:

[60]:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter


def rad_to_degrees(x,pos):
    return round(x*57.2985,2)

plt.figure(figsize=(12,7), dpi=100)
X = np.linspace(0,2*np.pi,1000)
plt.plot(X,np.sin(X))
plt.plot(X,np.cos(X))

# 1. Adjust x axis Ticks
plt.xticks(ticks=np.arange(0, 440/57.2985,
                           90/57.2985),
           fontsize=12, rotation=30,
           ha='center', va='top')  # 1 radian = 57.2985 degrees

# 2. Tick Parameters
plt.tick_params(axis='both',bottom=True,
                top=True, left=True,
                right=True, direction='in',
                which='major',
                grid_color='blue')

# 3. Format tick labels to convert radians to degrees
formatter = FuncFormatter(rad_to_degrees)
plt.gca().xaxis.set_major_formatter(formatter)

plt.grid(linestyle='--', linewidth=0.5, alpha=0.15)
plt.title('Sine and Cosine Waves\n(Notice the ticks are on all 4 sides pointing inwards, radians converted to degrees in x axis)', fontsize=14)

[60]:
Text(0.5, 1.0, 'Sine and Cosine Waves\n(Notice the ticks are on all 4 sides pointing inwards, radians converted to degrees in x axis)')
../_images/notebooks_0204_Matplotlib_plotting_27_1.png
[ ]: