This is the second of several exercise pages on visualization using Matplotlib that were prepared for use in the course ITSE 1302 at Austin Community College.
Matplotlib can be used to create a large variety of types (line plots, histograms, etc.) and features (legends for example) of plots as you will see if you visit the thumbnail gallery. The code for many of the features can be carried forward from one type of plot to another so that if you know how to create the feature for one type of plot, you also know how to create the feature for many different types of plots.
Perhaps the simplest type of plot is the line plot, which we will use as the vehicle for the application of features. This exercise page will provide brief explanations and demonstrations of the application of a variety of features using line plots as the vehicle.
import numpy as np
import matplotlib.pyplot as plt
import math
We will begin with a discussion of Axes.plot. We have been using this method since the beginning but have yet to discuss it in detail.
As shown in the top subplot below, this method requires, as a minimum, a single list or arry containing the data values to be plotted. If only one list is provided, the horizontal scale defaults to the index values of the list, which is 0 through 6 in this case.
Optionally a second list may be provided, as the first argument, containing a sequence of float values that specify the horizontal scale, as shown in the second subplot below. The two lists must contain the same number of elements. The method considers corresponding pairs of values between the two lists as x-y values to be plotted.
Other optional arguments can be passed to the method. I will discuss some of those possibiilities later when I discuss the format of the line (solid, dashed, etc.)
fig,(ax0,ax1) = plt.subplots(2,1)
ax0.plot([0,1,2,3,2,1,0])
ax1.plot([2,4,6,8,10,12,14],[0,1,2,3,2,1,0])
plt.show()
The standard math library and the numpy library each provide a function that returns the cosine of an angle expressed in radians. However, the input requirements of the two functions are different.
The math.cos function expects to receive a single angle in radians and returns the consine of the angle.
The numpy.cos function expects to receive a list or an array of angles expressed in radians and returns an array containing the cosine values for the corresponding angles.
The following code sample shows how each of the cos methods can be used to accomplish the same output plots.
An array named t is created containing 120 incremental values ranging from 0 to 5.95 in steps of 0.05.
The top subplot uses list comprehension and the math.cos function to create a list containing the cosine values for 120 angles based on the contents of the array named t. This list is passed as the second argument to the plot method for plotting against the values in the array named t.
The bottom subplot calls the np.cos function as the second argument to the plot method. This call is replaced by the array that is returned by the function. The contents of the array are plotted against the values in the array named t.
fig,(ax0,ax1) = plt.subplots(2,1)
t = np.arange(0, 6.0, 0.05)
ax0.plot(t,[math.cos(2*math.pi*x) for x in t])
ax1.plot(t,np.cos(2*np.pi*t))
plt.show()
We will start with the basic line plot shown below and progressively enhance it.
In addition to the numpy.cos function,this code calls the numpy.exp function. This function also receives an array of input values and returns an array of corresponding output values.
Note that this code does not provide an array to control the x-axis scale. As a result, the x-axis scale is the index values from the array named t, which in this case is the set of values from 0 through 119.
Note that there are two calls to ax3.plot, resulting in two different lines being plotted in that subplot.
fig,axes = plt.subplots(2,2)
ax0,ax1,ax2,ax3 = axes.flatten()
t = np.arange(0, 6.0, 0.05)
ax0.plot(np.cos(1*np.pi*t))
ax1.plot(np.exp(-t))
ax2.plot(np.exp(-t) * np.cos(3*np.pi*t))
ax3.plot(np.exp(-t) * np.cos(4*np.pi*t))
ax3.plot(np.cos(4*np.pi*t))
plt.show()
The following code provides the t array as the first argument to each call to the plot function, thereby causing the x-axis scale to reflect the values contained in that array. In other words, this code causes the x-axis scale to be reflective of the arguments to the functions whose outputs are being plotted instead of simply being an index to an array.
fig,axes = plt.subplots(2,2)
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t,np.cos(1*np.pi*t))
ax1.plot(t, np.exp(-t) )
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t))
ax3.plot(t, np.cos(4*np.pi*t))
plt.show()
Note that the output image shown above doesn't display the figure. However, it is possible to display the figure and to apply cosmetic properties if we choose to do so.
The call to the subplots method in the following code creates and returns a reference to a Figure object with two rows and two columns of axes. By default, the Figure object would be invisible as in the above example. However, we can pass keyword parameters to the subplots method that are in turn used to set properties on the figure. Some of the available keyword parameters are shown below.
figsize: This parameter can be used to set the width and height of the figure in inches.
facecolor: Specifies the color of the figure, which can be any allowable color. There are several different ways to specify a color. For example, as illustrated below, gray shades can be given as a string encoding a float in the 0-1 range.
linewidth: Specifies the width in pixels of a line that forms a border around the figure. The default width is 0 causing the border to be invisible.
edgecolor: Specifies the color of the line that forms the border around the figure.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=8,
edgecolor='Blue')
plt.show()
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax1.plot(t, np.exp(-t) )
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t))
ax3.plot(t, np.cos(4*np.pi*t))
plt.show()
The following code sets the sharex and sharey parameters for the figure to True causing the axes to be shared. This results in some obvious differences in the manner in which the subplots are dislayed. (Note that grid lines were also displayed to make the differences more obvious.)
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red',
sharex=True,
sharey=True)
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax1.plot(t, np.exp(-t) )
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t))
ax3.plot(t, np.cos(4*np.pi*t))
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
plt.show()
The following code places a legend in the bottom-right subplot. There are several ways to create legends. This code creates a label for each of the lines being plotted. Then it calls the legend method that causes those labels to be turned into a legend and placed on the subplot.
The location of the legend can be specified with a keyword parameter in the call to the legend method. In this case, the legend was specified to be located in the 'upper right'.
There are many other keyword parameters, such as framealpha and facecolor, that can be passed to the legend method to control the appearance of the legend. This code sets those two values to cause the background of the box containing the legend text to be partially transparent green -- not pretty, just for illustration.
Then the code calls the set_title method to place a title above the lower-right subplot. There are several keyword parameters that can be passed to control the appearance of the title. This code simply accepts the default of a centered title in the default font.
Once we start decorating the figure with titles, etc., we need to call tight_layout to automatically adjust subplot parameters so that the subplots and the decorations fit properly without overlap in the figure area.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax1.plot(t, np.exp(-t) )
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.tight_layout()
plt.show()
The following code creates and displays labels for the X and Y axes for each of the subplots. Note that without the call to the tight_layout method, this would create an overlapping mess.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax1.plot(t, np.exp(-t) )
ax1.set_ylabel('This is a ylabel')
ax1.set_xlabel('This is an xlabel')
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax2.set_ylabel('This is a ylabel')
ax2.set_xlabel('This is an xlabel')
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax3.set_ylabel('This is a ylabel')
ax3.set_xlabel('This is an xlabel')
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.tight_layout()
plt.show()
The following code adds a title to each of the subplots and then adds a title to the figure. To improve the cosmetics and move the figure title to a location above the top subplot titles, the code passes a parameter to the tight_layout method that provides "padding between the figure edge and the edges of subplots, as a fraction of the font-size.".
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.set_title('A cosine')
ax1.plot(t, np.exp(-t) )
ax1.set_ylabel('This is a ylabel')
ax1.set_xlabel('This is an xlabel')
ax1.set_title('An exponential')
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax2.set_ylabel('This is a ylabel')
ax2.set_xlabel('This is an xlabel')
ax2.set_title('A damped cosine')
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax3.set_ylabel('This is a ylabel')
ax3.set_xlabel('This is an xlabel')
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.suptitle('A figure title')
fig.tight_layout(pad=2)
plt.show()
Once you have established a relationship between the x-axis scale and the values being plotted, you can call the Axes.set_xlim method to control the visible range of the x-axis scale.
The following code specifies that only that portion of the curve that lies between 0 and 1.0 in the lower-left subplot should be visible in the available space. This has the impact of causing that portion of the curve to the right of 1.0 to be out of the visible range for the plot.
The code also sets the visible range for the lower-right subplot to be from -2 to 8. This results in a blank area on both ends of the curve because the curve is specified only for the interval from 0 to 6.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.set_title('A cosine')
ax1.plot(t, np.exp(-t) )
ax1.set_ylabel('This is a ylabel')
ax1.set_xlabel('This is an xlabel')
ax1.set_title('An exponential')
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax2.set_ylabel('This is a ylabel')
ax2.set_xlabel('This is an xlabel')
ax2.set_title('A damped cosine')
ax2.set_xlim(0,1)
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax3.set_ylabel('This is a ylabel')
ax3.set_xlabel('This is an xlabel')
ax3.set_xlim(-2,8)
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.suptitle('A figure title')
fig.tight_layout(pad=2)
plt.show()
Normally the y-axis scale is set automatically to fit the values of the data being plotted. However, you can call the Axes.set_ylim method to control the visible range of the y-axis scale.
The following code specifies that only that portion of the curve having values that lie between -0.2 and +0.2 in the two bottom subplots should be visible in the available space. This has the impact of causing that portion of the curves below -0.2 and above 0.2 to be out of the visible range for the plots.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.set_title('A cosine')
ax1.plot(t, np.exp(-t) )
ax1.set_ylabel('This is a ylabel')
ax1.set_xlabel('This is an xlabel')
ax1.set_title('An exponential')
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax2.set_ylabel('This is a ylabel')
ax2.set_xlabel('This is an xlabel')
ax2.set_title('A damped cosine')
ax2.set_ylim(-0.2,0.2)
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax3.set_ylabel('This is a ylabel')
ax3.set_xlabel('This is an xlabel')
ax3.set_ylim(-0.2,0.2)
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.suptitle('A figure title')
fig.tight_layout(pad=2)
plt.show()
Normally the x-axis and y-axis scales are linear. However, you can call the Axes.set_xscale and Axes.set_yscale methods to cause the data to be plotted on nonlinear scales, such as a log scale.
The following code causes the upper-right subplot to be plotted on a log vertical scale. This is equivalent to manually plotting the curve on semi-log graph paper if you know what that means.
This has the impact of causing the exponenial curve to appear as a straight line on the plot.
fig,axes = plt.subplots(2,2,
figsize=(7,5),
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(0, 6.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.plot(t, np.cos(1*np.pi*t))
ax0.set_ylabel('This is a ylabel')
ax0.set_xlabel('This is an xlabel')
ax0.set_title('A cosine')
ax1.plot(t, np.exp(-t) )
ax1.set_ylabel('This is a ylabel')
ax1.set_xlabel('This is an xlabel')
ax1.set_title('An exponential')
ax1.set_yscale('log')
ax2.plot(t, np.exp(-t) * np.cos(3*np.pi*t))
ax2.set_ylabel('This is a ylabel')
ax2.set_xlabel('This is an xlabel')
ax2.set_title('A damped cosine')
ax3.plot(t, np.exp(-t) * np.cos(4*np.pi*t),label='Damped cosine')
ax3.plot(t, np.cos(4*np.pi*t),label='Cosine')
ax3.legend(loc='upper right',framealpha=0.3,facecolor='Green')
ax3.set_title('Subplot with a legend')
ax3.set_ylabel('This is a ylabel')
ax3.set_xlabel('This is an xlabel')
ax0.grid(True)
ax1.grid(True)
ax2.grid(True)
ax3.grid(True)
fig.suptitle('A figure title')
fig.tight_layout(pad=2)
plt.show()
According to the documentation matplotlib.spines are the lines connecting the axis tick marks and noting the boundaries of the data area. They can be placed at arbitrary positions. They can also be made invisible by setting their color to none.
The following code uses the spines to form axes that intersect at the origin of each plot. The code also uses LaTeX to place labels inside of each plot describing the curve, and finally the code places some text at the lower-left of the figure.
fig,axes = plt.subplots(2,2,
facecolor='0.75',
linewidth=10,
edgecolor='Red')
t = np.arange(-3.0, 3.0, 0.05)
ax0,ax1,ax2,ax3 = axes.flatten()
ax0.spines['left'].set_position('center')
ax0.spines['right'].set_color('none')
ax0.spines['bottom'].set_position('center')
ax0.spines['top'].set_color('none')
ax0.text(-1.7,0.7,'$cosine$')
ax0.plot(t,np.cos(1*np.pi*t))
ax1.spines['left'].set_position('center')
ax1.spines['right'].set_color('none')
ax1.spines['bottom'].set_position('center')
ax1.spines['top'].set_color('none')
ax1.text(-3,0.5,'$sine$')
ax1.plot(t,np.sin(1*np.pi*t))
ax2.spines['left'].set_position('center')
ax2.spines['right'].set_color('none')
ax2.spines['top'].set_color('none')
ax2.set_ylim(0,9)
ax2.text(-3,1.0,'$y=x^2$')
ax2.plot(t, [x**2 for x in t])
ax3.spines['left'].set_position('center')
ax3.spines['right'].set_color('none')
ax3.spines['bottom'].set_position('center')
ax3.spines['top'].set_color('none')
ax3.set_ylim(-27,27)
ax3.text(-3,5,'$y=x^3$')
ax3.plot(t, [x**3 for x in t])
plt.figtext(0.1,0.02,'This is text rendered on the figure.')
plt.show()
Author: Prof. Richard G. Baldwin, Austin Community College, Austin, TX
File: VisualizationPart02.ipynb
Revised: 04/22/18
Copyright 2018 Richard G. Baldwin
-end-