﻿ COMP 200 Resources

# COMP 200 Resources

## Make a Line Plot

The mathplotlib.pyplot module has many functions that are very useful for making plots.   The functions are part of the mathplotlib library that is included with an EPD installation.  Here's a few commonly used functions:

• plot  -- takes x and y data lists and creates a plot as per a "format string" that controls colors, line type and symbol type.
• legend - Creates a legend corresponding to the given plots.
• xlabel and ylabel -- create descriptions of the x and y axes respectively.
• title -- creates a title for the plot
• show -- shows the plot in a pop-up dialog window.

For specific information on how to use these and other plotting functions please see the matplotlib documentation.

Example:

This example plots two sets of y-data against a common set of x-data values.

```# get the plotting library functions
import matplotlib.pyplot as plt  #import  plot, legend, xlabel, ylabel, title, show

xData = []   # Needs to be filled with x-data values

yData1 = []   # first y-data list
yData2 = []   # second y-data list

# put code here to fill yData lists with values corresponding to xData values.

plot1 = plt.plot(xData,  yData1, 'o-' )  # circles connected by solid line
plot2 = plt.plot(xData,  yData2, 's:' )  # squares connected by dotted line
plt.legend( (plot1, plot2), ('y-data 1', 'y-data 2'), 'best')   # legend box placed in "best" location
plt.xlabel('x description')   # description for the x-axis
plt.ylabel('y description')   # description for the y--axis
plt.title('Plot title')  # top title of the plot
plt.show()   # show the plot in a pop-up dialog window
```

Note that the data lists could have been represented as a single list of (x, y1, y2) tuples, but that the list would have to be transposed before its elements could used used by plot.   After transposing, the x-data would be the zero'th index element and the y-data lists would be the first and second index elements respectively.

Repeating the above code will cause your program to display a single plot window at a time, waiting for you to close that window before showing the next.   If you'd like to make multiple plots that show simultaneously, please see the section on Make and Show Multiple Plots.

Here is a very useful encapsulation of the above plotting technique in terms of a reusable function (collapsed for readability):

```import matplotlib.pyplot as plt # Needed at the top of your code to bring in the plotting functions

def plotData(xData,yData1,yData2, legend1 ,legend2, xLabel, yLabel, title):
"""Displays a plot of two yData values over the given common xData.
xData = the common x-axis points the yData corresponds to.
yData1, yData2 = lists of y-axis data that corresponds with the given xData values.
legend1, legend2 = the legend text for the respective yData
xLabel, yLabel = the labels for the axes in the plot.
title = the title of the plot
"""

# Prey use circles connected by solid line.
plot1 = plt.plot(xData, yData1, 'o-' )

# Predators use squares connected by dotted line.
plot2 = plt.plot(txData, yData2, 's:' )

# Place legend box in "best" location.
plt.legend( (plot1, plot2), (legend1, legend2), 'best')

plt.xlabel(xLabel)
plt.ylabel(yLabel)
plt.title(title)
plt.show()
```

## Make and Show Multiple Plots

The problem of in the above example for making a single line plot figure is that if you simply repeat the above code, Python will open up one plot window at a time and wait for you to close that window before showing you the next plot.    This is referred to a "blocking" or a "modal" window.  Somtimes however, you would like to calculate multiple plots and show them all simultaneously.

In the single line plot example, the plotting "figure" was created automatically and all the calls to make the legends, label the axes, etc. defaulted to that one figure. In a nutshell, to make multiple plots, what you need to do is to explicitly create each plot figure and explicitly add the plots, legends, labels, etc. to that figure -- technically the "axes" object from that figure.

```# get the plotting library functions
import matplotlib.pyplot as plt  #import show, figure

xDataA = []   # Needs to be filled with x-data values

yDataA1 = []   # first y-data list
yDataA2 = []   # second y-data list

xDataB = []   # Needs to be filled with x-data values

yDataB1 = []   # first y-data list
yDataB2 = []   # second y-data list

# put code here to fill yData lists with values corresponding to xData values.

fig1 = plt.figure()   # create an empty plotting figure
fig2 = plt.figure()   # create an empty plotting figure

f1ax = fig1.add_subplot(111)   # add a sub-plot to the figure (subplot #1 with 1 row and 1 col).  An axes object is returned.
f2ax = fig2.add_subplot(111)   # add a sub-plot to the figure (subplot #1 with 1 row and 1 col).  An axes object is returned.

# Create one plot
f1ax.plotA1 = f1ax.plot(xDataA,  yDataA1, 'o-' )  # circles connected by solid line
f1ax.plotA2 = f1ax.plot(xDataA,  yDataA2, 's:' )  # squares connected by dotted line
f1ax.legend( (plotA1, plotA2), ('y-data 1', 'y-data 2'), 'best')   # legend box placed in "best" location
f1ax.set_xlabel('x description')   # description for the x-axis
f1ax.set_ylabel('y description')   # description for the y--axis
f1ax.set_title('Plot title A')  # top title of the plot

# Create another plot
f2ax.plotA1 = f1ax.plot(xDataB,  yDataB1, 'o-' )  # circles connected by solid line
f2ax.plotA2 = f1ax.plot(xDataB,  yDataB2, 's:' )  # squares connected by dotted line
f2ax.legend( (plotB1, plotB2), ('y-data 1', 'y-data 2'), 'best')   # legend box placed in "best" location
f2ax.set_xlabel('x description')   # description for the x-axis
f2ax.set_ylabel('y description')   # description for the y--axis
f2ax.set_title('Plot title B')  # top title of the plot

plt.show()   # show all the plots in modal pop-up windows
```

Note that the set of plots is still modal -- you have to close all the plot windows before the program will continue.

