Visualization Exercises Part 3

This is the third of several exercise pages on visualization using Matplotlib that were prepared for use in the course ITSE 1302 at Austin Community College.

In order to keep our minds organized, let's take inventory of what you have learned so far about visualization using matplotlib with a little help from numpy:

  • How to create and interpret histograms, both standard and cumulative
  • How to draw normal distributions, both standard and cumulative
  • How to overlay normal distributions on histograms, and how to interpret them
  • How to create scatter plots
  • How to draw best fit lines on scatter plots
  • How to create simple bar charts
  • How to create box and whisker plots and how to interpret them
  • How to create a figure and display multiple subplots in the figure, both simple and fancy
  • How to decorate a figure with features such as
    • a facecolor
    • a border
    • a title
    • a tight layout
    • shared horizontal and vertical axes
    • text at arbitrary locations
  • How to decorate the subplots in a figure with features such as
    • a legend
    • x-axis and y-axis tick marks and scales
    • x-axis and y-axis labels
    • x-axis and y-axis scaling such as logarithmic scaling
    • x-axis and y-axis limits
    • a title
    • a grid
    • text at arbitrary locations
  • How to manipulate the spines in a subplot

The remainder of this exercise page will deal with the features that we can apply to the lines in a line plot along with the features that we can apply to the plots containing the lines (tick marks and grids for example). It will also deal with modifications to the tick labels.

Import required libraries

In [1]:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

A basic line plot

The following code displays a basic line plot with two lines and some decorations.

One of the lines is a cosine function. The other line is a damped cosine function produced by multiplying a cosine functon with a function of an exponential decay.

In [2]:
fig,ax0 = plt.subplots(1,1,
                        facecolor='0.75',
                        linewidth=3,
                        edgecolor='Black')

t = np.arange(0, 6.0, 0.1)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),label='Damped cosine')
ax0.plot(t, np.cos(np.pi*t),label='Cosine')

ax0.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.grid(True)

plt.show()

A format string

I told you in an earlier exercise page that the Axes.plot method requires one incoming parameter, which is a list or an array containing the values to be plotted. The method can accept a second argument, which is also a list or array of the same length containing x values that match up with the y values for plotting. Something that I didn't tell you is that the method can accept a third argument that is a format string.

The format string can contain combinations of about two dozen different character that specify the format of the line that will be plotted.

The following code displays a basic line plot with two lines and some decorations. The string 'b+' is passed to one of the calls to the plot method. That string causes the damped cosine to be marked with blue (b) plus characters (+) at each point that is plotted with no lines connecting the points.

The string '-ro' is passed to the other call to the plot method. That string causes the cosine to be plotted as a solid (-) red (r) line with circle (o) markers at each point that is plotted.

In [3]:
fig,ax0 = plt.subplots(1,1,
                        facecolor='0.75',
                        linewidth=3,
                        edgecolor='Black')

t = np.arange(0, 6.0, 0.11)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),'b+',label='Damped cosine')
ax0.plot(t, np.cos(np.pi*t),'-ro',label='Cosine')

ax0.legend(loc='lower left',framealpha=0.3,facecolor='Green')
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.grid(True)

plt.show()

Keyword arguments

In addition to the format string, the Axes.plot method can accept about 40 different keyword arguments, some of which overlap the features provided by the format string.

The following code passes nine keyword arguments to the plot method producing the ugly, partially transparent, stepped cosine with large square yellow markers with blue edges.

In [4]:
fig,ax0 = plt.subplots(1,1,
                        facecolor='0.75',
                        linewidth=3,
                        edgecolor='Black')

t = np.arange(0, 6.0, 0.11)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),label='Damped cosine')
ax0.plot(t, np.cos(np.pi*t),
         label='Cosine',
         alpha=0.5,
         linewidth=5,
         marker='s',
         color='Red',
         drawstyle='steps',
         markeredgecolor='Blue',
         markerfacecolor='Yellow',
         markersize=10)

ax0.legend(loc='lower left',framealpha=0.3,facecolor='Green')
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.grid(True)

plt.show()

Annotating Axes

See Annotating with Arrow for an explanation of the following code that creates text labels for each of the lines in the plot.

In [5]:
fig,ax0 = plt.subplots(1,1,
                    facecolor='0.75',
                    linewidth=3,
                    edgecolor='Black')

