Post Exam Python-Matplotlib tutorial

matplotlib is an excellent 2D (and 3D) python library that can be used to produce publication quality output from your data. The website https://matplotlib.org/ provides a complete resource for how to use matplotlib for your work. In particular if you click on an example plot in the gallery, https://matplotlib.org/gallery/index.html, the browser will display the code required to produce the plot. It is quite difficult to ask google "I would like my plot to look like this, and have these features, how do I do it?", however it is easy to browse through the gallery until you see the feature that you are interested in.

Unlike software like Excel in matplotlib you write code to determine the appearance of all aspects of your graph, you can recycle this code to easily create reproducable, consistent publication quality representations of your scientific data

Preparing the notebook for using matplotlib and numpy.

In [29]:
%matplotlib inline 
# this line is required for the plots to appear in the Jupyter cells, rather than launching the matplotlib GUI

import matplotlib

import numpy as np

import matplotlib.pyplot as plt

# Let printing work the same in Python 2 and 3
from __future__ import print_function

# notice two underscores _ either side of future

Create some data for plotting examples.

In [30]:
x=np.linspace(0,2*np.pi, 100)
y=np.cos(x)

Generate the basic matplotlib 2D plot, figure() creates the space into which the plot will be added.

In [31]:
plt.figure()
plt.plot(x,y)
Out[31]:
[<matplotlib.lines.Line2D at 0x22c5ec95668>]
In [32]:
plt.figure()
plt.plot(x,y,'r+') # change the line style to red plusses highlights that we are dealing with a discrete set of points
Out[32]:
[<matplotlib.lines.Line2D at 0x22c5ecf7a58>]

Within matplotlib.pyplot there are too many functions to describe here:

In [33]:
print(dir(plt)) # matplotlib.pyplot is an extensive package
['Annotation', 'Arrow', 'Artist', 'AutoLocator', 'Axes', 'Button', 'Circle', 'Figure', 'FigureCanvasBase', 'FixedFormatter', 'FixedLocator', 'FormatStrFormatter', 'Formatter', 'FuncFormatter', 'GridSpec', 'IndexLocator', 'Line2D', 'LinearLocator', 'Locator', 'LogFormatter', 'LogFormatterExponent', 'LogFormatterMathtext', 'LogLocator', 'MaxNLocator', 'MultipleLocator', 'Normalize', 'NullFormatter', 'NullLocator', 'PolarAxes', 'Polygon', 'Rectangle', 'ScalarFormatter', 'Slider', 'Subplot', 'SubplotTool', 'Text', 'TickHelper', 'Widget', '_INSTALL_FIG_OBSERVER', '_IP_REGISTERED', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '_auto_draw_if_interactive', '_autogen_docstring', '_backend_mod', '_backend_selection', '_hold_msg', '_imread', '_imsave', '_interactive_bk', '_pylab_helpers', '_setp', '_setup_pyplot_info_docstrings', '_show', '_string_to_bool', 'absolute_import', 'acorr', 'angle_spectrum', 'annotate', 'arrow', 'autoscale', 'autumn', 'axes', 'axhline', 'axhspan', 'axis', 'axvline', 'axvspan', 'bar', 'barbs', 'barh', 'bone', 'box', 'boxplot', 'broken_barh', 'cla', 'clabel', 'clf', 'clim', 'close', 'cm', 'cohere', 'colorbar', 'colormaps', 'colors', 'connect', 'contour', 'contourf', 'cool', 'copper', 'csd', 'cycler', 'dedent', 'delaxes', 'deprecated', 'disconnect', 'division', 'docstring', 'draw', 'draw_all', 'draw_if_interactive', 'errorbar', 'eventplot', 'figaspect', 'figimage', 'figlegend', 'fignum_exists', 'figtext', 'figure', 'fill', 'fill_between', 'fill_betweenx', 'findobj', 'flag', 'gca', 'gcf', 'gci', 'get', 'get_backend', 'get_cmap', 'get_current_fig_manager', 'get_figlabels', 'get_fignums', 'get_plot_commands', 'get_scale_docs', 'get_scale_names', 'getp', 'ginput', 'gray', 'grid', 'hexbin', 'hist', 'hist2d', 'hlines', 'hold', 'hot', 'hsv', 'imread', 'imsave', 'imshow', 'inferno', 'install_repl_displayhook', 'interactive', 'ioff', 'ion', 'is_numlike', 'ishold', 'isinteractive', 'jet', 'legend', 'locator_params', 'loglog', 'magma', 'magnitude_spectrum', 'margins', 'matplotlib', 'matshow', 'minorticks_off', 'minorticks_on', 'mlab', 'new_figure_manager', 'nipy_spectral', 'np', 'over', 'pause', 'pcolor', 'pcolormesh', 'phase_spectrum', 'pie', 'pink', 'plasma', 'plot', 'plot_date', 'plotfile', 'plotting', 'polar', 'print_function', 'prism', 'psd', 'pylab_setup', 'quiver', 'quiverkey', 'rc', 'rcParams', 'rcParamsDefault', 'rc_context', 'rcdefaults', 'register_cmap', 'rgrids', 'savefig', 'sca', 'scatter', 'sci', 'semilogx', 'semilogy', 'set_cmap', 'setp', 'show', 'silent_list', 'six', 'specgram', 'spectral', 'spring', 'spy', 'stackplot', 'stem', 'step', 'streamplot', 'style', 'subplot', 'subplot2grid', 'subplot_tool', 'subplots', 'subplots_adjust', 'summer', 'suptitle', 'switch_backend', 'sys', 'table', 'text', 'thetagrids', 'tick_params', 'ticklabel_format', 'tight_layout', 'time', 'title', 'tricontour', 'tricontourf', 'tripcolor', 'triplot', 'twinx', 'twiny', 'types', 'unicode_literals', 'uninstall_repl_displayhook', 'violinplot', 'viridis', 'vlines', 'waitforbuttonpress', 'warnings', 'winter', 'xcorr', 'xkcd', 'xlabel', 'xlim', 'xscale', 'xticks', 'ylabel', 'ylim', 'yscale', 'yticks']
In [34]:
help(plt.plot) # the plot docstring gives a detailed set of instructions on the usasge
Help on function plot in module matplotlib.pyplot:

plot(*args, **kwargs)
    Plot lines and/or markers to the
    :class:`~matplotlib.axes.Axes`.  *args* is a variable length
    argument, allowing for multiple *x*, *y* pairs with an
    optional format string.  For example, each of the following is
    legal::
    
        plot(x, y)        # plot x and y using default line style and color
        plot(x, y, 'bo')  # plot x and y using blue circle markers
        plot(y)           # plot y using x as index array 0..N-1
        plot(y, 'r+')     # ditto, but with red plusses
    
    If *x* and/or *y* is 2-dimensional, then the corresponding columns
    will be plotted.
    
    If used with labeled data, make sure that the color spec is not
    included as an element in data, as otherwise the last case
    ``plot("v","r", data={"v":..., "r":...)``
    can be interpreted as the first case which would do ``plot(v, r)``
    using the default line style and color.
    
    If not used with labeled data (i.e., without a data argument),
    an arbitrary number of *x*, *y*, *fmt* groups can be specified, as in::
    
        a.plot(x1, y1, 'g^', x2, y2, 'g-')
    
    Return value is a list of lines that were added.
    
    By default, each line is assigned a different style specified by a
    'style cycle'.  To change this behavior, you can edit the
    axes.prop_cycle rcParam.
    
    The following format string characters are accepted to control
    the line style or marker:
    
    ================    ===============================
    character           description
    ================    ===============================
    ``'-'``             solid line style
    ``'--'``            dashed line style
    ``'-.'``            dash-dot line style
    ``':'``             dotted line style
    ``'.'``             point marker
    ``','``             pixel marker
    ``'o'``             circle marker
    ``'v'``             triangle_down marker
    ``'^'``             triangle_up marker
    ``'<'``             triangle_left marker
    ``'>'``             triangle_right marker
    ``'1'``             tri_down marker
    ``'2'``             tri_up marker
    ``'3'``             tri_left marker
    ``'4'``             tri_right marker
    ``'s'``             square marker
    ``'p'``             pentagon marker
    ``'*'``             star marker
    ``'h'``             hexagon1 marker
    ``'H'``             hexagon2 marker
    ``'+'``             plus marker
    ``'x'``             x marker
    ``'D'``             diamond marker
    ``'d'``             thin_diamond marker
    ``'|'``             vline marker
    ``'_'``             hline marker
    ================    ===============================
    
    
    The following color abbreviations are supported:
    
    ==========  ========
    character   color
    ==========  ========
    'b'         blue
    'g'         green
    'r'         red
    'c'         cyan
    'm'         magenta
    'y'         yellow
    'k'         black
    'w'         white
    ==========  ========
    
    In addition, you can specify colors in many weird and
    wonderful ways, including full names (``'green'``), hex
    strings (``'#008000'``), RGB or RGBA tuples (``(0,1,0,1)``) or
    grayscale intensities as a string (``'0.8'``).  Of these, the
    string specifications can be used in place of a ``fmt`` group,
    but the tuple forms can be used only as ``kwargs``.
    
    Line styles and colors are combined in a single format string, as in
    ``'bo'`` for blue circles.
    
    The *kwargs* can be used to set line properties (any property that has
    a ``set_*`` method).  You can use this to set a line label (for auto
    legends), linewidth, anitialising, marker face color, etc.  Here is an
    example::
    
        plot([1,2,3], [1,2,3], 'go-', label='line 1', linewidth=2)
        plot([1,2,3], [1,4,9], 'rs',  label='line 2')
        axis([0, 4, 0, 10])
        legend()
    
    If you make multiple lines with one plot command, the kwargs
    apply to all those lines, e.g.::
    
        plot(x1, y1, x2, y2, antialiased=False)
    
    Neither line will be antialiased.
    
    You do not need to use format strings, which are just
    abbreviations.  All of the line properties can be controlled
    by keyword arguments.  For example, you can set the color,
    marker, linestyle, and markercolor with::
    
        plot(x, y, color='green', linestyle='dashed', marker='o',
             markerfacecolor='blue', markersize=12).
    
    See :class:`~matplotlib.lines.Line2D` for details.
    
    The kwargs are :class:`~matplotlib.lines.Line2D` properties:
    
      agg_filter: a filter function, which takes a (m, n, 3) float array and a dpi value, and returns a (m, n, 3) array 
      alpha: float (0.0 transparent through 1.0 opaque) 
      animated: bool 
      antialiased or aa: [True | False] 
      clip_box: a `~.Bbox` instance 
      clip_on: bool 
      clip_path: [(`~matplotlib.path.Path`, `~.Transform`) | `~.Patch` | None] 
      color or c: any matplotlib color 
      contains: a callable function 
      dash_capstyle: ['butt' | 'round' | 'projecting'] 
      dash_joinstyle: ['miter' | 'round' | 'bevel'] 
      dashes: sequence of on/off ink in points 
      drawstyle: ['default' | 'steps' | 'steps-pre' | 'steps-mid' | 'steps-post'] 
      figure: a `~.Figure` instance 
      fillstyle: ['full' | 'left' | 'right' | 'bottom' | 'top' | 'none'] 
      gid: an id string 
      label: object 
      linestyle or ls: ['solid' | 'dashed', 'dashdot', 'dotted' | (offset, on-off-dash-seq) | ``'-'`` | ``'--'`` | ``'-.'`` | ``':'`` | ``'None'`` | ``' '`` | ``''``]
      linewidth or lw: float value in points 
      marker: :mod:`A valid marker style <matplotlib.markers>`
      markeredgecolor or mec: any matplotlib color 
      markeredgewidth or mew: float value in points 
      markerfacecolor or mfc: any matplotlib color 
      markerfacecoloralt or mfcalt: any matplotlib color 
      markersize or ms: float 
      markevery: [None | int | length-2 tuple of int | slice | list/array of int | float | length-2 tuple of float]
      path_effects: `~.AbstractPathEffect` 
      picker: float distance in points or callable pick function ``fn(artist, event)`` 
      pickradius: float distance in points
      rasterized: bool or None 
      sketch_params: (scale: float, length: float, randomness: float) 
      snap: bool or None 
      solid_capstyle: ['butt' | 'round' |  'projecting'] 
      solid_joinstyle: ['miter' | 'round' | 'bevel'] 
      transform: a :class:`matplotlib.transforms.Transform` instance 
      url: a url string 
      visible: bool 
      xdata: 1D array 
      ydata: 1D array 
      zorder: float 
    
    kwargs *scalex* and *scaley*, if defined, are passed on to
    :meth:`~matplotlib.axes.Axes.autoscale_view` to determine
    whether the *x* and *y* axes are autoscaled; the default is
    *True*.
    
    .. note::
        In addition to the above described arguments, this function can take a
        **data** keyword argument. If such a **data** argument is given, the
        following arguments are replaced by **data[<arg>]**:
    
        * All arguments with the following names: 'x', 'y'.

plot(*args, **kwargs) refers to the functions arguments and keyword arguments. The order of the arguments in a python function determines how the argument is passed into the function i.e plot(x,y) will have x as the x-axis, plot(y,x) will have y as the x-axis. The kwargs can come in any order as they are recognised by the keyword i.e. label='my experimental data'.

Returning to our plot:

The following code begins to show how much control you can have over the appearance of the plot, in particular note that LaTex math symbols have been used to label the xticks, and the ticks have been moved to user defined positions.

In [44]:
z=np.sin(x)
plt.figure()
plt.plot(x,y, label=r'$cos(x)$')
plt.plot(x,z, label=r'$sin(x)$')# I have not specified the colour, but matplotlib will increment 
#through a range as new plots are addded.
plt.legend(loc=1) # places the legend (created from the plot labels) in the upper-right
plt.title('My First Plot')
plt.xlabel(r'$\theta$') # the r tells python to read all characters, otherwise it would not read the \ 
plt.ylabel('y')
xmin,xmax=plt.xlim() # returns the current limits
plt.xlim(0,xmax*1.3) # sets new limits, makes some space on the right for the legend
plt.xticks((0,np.pi/2,np.pi,3*np.pi/2,2*np.pi),('0','$\pi/2$','$\pi$','$3\pi/2$','$2\pi$')) # Move the tick labels and use 
#Latex commands for the labels
plt.tight_layout() #Ensures nothing overlaps
plt.show() # this is not needed in the notebook but is required from your code.

Loading data from a file to an array, np.loadtxt('fname',...)

Data downloaded from https://climate.nasa.gov/system/internal_resources/details/original/647_Global_Temperature_Data_File.txt

In [45]:
climate=np.loadtxt('https://climate.nasa.gov/system/internal_resources/details/original/647_Global_Temperature_Data_File.txt') # data downloaded from the NASA climate change website.
In [46]:
np.shape(climate) # I want the first two columns in this array 
Out[46]:
(138, 3)
In [47]:
year,tempchange=climate.transpose()[0],climate.transpose()[1]
# by sepearting the variables with a comma we can assign both in a single line
In [41]:
plt.figure()
plt.scatter(year,tempchange, label='NASA data')
plt.title('Nasa Climate Change data since 1880')
plt.xlabel('Year')
plt.ylabel('$\delta T$ from the 1951-1980 mean [C]')
plt.legend()
plt.show()

As a quick look at how simple it can be to analyse your data with python the following histogram can be generated with a single additional line of code.

In [42]:
plt.hist(tempchange)
Out[42]:
(array([13., 25., 34., 22., 12.,  7., 10., 10.,  2.,  3.]),
 array([-0.49 , -0.342, -0.194, -0.046,  0.102,  0.25 ,  0.398,  0.546,
         0.694,  0.842,  0.99 ]),
 <a list of 10 Patch objects>)

This notebook forms the basic introduction to plotting in python with matplotlib, next term we will expand on this with further topics:

  • Adding multiple plots to a figure (subplots)
  • Exploring different types of graph
    • Imshow, Axes3D, plotting histograms
  • Animate plots