The pyplot.display() function can be called to update any open plotting windows

Here are some useful plotting functions:   (The code has been collapsed for easier readability.)

Note that "`plt.show()`" must be called after these functions in order to display the plotting window(s).

Plot a list of tuples  (t, y1, y2, y3, ...., yn) to create a plot with n lines corresponding to each y-vs-t plot:

```import matplotlib.pyplot as plt  # assumed to be at top of code

def makeTimePlot(data, legendSymbols, tLabel, yLabel, title):
""" Make a plot of a list of tuples of at least two elements where all elements in the tuples are plotted against the first value in the tuple
data = list of tuples (time, y1, y2, y3, ...)
legendSymbols = list of tuples (legend1, symbol1) corresponding to the y-data.  symbolx="" means line only, no symbols
tLabel, yLabel = the labels for the time and y axis respectively
title = the top title for the plot
Returns the plt.Figure object created.
--show() MUST be called after this function to display the plots!--
"""

fig = plt.figure()
legSyms = zip(*legendSymbols)
results = zip(*data)  # transpose the data
plots = []
for i in range(1, len(data[0])) :
plots.append(fax.plot(results[0], results[i], (legSyms[1])[i-1]+'-'))
fax.legend(plots, legSyms[0], 'best') # legend box placed in "best" location
fax.set_xlabel(tLabel)
fax.set_ylabel(yLabel)
fax.set_title(title)
return fig
```

Plot a multiple lists of tuples where the first element of the tuple is plotted against the zero'th element of that tuple.

```import matplotlib.pyplot as plt  # assumed to be at top of code

def makeXYPlot(data, legendSymbols, xLabel, yLabel, title):
""" Make a plot of multiple lists of x-y tuples
data = list of lists of tuples (x, y)
legendSymbols = list of tuples (legend1, symbol1) corresponding to the xy-data.  symbolx="" means line only, no symbols
xLabel, yLabel = the labels for the x and y axis respectively
title = the top title for the plot
Returns the plt.Figure object created.
--show() MUST be called after this function to display the plots!--
"""

fig = plt.figure()
legSyms = zip(*legendSymbols)

plots = []
for i in range(len(data)) :
results = zip(*data[i])  # transpose the data
plots.append(fax.plot(results[0], results[1], (legSyms[1])[i]+'-'))
fax.legend(plots, legSyms[0], 'best') # legend box placed in "best" location
fax.set_xlabel(xLabel)
fax.set_ylabel(yLabel)
fax.set_title(title)
return fig

```

## Reset the plotting windows

If your code fails during the processing of plots,  the matplotlib library unfortunately makes global state changes to your Python execution environment, retaining the plots that were initiated but not shown.    This means that the next time you run your code successfully, all the pending plots from previous failed run attemps will show alongside the correct plots.    If you run your code again however, since the cache of pending plots has now been cleared, only the expected plots will subsequently be displayed.

To get around this rather annoying and confusing behavior, it is a useful to forcibly reset the pending plotting windows so that they won't be displayed.    To do this, simply make a call to the pyplot.close function with the 'all' parameter before you begin your plotting calls.   This will close all the pending (and thus invisible) plotting windows, leaving only the desired and expected plots.

```import matplotlib.pyplot as plt  # This line should be at the top of your code

plt.close('all')   # close all the pending, invisible plot windows.

# Now do your plotting calls...

```

## Creating 3-D Plots

For 3-D plots, you will need two import statements:

```import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
```

The Axis3D object is used to create 3-D plots.    Most of the operations are very similar to those used to make 2-D plots, but with the additional z-data list.

No axis lines?   Unfortunately, so far I can tell, Python does not have a feature to show axis lines to be plotted through middle of a 3-D plot.   The axes are only shown on the edge of the plotting area.  This makes it more difficult visualize the origin and the plotting axis in space relative to the plotted entities, such as a cluster of data.   Luckily, most of the problem can be solved by simply adding 3 straight lines on the plot that correspond to the locations of the 3 axes.

The following example makes a 3-D scatter plot of blue dots with 3 straight red lines corresponding to the x, y and z axes:

```     # The plotting data is in pltTuples, a list of tuples of (x, y, z)

plt.close('all')   # close all latent plotting windows
fig1 = plt.figure()   # Make a plotting figure
ax = Axes3D(fig1)   # use the plotting figure to create a Axis3D object.
pltData = zip(*pltTuples)     # transpose the list of tuples to separate the x, y and z components.
ax.scatter(pltData[0], pltData[1], pltData[2], 'bo')  # make a scatter plot of blue dots from the data

# make simple, bare axis lines through space:
xAxisLine = ((min(pltData[0]), max(pltData[0])), (0, 0), (0,0))  # 2 points make the x-axis line at the data extrema along x-axis
ax.plot(xAxisLine[0], xAxisLine[1], xAxisLine[2], r')   # make a red line for the x-axis.
yAxisLine = ((0, 0), (min(pltData[1]), max(pltData[1])),  (0,0))    # 2 points make the y-axis line at the data extrema along y-axis
ax.plot(yAxisLine[0], yAxisLine[1], yAxisLine[2], 'r')    # make a red line for the y-axis.
zAxisLine = ((0, 0), (0,0), (min(pltData[2]), max(pltData[2])))  # 2 points make the z-axis line at the data extrema along z-axis
ax.plot(zAxisLine[0], zAxisLine[1], zAxisLine[2], 'r')   # make a red line for the z-axis.

# label the axes
ax.set_xlabel("x-axis label")
ax.set_ylabel("y-axis label")
ax.set_zlabel("y-axis label")
ax.set_title("The title of the plot")
plt.show()    # show the plot
```