t = np.arange(0, 6.0, 0.11)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),label='Damped cosine')
ax0.plot(t, np.cos(np.pi*t),label='Cosine')

ax0.legend(loc='lower left',framealpha=0.3,facecolor='Green')
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.grid(True)

#Now add some annotation to the plot. 
# Could also annotate with LaTeX.
ax0.annotate('Damped cosine',
             xy=[2,0.39], #Coordinates of point
             xytext=(-20, -80), #Coordinates of text in offset pixels.
             textcoords='offset pixels', 
             fontsize=12,
             arrowprops=dict(arrowstyle="->", 
             connectionstyle="arc3,rad=-0.2"))
ax0.annotate('Cosine',
             xy=[3.7,0.5],
             xytext=(-50, +15), 
             textcoords='offset pixels', 
             fontsize=12,
             arrowprops=dict(arrowstyle="->", 
             connectionstyle="arc3,rad=.2"))

plt.show()

Plot multiple lines with one call

The Axes.plot method will take an arbitrary number of arguments and plot multiple lines with a single call to the method.

The following code calls the plot method once passing two sets of three positional arguments causing two lines to be plotted. There is a potential downside to this approach involving keyword arguments. You must pass all positional arguments before you can pass any keyword arguments. Each keyword argument that is passed following the positional arguments applies to all of the lines being plotted. If I were to pass a "label=" keyword argument in this case, that would produce an incorrect result for the legend because both lines would be assigned the same label.

There is a workaround, however. The plot method returns a list of matplotlib.lines.Line2D objects, one for each line being plotted. If you capture the returned values, you can call methods such as set_label('...') on the returned values and have the same effect as if you had achieved that effect by passing keyword arguments.

The following code calls the set_label method on each of the returned values producing a correct version of the legend.

In [6]:
fig,ax0 = plt.subplots(1,1,
                    facecolor='0.75',
                    linewidth=3,
                    edgecolor='Black')

t = np.arange(0, 6.0, 0.11)
line0,line1 = ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),'-.',
                       t, np.cos(np.pi*t),'--o')
line0.set_label('Damped cosine')
line1.set_label('Cosine')

ax0.legend(loc='lower left',framealpha=0.3,facecolor='Green')
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.grid(True)

plt.show()

Major and minor tick marks

Up to this point, we have accepted the default for tick marks, which is to show only the major tick marks. However, it is often useful to have at least two and sometimes three levels of tick marks.

As I understand it, matplotlib directly supports only two levels of tick marks, major and minor. However, if you need three levels, this web page may show you how to do that.

An Internet search will reveal several ways to create major and minor tick marks. The following code will show you one way to do that. I will leave it as an exercise for the student to dig through the documentation to understand how this works.

The code also calls the Axes.tick_params method to change some of the attributes of the tick marks. The web page listed above shows a number of attributes that can be controlled, including attributes of the text labels associated with the tick marks.

In [7]:
fig,ax0 = plt.subplots(1,1)

t = np.arange(0, 6.0, 0.11)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),'-.',
                       t, np.cos(np.pi*t),'--o')

ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')

ax0.xaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(0.1))

ax0.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax0.yaxis.set_minor_locator(ticker.MultipleLocator(0.1))

ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',color='red',
                length=10,labelcolor='blue',width=2)

ax0.grid(True)

plt.show()

Major and minor grid lines

We have been calling the the Axes.grid method since the beginning of the course to place grid lines on our plots. However, up to this point, we have accepted the default for grid lines, which is to show lines only for the major tick marks. It is often useful to also show grid lines on the minor tick marks as well.

The following code uses keyword arguments in two calls to the grid method to cause both major and minor gride lines to be shown and to control some of the attributes of those lines.

In [8]:
fig,ax0 = plt.subplots(1,1)

t = np.arange(0, 6.0, 0.11)
ax0.plot(t, np.exp(-t/2) * np.cos(np.pi*t),'-.',
                       t, np.cos(np.pi*t),'--o')

ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')

ax0.xaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(0.1))

ax0.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax0.yaxis.set_minor_locator(ticker.MultipleLocator(0.1))

ax0.tick_params(axis='both',which='minor',length=5)
ax0.tick_params(axis='both',which='major',length=10,
                labelcolor='blue',width=2)

ax0.grid(b=True, which='major', color='cyan')
ax0.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)

plt.show()

Log and linear plots

The following code displays an exponential curve in both a semi-log and a linear format making use of major and minor tick marks and grid lines. As I mentioned earlier, an exponential becomes a straight line when plotted in a semelog format.

In [9]:
fig,(ax0,ax1) = plt.subplots(2,1,figsize=(6,6))

t = np.arange(0, 20.0, 0.1)
#================
ax0.plot(t, np.exp(-t/2))

ax0.set_yscale('log')
ax0.set_ylim(0.0001,1)
ax0.set_xlim(0,20)

ax0.set_ylabel('Log plot')
ax0.set_xlabel('x')

ax0.xaxis.set_major_locator(ticker.MultipleLocator(2))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(.5))

ax0.grid(b=True, which='major', color='cyan')
ax0.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)
#================

ax1.plot(t, np.exp(-t/2))

ax1.set_xlim(0,20)

ax1.set_ylabel('Linear plot')
ax1.set_xlabel('x')

ax1.xaxis.set_major_locator(ticker.MultipleLocator(2))
ax1.xaxis.set_minor_locator(ticker.MultipleLocator(.5))

ax1.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax1.yaxis.set_minor_locator(ticker.MultipleLocator(.1))

ax1.grid(b=True, which='major', color='cyan')
ax1.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)
fig.tight_layout()
plt.show()

Plotting in reverse order

If you reverse the order of the arguments to the set_xlim and set_ylim methods, that will reverse your plots as illustrated by the following code.

The following code displays an exponential curve in both a semi-log and a linear format making use of major and minor tick marks and grid lines. However, the order of the arguments to the set_xlim and set_ylim methods causing the plots to appear upside down and backwards as compared to the previous example.

In [10]:
fig,(ax0,ax1) = plt.subplots(2,1,figsize=(6,6))

t = np.arange(0, 20.0, 0.1)
#================
ax0.plot(t, np.exp(-t/2))

ax0.set_yscale('log')
ax0.set_ylim(1,0.0001)
ax0.set_xlim(20,0)

ax0.set_ylabel('Log plot')
ax0.set_xlabel('x')

ax0.xaxis.set_major_locator(ticker.MultipleLocator(2))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(.5))

ax0.grid(b=True, which='major', color='cyan')
ax0.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)
#================

ax1.plot(t, np.exp(-t/2))

ax1.set_xlim(20,0)
ax1.set_ylim(1,0)

ax1.set_ylabel('Linear plot')
ax1.set_xlabel('x')

ax1.xaxis.set_major_locator(ticker.MultipleLocator(2))
ax1.xaxis.set_minor_locator(ticker.MultipleLocator(.5))

ax1.yaxis.set_major_locator(ticker.MultipleLocator(0.5))
ax1.yaxis.set_minor_locator(ticker.MultipleLocator(.1))

ax1.grid(b=True, which='major', color='cyan')
ax1.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)
fig.tight_layout()
plt.show()

Setting tick labels

Suppose that you aren't happy with the default vertical tick labels for the log plot in the previous exercise and you want to change them. The following code does that.

I am unable to explain why you need to put an empty string at the beginning of the list of labels.

In [11]:
fig,ax0 = plt.subplots(1,1,figsize=(6,6))

t = np.arange(0, 20.0, 0.1)
#================
ax0.plot(t, np.exp(-t/2))

ax0.set_yscale('log')

ax0.set_ylim(0.0001,1)
ax0.set_xlim(0,20)

ax0.set_ylabel('Log plot')
ax0.set_xlabel('x')

ax0.xaxis.set_major_locator(ticker.MultipleLocator(2))
ax0.xaxis.set_minor_locator(ticker.MultipleLocator(.5))

vLabels = ['','0.0001','0.001','0.01','0.1','1']
ax0.set_yticklabels(vLabels)

ax0.grid(b=True, which='major', color='cyan')
ax0.grid(b=True, which='minor', color='blue',
         linestyle='dotted',alpha=0.6)

fig.tight_layout()
plt.show()

--To be continued in the tutorial titled Visualization Exercises Part 4--

Author: Prof. Richard G. Baldwin, Austin Community College, Austin, TX

File: VisualizationPart03.ipynb

Revised: 04/22/18

Copyright 2018 Richard G. Baldwin

-end-