Difference between revisions of "Python:Plotting"

From PrattWiki
Jump to navigation Jump to search
(Notes)
 
(34 intermediate revisions by the same user not shown)
Line 1: Line 1:
This page will primarily go over some examples of different ways to plot data sets. It assumes
+
This page will primarily go over some examples of different ways to plot data sets. It assumes the lines
<source lang="python">
+
<syntaxhighlight lang="python">
 
import math as m
 
import math as m
 
import numpy as np
 
import numpy as np
 
import matplotlib.pyplot as plt
 
import matplotlib.pyplot as plt
</source>
+
</syntaxhighlight >
is in the program.
+
are in the program.
  
 
== Introduction ==
 
== Introduction ==
For a good tutorial on using the matplotlib.pyplot module - especially the object-oriented approach, see[https://realpython.com/python-matplotlib-guide/ Python Plotting With Matplotlib (Guide)] by Brad Solomon as recommended by [https://bme.duke.edu/faculty/mark-palmeri Dr. Mark Palmeri], Duke DME.
+
For a good tutorial on using the matplotlib.pyplot module - especially the object-oriented approach, see [https://realpython.com/python-matplotlib-guide/ Python Plotting With Matplotlib (Guide)] by Brad Solomon as recommended by [https://bme.duke.edu/faculty/mark-palmeri Dr. Mark Palmeri], Duke BME.
 +
 
 +
Note that commands given as plt.COMMAND() may sometimes apply to either a figure or a set of axes or both.  In November of 2019, plot infrastructure on this page was formalized to independently create the figure and one or more handles to axes.  The primary reason for this is that the formal method is required in order to create 3D plots and so practicing it with 2D plots will prepare programmers for all cases.
 +
 
 +
=== Notes ===
 +
In Fall of 2022, it looks like figures may not update correctly unless (a) saved, (b) resized, (c) explicitly told to update, or (d) interactive plotting is turned on.  That is:
 +
 
 +
* Saving the figure with
 +
fig.savefig("FILE.EXT")
 +
* Resizing the window using the mouse
 +
* Explicitly updating with
 +
fig.canvas.draw()
 +
* Turning interactive plotting on with
 +
plt.ion()
 +
 
 +
This is with Spyder 5.2.2.
 +
 
 +
=== Simple Examples ===
 +
If you are here to see some basic ways to get plots to work, this is the section for you!  The main steps for creating most (relatively simple) plots are:
 +
* Import appropriate modules
 +
* Create figure and axis handles
 +
* Make plot
 +
* Add title, labels, etc.
 +
* Save the plot
 +
Note also that depending on your development environment, graphics may show up in their own window or in the console.  These examples generally assume you are having graphics show up in their own window - it makes it much easier to make alterations to graphs after their initial creation.  If you are using Spyder, the way to get graphs to show up in their own window is:
 +
* Within the Tools menu select Preferences
 +
* From the Preferences list on the left select IPython console
 +
* In the right window, click the Graphics tab
 +
* In the second section (Graphics backend), select Automatic
 +
* Click the OK button at the bottom right of the window.
 +
==== Plotting a few points ====
 +
Here is a complete example that will generate and save a single figure window with a single set of axes within it:
 +
[[File:Example1 PunPlot.png|thumb]]
 +
<syntaxhighlight lang=python>
 +
# Imports
 +
import matplotlib.pyplot as plt
 +
# Data
 +
x = [1, 2, 4]
 +
y = [2, 1, 5]
 +
# Set up figure and axis
 +
fig = plt.figure(num=1, clear=True)
 +
ax = fig.add_subplot(1, 1, 1)
 +
# Plot using red circles
 +
ax.plot(x, y, 'ro')
 +
# Set labels and turn grid on
 +
ax.set(xlabel='x', ylabel='y', title='Three Points')
 +
ax.grid(True)
 +
# Use space most effectively
 +
fig.tight_layout()
 +
# Save as a PNG graphics file
 +
fig.savefig('Example1.png')
 +
</syntaxhighlight>
 +
Note that the <code>plt.figure(num=1, clear=True)</code> line does not work correctly in some versions of matplotlib - the way you will detect the problem is if you re-run the code and there either looks to be two different plots on top of each other or your current plots takes up less of the screen and the text gets dark.  If that happens to you, look at the fix in the [[#Clearing_Figures]] section below.
 +
 
 +
==== Plotting a couple lines ====
 +
This example uses the numpy module to create an array and apply trig and basic math to that array.  It also shows how to use the <code>label</code> kwarg in concert with the legend method to produce a legend.  Finally, notice in the <code>ax.set()</code> method that there is an <code>r</code> in front of the <code>ylabel</code> text.  If you want to use basic <math>\LaTeX</math> commands in labels, titles, or text, you need to pass them in raw strings.  Finally, the <code>markevery</code> command allwys you to specify how often to put a data marker when plotting a data set.
 +
[[File:Example2 PunPlot.png|thumb]]
 +
<syntaxhighlight lang=python>
 +
# Imports
 +
import matplotlib.pyplot as plt
 +
import numpy as np
 +
# Data
 +
t = np.linspace(0, 2*np.pi, 101)
 +
x1 = 1 + 2 * np.cos(t)
 +
x2 = 2 + 1 * np.sin(t)
 +
# Set up figure and axis
 +
fig = plt.figure(num=1, clear=True)
 +
ax = fig.add_subplot(1, 1, 1)
 +
# Plot using red circles
 +
ax.plot(t, x1, 'gs-', label='First Line', markevery=10)
 +
ax.plot(t, x2, 'bd:', label='Second Line', markevery=20)
 +
# Set labels and turn grid on
 +
ax.set(xlabel='Time $t$, sec', ylabel=r'Value $\xi$, m', title='Two Lines')
 +
ax.grid(True)
 +
ax.legend(loc='best')
 +
# Use space most effectively
 +
fig.tight_layout()
 +
# Save as a PNG file
 +
fig.savefig('Example2.png')
 +
</syntaxhighlight>
 +
 
 
=== The <code>plt.plot()</code> Function ===
 
=== The <code>plt.plot()</code> Function ===
 
The <code>plt.plot()</code> function is used to plot sets of data on a 2-D grid.
 
The <code>plt.plot()</code> function is used to plot sets of data on a 2-D grid.
 
What follows comes from matplotlib.pyplot's <code>help</code> function  (some paragraphs have been snipped out).  The line styles, symbols, and colors are formatted as a clearer table.  
 
What follows comes from matplotlib.pyplot's <code>help</code> function  (some paragraphs have been snipped out).  The line styles, symbols, and colors are formatted as a clearer table.  
<source lang="text">
+
<syntaxhighlight lang="text">
  
 
plot(*args, **kwargs)
 
plot(*args, **kwargs)
Line 48: Line 128:
 
     The following format string characters are accepted to control
 
     The following format string characters are accepted to control
 
     the line style or marker (and) the following color abbreviations are supported:
 
     the line style or marker (and) the following color abbreviations are supported:
</source>
+
</syntaxhighlight >
 
<center>
 
<center>
 
{| style="text-align:center" cellpadding="3"
 
{| style="text-align:center" cellpadding="3"
Line 116: Line 196:
 
</center>
 
</center>
  
<source lang=text>     
+
<syntaxhighlight lang=text>     
 
     In addition, you can specify colors in many weird and
 
     In addition, you can specify colors in many weird and
 
     wonderful ways, including full names (``'green'``), hex
 
     wonderful ways, including full names (``'green'``), hex
Line 151: Line 231:
 
         plot(x, y, color='green', linestyle='dashed', marker='o',
 
         plot(x, y, color='green', linestyle='dashed', marker='o',
 
             markerfacecolor='blue', markersize=12).
 
             markerfacecolor='blue', markersize=12).
</source>
+
</syntaxhighlight >
 
 
<!--
 
=== The <code>subplot</code> Function ===
 
The <code>subplot</code> function is used to plot multiple plot windows
 
within the same figure.
 
What follows comes from MATLAB's <code>help</code> function in MATLAB R2009a<ref name="Help:Subplot">Quoted from MATLAB help file for <code>plot</code></ref> (some
 
paragraphs have been snipped out).
 
<source lang="text">
 
SUBPLOT Create axes in tiled positions.
 
    H = SUBPLOT(m,n,p), or SUBPLOT(mnp), breaks the Figure window
 
    into an m-by-n matrix of small axes, selects the p-th axes for
 
    for the current plot, and returns the axis handle.  The axes
 
    are counted along the top row of the Figure window, then the
 
    second row, etc.  For example,
 
 
 
        SUBPLOT(2,1,1), PLOT(income)
 
        SUBPLOT(2,1,2), PLOT(outgo)
 
 
 
    plots income on the top half of the window and outgo on the
 
    bottom half. If the CurrentAxes is nested in a uipanel the
 
    panel is used as the parent for the subplot instead of the
 
    current figure.
 
 
 
    SUBPLOT(m,n,p), if the axis already exists, makes it current.
 
    SUBPLOT(m,n,p,'replace'), if the axis already exists, deletes it and
 
    creates a new axis.
 
    SUBPLOT(m,n,p,'align') places the axes so that the plot boxes
 
    are aligned instead of preventing the labels and ticks from
 
    overlapping.
 
    SUBPLOT(m,n,P), where P is a vector, specifies an axes position
 
    that covers all the subplot positions listed in P.
 
    SUBPLOT(H), where H is an axis handle, is another way of making
 
    an axis current for subsequent plotting commands.
 
 
    SUBPLOT('position',[left bottom width height]) creates an
 
    axis at the specified position in normalized coordinates (in
 
    in the range from 0.0 to 1.0).
 
 
    SUBPLOT(m,n,p, PROP1, VALUE1, PROP2, VALUE2, ...) sets the
 
    specified property-value pairs on the subplot axis. To add the
 
    subplot to a specific figure pass the figure handle as the
 
    value for the 'Parent' property.
 
 
    If a SUBPLOT specification causes a new axis to overlap an
 
    existing axis, the existing axis is deleted - unless the position
 
    of the new and existing axis are identical.  For example,
 
    the statement SUBPLOT(1,2,1) deletes all existing axes overlapping
 
    the left side of the Figure window and creates a new axis on that
 
    side - unless there is an axes there with a position that exactly
 
    matches the position of the new axes (and 'replace' was not specified),
 
    in which case all other overlapping axes will be deleted and the
 
    matching axes will become the current axes.
 
% snip
 
</source>
 
Note that subplots are numbered first from left to right, then by row - in other words, like a telephone dial.  This is transposed from how just about everything else is numbered in MATLAB.
 
 
 
=== The <code>orient</code> Function ===
 
The <code>orient</code> function is used to change how the plot is oriented
 
on the page when printing.  Note that it does not affect how the plot
 
looks on the screen.  Furthermore, the <code>orient</code> command must
 
be used each time a new figure opens for that figure to have a specified
 
orientation.
 
What follows comes from MATLAB's <code>help</code> function in MATLAB R2009a<ref name="Help:Orient">Quoted from MATLAB help file for <code>orient</code></ref> (some
 
paragraphs have been snipped out).
 
<source lang="text">
 
ORIENT Set paper orientation for printing.
 
    ORIENT is used to set up the orientation of a Figure or Model
 
    window for printing.
 
 
    ORIENT LANDSCAPE causes subsequent PRINT operations from the current
 
    Figure window to generate output in full-page landscape orientation
 
    on the paper.
 
 
    ORIENT ROTATED causes subsequent PRINT operations from the current
 
    Figure window to generate output in full-page rotated orientation
 
    on the paper.
 
 
    ORIENT PORTRAIT causes subsequent PRINT operations from the current
 
    Figure window to generate output in portrait orientation.
 
 
    ORIENT TALL causes the current Figure window to map to the whole page
 
    in portrait orientation for subsequent PRINT operations.
 
 
    ORIENT, by itself, returns a string containing the paper
 
    orientation, either PORTRAIT, LANDSCAPE, ROTATED or TALL
 
        of the current Figure.
 
 
    ORIENT(FIGHandle) or ORIENT(MODELName) returns the current
 
    orientation of the Figure or Model.
 
 
    ORIENT( FIG, ORIENTATION) specifies which figure to orient and how to
 
    orient it based on the rules given above.  ORIENTATION is one of
 
    'landscape', 'portrait', 'rotated', or 'tall'.
 
% snip
 
</source>
 
-->
 
  
 
=== The <code>plt.savefig()</code> Function ===
 
=== The <code>plt.savefig()</code> Function ===
The <code>plt.savefig()</code> function is used to save a plot to a file.  The type of sile is determined by the extension of the file name.  For example,  
+
The <code>plt.savefig()</code> function is used to save a plot to a file.  The type of file is determined by the extension of the file name.  For example,  
  plt.savefig('blah.eps')
+
  plt.savefig('blah.png')
will save the file to a color Encapsulated PostScript file, while
+
will save the file to a portable network graphics file, while
 
  plt.savefig('blah.pdf')
 
  plt.savefig('blah.pdf')
will save it to a PDF.  From the help file for the command, Most backends support png, pdf, ps, eps and svg.
+
will save it to a PDF.  From the help file for the command, "Most backends support png, pdf, ps, eps and svg."  If you have created a figure handle, you should use the <code>safefig</code> on that handle - that is, use
 +
fig.savefig('blah.png')
 +
instead of using <code>plt</code>.
  
 
=== Example ===
 
=== Example ===
Line 260: Line 246:
 
The following Python code demonstrates how to fill the fourth window
 
The following Python code demonstrates how to fill the fourth window
 
of a 2x3 plot figure and save the figure as a PNG file; the resulting figure is in the thumbnail at right.
 
of a 2x3 plot figure and save the figure as a PNG file; the resulting figure is in the thumbnail at right.
<source lang="python">
+
<syntaxhighlight lang="Python">
 
# Import required modules
 
# Import required modules
 
import numpy as np
 
import numpy as np
Line 273: Line 259:
  
 
# Create a figure with two rows of three figures
 
# Create a figure with two rows of three figures
fig, axs = plt.subplots(num=0)
+
fig = plt.figure(num=1, clear=True)
fig.clf()  
+
ax = fig.subplots(2, 3)
fig, axs = plt.subplots(2, 3, num=0)
 
  
 
# Plot the values of the function usf(x) against the vector x in bottom left
 
# Plot the values of the function usf(x) against the vector x in bottom left
axs[1][0].plot(x, usf(x))  
+
ax[1][0].plot(x, usf(x))  
  
 
# Change the axes so the function is not covered by the subplot box
 
# Change the axes so the function is not covered by the subplot box
axs[1][0].axis([-1, 1, -1, 2])
+
ax[1][0].axis([-1, 1, -1, 2])
 +
 
 +
# Set labels and title
 +
ax[1][0].set(title='Unit Step Function', xlabel='x', ylabel='u(x)')
 +
 
 +
# Clear the rest other than bottom left and top right
 +
fig.delaxes(ax[0][0])
 +
fig.delaxes(ax[0][1])
 +
fig.delaxes(ax[1][1])
 +
fig.delaxes(ax[1][2])
 +
 
 +
# Issue tight layout to fix label overlaps
 +
fig.tight_layout()
 +
 
 +
# Send the current figure to a file named usfplot.png
 +
fig.savefig('usfplot.png')
 +
</syntaxhighlight>
 +
 
 +
=== Example Redux ===
 +
[[File:UsfplotPython.png|thumb|Example showing a figure split into 2x3 subplots with subplot 3 holding an empty axis and subplot 4 holding a graph of the unit step function.]]
 +
The following Python code demonstrates how to fill the fourth window
 +
of a 2x3 plot figure and save the figure as a PNG file; the resulting figure is in the thumbnail at right.  In this case, instead of creating 6 subplots and deleting four of them, the code below only adds the subplots we want.
 +
<syntaxhighlight lang="Python">
 +
# Import required modules
 +
import numpy as np
 +
import matplotlib.pyplot as plt
 +
 
 +
# Create a 100 numbers between -1 and 1
 +
x = np.linspace(-1, 1, 100);
 +
 
 +
# This formula for usf is one of several ways to define the unit step function
 +
def usf(t):
 +
    return (t>=0)*1.0;
 +
 
 +
# Create a figure
 +
fig = plt.figure(num=1, clear=True)
 +
 
 +
# Create axes for panels 3 and 4 in a 2x3 configuration
 +
ax3 = fig.add_subplot(2, 3, 3)
 +
ax4 = fig.add_subplot(2, 3, 4)
 +
 
 +
# Plot the values of the function usf(x) against the vector x in bottom left
 +
ax4.plot(x, usf(x))
 +
 
 +
# Change the axes so the function is not covered by the subplot box
 +
ax4.axis([-1, 1, -1, 2])
  
 
# Give the current subplot a title
 
# Give the current subplot a title
axs[1][0].set_title('Unit Step Function')
+
ax4.set_title('Unit Step Function')
  
 
# Set the x-label
 
# Set the x-label
axs[1][0].set_xlabel('x')
+
ax4.set_xlabel('x')
  
 
# Set the y-label
 
# Set the y-label
axs[1][0].set_ylabel('u(x)')
+
ax4.set_ylabel('u(x)')
 
 
# Clear the rest other than bottom left and top right
 
fig.delaxes(axs[0][0])
 
fig.delaxes(axs[0][1])
 
fig.delaxes(axs[1][1])
 
fig.delaxes(axs[1][2])
 
  
 
# Issue tight layout to fix label overlaps
 
# Issue tight layout to fix label overlaps
Line 303: Line 327:
 
# Send the current figure to a file named usfplot.png
 
# Send the current figure to a file named usfplot.png
 
fig.savefig('usfplot.png')
 
fig.savefig('usfplot.png')
</source>
+
</syntaxhighlight>
 +
 
 +
== Setting Up Subplots ==
 +
See [[Python:Plotting/Subplots]] for more on setting up subplots.
 +
 
 +
== Python Settings ==
 +
For this course, you will generally want to have your graphics set to automatic; to make this change in Spyder:
 +
*Open the preferences window
 +
**On Windows, go to the Tools menu near the top right of the window and select Preferences
 +
**On MACs, go to the python menu near the top left of the window and select Preferences
 +
*In the Preferences window at the left select IPython console
 +
*In the right half of the Preferences window, select the Graphics tab
 +
*In the Backend pulldown, select Automatic
 +
*Click OK in the Preferences window
  
 
== General Plotting Tips ==
 
== General Plotting Tips ==
Line 311: Line 348:
 
<tr>
 
<tr>
 
<td width=100%>
 
<td width=100%>
<source lang="python">
+
<syntaxhighlight lang="python">
ax.set_xlabel('$v$, m/s')
+
ax.set(xlabel='$v$, m/s')
ax.set_xlabel('$v$ (m/s)')
+
ax.set(xlabel='$v$ (m/s)')
ax.set_xlabel('Velocity, m/s')
+
ax.set(xlabel='Velocity, m/s')
ax.set_xlabel('Velocity (m/s)')
+
ax.set(xlabel='Velocity (m/s)')
ax.set_xlabel('Velocity ($v$), m/s');
+
ax.set(xlabel='Velocity ($v$), m/s')
ax.set_xlabel('Velocity ($v$, m/s)');
+
ax.set(xlabel='Velocity ($v$, m/s)')
</source>
+
</syntaxhighlight >
 
</td></tr></table>
 
</td></tr></table>
:You should be consistent within a single graph as far as which version you use, and you should pick which format you believe conveys the information most efficiently and effectively.
+
: Note that you can use basic $$\LaTeX$$ commands (Greek letters, superscripts, etc) but if you do, you need to make sure the kwarg gets the raw version of the string; specifically, if you want to have the Greek letter $$\theta$$ and you try
* Make sure your titles make sense and that you have your NetID in one of the titles for each figure you create.  If you have multiple subplots, you only need one of the titles to have your NetID.  Typically, the title will read "DEP. VAR vs. INDEP. VAR for DESCRIPTION (NetID)" where DEP. VAR is your dependent variable (typically on the ''y'' axis), INDEP. VAR is your independent variable (typically on the ''x'' axis), DESCRIPTION is something that will differentiation this particular plot from others that might have the same variables (for example a data, or an experiment number), and NetID is your NetID.
+
<syntaxhighlight lang="python">
 +
ax.set(xlabel='$\theta(t)$, rad')
 +
</syntaxhighlight >
 +
:you will end up getting "''heta(t)'', rad" since the "\t" is processed as a tab character.  If you use:
 +
<syntaxhighlight lang="python">
 +
ax.set(xlabel=r'$\theta(t)$, rad')
 +
</syntaxhighlight >
 +
: you will get the correct result.
 +
* You should be consistent within a single graph as far as which version of axis labels you use, and you should pick which format you believe conveys the information most efficiently and effectively.
 +
* Make sure your titles make sense.  Typically, the title will read "DEP. VAR vs. INDEP. VAR for DESCRIPTION" where DEP. VAR is your dependent variable (typically on the ''y'' axis), INDEP. VAR is your independent variable (typically on the ''x'' axis), and DESCRIPTION is something that will differentiation this particular plot from others that might have the same variables (for example a data, or an experiment number).
 
* Data points should be represented by symbols and model equations should be represented by lines.  Be sure to use a legend to identify each item plotted if there are multiple data sets on the same graph.
 
* Data points should be represented by symbols and model equations should be represented by lines.  Be sure to use a legend to identify each item plotted if there are multiple data sets on the same graph.
 
* Typically, you will want to set the axis limits such that no data point is on the figure boundary.  Certainly you do not want a line to be plotted on top of a figure boundary.  After you make a plot, if there is a data point on an edge, look at the current axes and go out a little bit. Just make sure if you end up fundamentally changing your code that you adjust the axes accordingly.
 
* Typically, you will want to set the axis limits such that no data point is on the figure boundary.  Certainly you do not want a line to be plotted on top of a figure boundary.  After you make a plot, if there is a data point on an edge, look at the current axes and go out a little bit. Just make sure if you end up fundamentally changing your code that you adjust the axes accordingly.
  
 
== Using Different Line Styles ==
 
== Using Different Line Styles ==
Most of the time, you will be plotting three or fewer different lines on a single window, and they can thus be distinguished from one another in [[Python]] by using different line styles.   
+
Most of the time, you will be plotting three or fewer different lines on a single window, and they can thus be distinguished from one another in [[Python]] by using different line styles:
<source lang="python">
+
<html>
 +
<iframe src="https://trinket.io/embed/python3/4a45aaa186" width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>
 +
</html>
 +
<!--  
 +
<syntaxhighlight lang="python">
 +
import numpy as np
 +
import matplotlib.pyplot as plt
 +
 
 
x = np.linspace(0, 1, 100)
 
x = np.linspace(0, 1, 100)
 
y1 = x**0.5
 
y1 = x**0.5
 
y2 = x**1
 
y2 = x**1
 
y3 = x**2
 
y3 = x**2
plt.figure(1)
+
 
plt.clf()
+
fig = plt.figure(num=1, clear=True)
plt.plot(x, y1, 'k-', label='$y=x^{0.5}$')
+
ax = fig.add_subplot(1, 1, 1)
plt.plot(x, y2, 'k--', label='$y=x$')
+
 
plt.plot(x, y3, 'k:', label='$y=x^2$')
+
ax.plot(x, y1, 'k-', label='$y=x^{0.5}$')
plt.legend(loc='best');
+
ax.plot(x, y2, 'k--', label='$y=x$')
plt.title('$y=x^n$ for Three Values of n (mrg)')
+
ax.plot(x, y3, 'k:', label='$y=x^2$')
plt.xlabel('x')
+
ax.legend(loc='best')
plt.ylabel('y')
+
ax.set(xlabel = 'x', ylabel = 'y', title = '$y=x^n$ for Three Values of n')
plt.savefig('CurvePlotPython.png')
+
 
</source>
+
fig.tight_layout()
 +
fig.savefig('CurvePlotPython.png')
 +
</syntaxhighlight >
 
The figure this creates will be:
 
The figure this creates will be:
 
<center>
 
<center>
 
[[Image:CurvePlotPython.png]]
 
[[Image:CurvePlotPython.png]]
 
</center>
 
</center>
 +
-->
 
Note the legend command argument.  The default location is upper right but that may land on the data.  Telling it to use the best location will have Python look for the most-blank part of the plot.  There are many other options for location - try <code>help(plt.legend)</code> and look at the section about <code>loc</code> in the Other Parameters.
 
Note the legend command argument.  The default location is upper right but that may land on the data.  Telling it to use the best location will have Python look for the most-blank part of the plot.  There are many other options for location - try <code>help(plt.legend)</code> and look at the section about <code>loc</code> in the Other Parameters.
  
Line 355: Line 411:
 
you do not want to try to jam hundreds of symbols onto a curve.
 
you do not want to try to jam hundreds of symbols onto a curve.
 
The left graph in the figure below shows what a disaster that
 
The left graph in the figure below shows what a disaster that
could be.  Instead, you can plot a '''line''' with all your data but plot
+
could be.  Instead, you can plot a '''line''' with all your data but then tell Python to
points on top of the line with only some of the data.  The right side of the figure  
+
plot points only every so often.  This is done with the ''markevery'' kwarg.  The right side of the figure  
shows the result of this operation.  The code for  
+
shows the result of this operation.   
both graphs in the figure is given below it.
+
<html>
 +
<iframe src="https://trinket.io/embed/python3/9f30e477a0" width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" allowfullscreen></iframe>
 +
</html>
 +
<!--
 +
The code for both graphs in the figure is given below it.
 
<center>
 
<center>
 
[[Image:DeccurvesPython.png]]
 
[[Image:DeccurvesPython.png]]
 
</center>
 
</center>
  
<source lang="python">
+
<syntaxhighlight lang="python">
 +
import numpy as np
 +
import matplotlib.pyplot as plt
 +
 
 
x = np.linspace(0, 1, 100)
 
x = np.linspace(0, 1, 100)
 
y1 = x**0.5
 
y1 = x**0.5
 
y2 = x**1
 
y2 = x**1
 
y3 = x**2
 
y3 = x**2
plt.figure(1)
+
fig = plt.figure(num=1, clear=True)
plt.clf()
+
ax = fig.subplots(1, 2)
plt.subplot(1,2,1) # left graph (ewwwwwww)
 
plt.plot(x, y1, 'k-s', x, y2, 'k-o', x, y3, 'k-d')
 
plt.legend(['y=x^{0.5}', 'y=x', 'y=x^2'], loc='best')
 
  
plt.title('Three Power Curves (mrg)')
+
# left graph (ewwwwwww)
plt.xlabel('x')
+
ax[0].plot(x, y1, 'k-s', x, y2, 'k-o', x, y3, 'k-d')
plt.ylabel('y')
+
ax[0].legend(['$y=x^{0.5}$', '$y=x$', '$y=x^2$'], loc='best')
 +
ax[0].set(xlabel = 'x', ylabel = 'y', title = 'Three Power Curves')
  
plt.subplot(1,2,2) # right graph (ooo.  aah.  wow)
+
# right graph (ahhhhhhh)
plt.plot(x, y1, 'k-', x, y2, 'k-', x, y3, 'k-') # lines only!
+
ax[1].plot(x, y1, 'k-s', x, y2, 'k-o', x, y3, 'k-d', markevery=10)
Incr = 10;
+
ax[1].legend(['$y=x^{0.5}$', '$y=x$', '$y=x^2$'],  loc='best')
 +
ax[1].set(xlabel = 'x', ylabel = 'y', title = 'Three Power Curves')
  
PointPlot=plt.plot(
+
fig.tight_layout()
    x[::Incr], y1[::Incr], 'ks',
+
fig.savefig('DeccurvesPython.png')
    x[::Incr], y2[::Incr], 'ko',
+
</syntaxhighlight >
    x[::Incr], y3[::Incr], 'kd')
+
-->
plt.legend(PointPlot, ['y=x^{0.5}', 'y=x', 'y=x^2'],  loc='best')
 
plt.title('Three Power Curves (mrg)')
 
plt.xlabel('x')
 
plt.ylabel('y')
 
plt.savefig('DeccurvesPython.png')
 
</source>
 
 
 
Note that the assignment of one of the plots to the variable <code>PointPlot</code> is required so that the legend can be told which set of
 
plots to use as a basis for the legend.  Without this, it would be
 
possible for the legend to be based on the plot using solid lines (which are
 
the same for all three data sets) rather than to be based on the
 
plot using the symbols.
 
  
 
== Using Different Scales ==
 
== Using Different Scales ==
 
In the above example, two different scales were used for the data sets - a refined scale for the line and a rougher scale for the data points themselves.  In other cases, it is the ''line'' that will need the rougher scale.  As an example, assume you want Python to numerically find the minimum of the function <math>y=3x^2+11x-2</math> using the built-in <code>min</code> command.  To get the most precise answer possible, you will want to give Python a very large number of points - say, 1 million.  That code might look like:
 
In the above example, two different scales were used for the data sets - a refined scale for the line and a rougher scale for the data points themselves.  In other cases, it is the ''line'' that will need the rougher scale.  As an example, assume you want Python to numerically find the minimum of the function <math>y=3x^2+11x-2</math> using the built-in <code>min</code> command.  To get the most precise answer possible, you will want to give Python a very large number of points - say, 1 million.  That code might look like:
<source lang="Python">
+
<syntaxhighlight lang="Python">
P = [3, 11, -2]
+
fun = lambda x: 3*x**2 + 11*x - 2
 
xVals = np.linspace(-3.0, 3.0, int(1e6))
 
xVals = np.linspace(-3.0, 3.0, int(1e6))
yVals = np.polyval(P, xVals)
+
yVals = fun(xVals)
 
yMin = min(yVals)
 
yMin = min(yVals)
 
xMin = xVals[np.where(yVals==yMin)]
 
xMin = xVals[np.where(yVals==yMin)]
 
print(xMin, yMin)
 
print(xMin, yMin)
</source>
+
</syntaxhighlight >
 
Since the domain is 6 and there are 1e6 points, the spacing between points is approximately 6e-06.  The ''x''-coordinate of the answer then should be very close to the actual answer.  In fact, Python determines that the minimum value is -1.208333333332658e+01 and its ''x''-coordinate is -1.833334833334833e+00 - very close to finding the minimum of ''y''=-1.208333333333333e+01 at  ''x''=-1.833333333333333e+00
 
Since the domain is 6 and there are 1e6 points, the spacing between points is approximately 6e-06.  The ''x''-coordinate of the answer then should be very close to the actual answer.  In fact, Python determines that the minimum value is -1.208333333332658e+01 and its ''x''-coordinate is -1.833334833334833e+00 - very close to finding the minimum of ''y''=-1.208333333333333e+01 at  ''x''=-1.833333333333333e+00
  
 
The problem, however, is that if you want to graph this, one million points is somewhat absurd.  Unless you are planning to zoom in on the graph, there are around 1000x as many points as necessary/realistic/useful for a plot like this.  Instead, you should either recalculate the equation using a smaller set of independent data or plot only some of the data.  Code for the former might be:
 
The problem, however, is that if you want to graph this, one million points is somewhat absurd.  Unless you are planning to zoom in on the graph, there are around 1000x as many points as necessary/realistic/useful for a plot like this.  Instead, you should either recalculate the equation using a smaller set of independent data or plot only some of the data.  Code for the former might be:
<source lang="python">
+
<syntaxhighlight lang="python">
 
xPlot = np.linspace(-3, 3, 100)
 
xPlot = np.linspace(-3, 3, 100)
yPlot = np.polyval(P, xPlot)
+
yPlot = fun(xPlot)
plt.plot(xPlot, yPlot, 'k-')
+
fig = plt.figure(num=1, clear=True)
plt.xlabel('x')
+
ax = fig.add_subplot(1, 1, 1)
plt.ylabel('y')
+
ax.plot(xPlot, yPlot, 'k-')
plt.title('$y=3x^2+11x-2$ (mrg)')
+
ax.set(xlabel = 'x', ylabel = 'y', title = '$y=3x^2+11x-2$')
plt.grid(1)
+
ax.grid(True)
</source>
+
</syntaxhighlight >
 
while code for the latter might be:
 
while code for the latter might be:
<source lang="python">
+
<syntaxhighlight lang="python">
plt.plot(xVals[::1000], yVals[::1000], 'k-');
+
fig.plot(xVals[::1000], yVals[::1000], 'k-')
plt.xlabel('x');
+
ax.set(xlabel = 'x', ylabel = 'y', title = '$y=3x^2+11x-2$')
plt.ylabel('y');
+
ax.grid(True)
plt.title('$y=3x^2+11x-2$ (mrg)');
+
</syntaxhighlight >
plt.grid(1)
 
</source>
 
  
 
The advantage of the first version above is that your domain definitely remains the same - the <code>xPlot</code> variable spans the same [-3, 3] as the <code>xVals</code> set even though it has three orders of magnitude fewer values.  The disadvantage is that you will have to re-perform all the calculations on this new set.
 
The advantage of the first version above is that your domain definitely remains the same - the <code>xPlot</code> variable spans the same [-3, 3] as the <code>xVals</code> set even though it has three orders of magnitude fewer values.  The disadvantage is that you will have to re-perform all the calculations on this new set.
Line 435: Line 483:
  
 
== Putting Text on a Plot ==
 
== Putting Text on a Plot ==
The <code>text(x, y, MyText)</code>  
+
The <code>ax.text(x, y, MyText)</code>  
 
command will plot the text string <code>MyText</code> at  
 
command will plot the text string <code>MyText</code> at  
 
the (''x'',''y'') coordinate. There are several important aspects to the <code>text</code>  
 
the (''x'',''y'') coordinate. There are several important aspects to the <code>text</code>  
Line 441: Line 489:
  
 
First, the text command will ''not'' make the figure change its axes  
 
First, the text command will ''not'' make the figure change its axes  
to fit the data automatically. Clear the figure using <code>plt.clf()</code> then  try typing:
+
to fit the data automatically. Try typing:
<source lang="python">
+
<syntaxhighlight lang="python">
plt.text(2, 2, 'Hi')
+
fig = plt.figure(num=1, clear=True)
</source>
+
ax = fig.add_subplot(1, 1, 1)
The figure will show the word Hi in the top right but it will not be in the axis limits.
+
ax.text(2, 2, 'Hi')
 +
</syntaxhighlight >
 +
The figure will try to show the word Hi in the top right but it will not be in the axis limits.
 
You need to change the axis limits by using the <code>axis</code> command.  In this case,
 
You need to change the axis limits by using the <code>axis</code> command.  In this case,
<source lang="python">
+
<syntaxhighlight lang="python">
plt.axis((0, 3, 1, 4))
+
ax.axis((0, 3, 1, 4))
plt.text(2, 3, 'Hi')
+
ax.text(2, 3, 'Hi 2')
</source>
+
</syntaxhighlight >
 
will make the ''x'' axis go from 0 to 3 and the ''y''
 
will make the ''x'' axis go from 0 to 3 and the ''y''
axis go from 1 to 4, more than enough space to say "Hi" at (2,3)!
+
axis go from 1 to 4, more than enough space to say "Hi 2" at (2,3)!
  
 
Next, note that  
 
Next, note that  
text placed on plots stays until the next plotting command is
+
text placed on plots will stay until the axes are cleared, so you can use multiple <code>ax.text</code> commands to get several
issued, so you can use multiple <code>plt.text</code> commands to get several
+
labels on one plot.  You can also use the '''fig''' object to place text - in this case, the coordinates for the ''x'' and ''y'' values will be numbers between 0 and 1 where 0,0 is the bottom left of the figure and 1,1 is the top right.
labels on one plot.   
 
  
<!--
+
== Updates / Comments ==
== Setting Limits and Tick Locations ==
+
===Clearing Figures ===
[[File:PalmFig050204plot.png|thumb|300px]]You can set the specific limits and tick locations for a graph such that they will not change when you resize the figure window and so that they print exactly as you see on the screen.  To do this, you will use the <code>set</code> command with some specific arguments. The following example is modified from Palm, Chapter 5.2, on page 231 to create the specific figure as shown in the book:
+
The <code>clear</code> kwarg in <code>plt.figure()</code> or <code>plt.subplots()</code> does not always seem to work with older versions of matplotlib. Sometimes, future calls to <code>plt.subplots()</code> and <code>fig.tight_layout()</code> will cause the active area of the plot to become smaller and also will cause the axes to be redrawn without removing the old axes. If you have that problem, you will need to replace
 
+
<syntaxhighlight  lang=python>
<source lang=python>
+
fig = plt.figure(num=1, clear=True)
% Code based on code from:
+
</syntaxhighlight>
% Palm, Introduction to MATLAB for Engineers
+
with
% Chapter 5.2, p. 231
+
<syntaxhighlight lang=python>
 
+
fig = plt.figure(num=1)
%% Initialize workspace and figure
+
fig.clf()
clear, figure(1), clf
+
</syntaxhighlight>
 
+
to explicitly run the <code>clf</code> method on the new (or old) figure.
%% Original code (mostly)
 
x = -1:0.01:1;
 
y1 = 3+exp(-x).*sin(6*x);
 
y2 = 4+exp(-x).*cos(6*x);
 
plot((0.1+0.9i).^(0:0.01:10))
 
hold on
 
plot(y1, y2)
 
hold off
 
 
 
%% Add text and adjust limits and ticks
 
% Add text at specific locations, versus gtext
 
text(2.2, 5.7, 'y2 versus y1')
 
text(1.1, 0.3, 'Imag(z) versus Real(z)')
 
%  Get current axis (gca) and change limits and ticks
 
set(gca, 'XLim', [-1 6], 'YLim', [-1 7],...
 
        'XTick', -1:1:6, 'YTick', -1:1:7)
 
 
 
%% Print figure   
 
print -deps PalmFig050204plot</source>
 
 
 
[[File:PalmFig050204plot2.png|thumb|300px]]
 
You can also specify what goes at each tick mark - either by giving a vector of values or a cell array of strings. Changing the <code>set</code> command as given below produces the graph at right:
 
<source lang=matlab>
 
set(gca, 'XLim', [-1 6],  'YLim', [-1 7],...
 
        'XTick', -1:1:6, 'YTick', -1:1:7,...
 
        'XTickLabel',...
 
        {'eight', 'things', 'must', 'go', 'in', 'here', 'to', 'work'}, ...
 
        'YTickLabel', ...
 
        roundn(linspace(pi, 2*pi, 9), -2))
 
</source>
 
where the <code>round(x,n)</code> command rounds <math>x</math> to the nearest <math>10^n</math>.
 
-->
 
  
 
== Questions ==
 
== Questions ==

Latest revision as of 13:52, 17 October 2022

This page will primarily go over some examples of different ways to plot data sets. It assumes the lines

import math as m
import numpy as np
import matplotlib.pyplot as plt

are in the program.

Introduction

For a good tutorial on using the matplotlib.pyplot module - especially the object-oriented approach, see Python Plotting With Matplotlib (Guide) by Brad Solomon as recommended by Dr. Mark Palmeri, Duke BME.

Note that commands given as plt.COMMAND() may sometimes apply to either a figure or a set of axes or both. In November of 2019, plot infrastructure on this page was formalized to independently create the figure and one or more handles to axes. The primary reason for this is that the formal method is required in order to create 3D plots and so practicing it with 2D plots will prepare programmers for all cases.

Notes

In Fall of 2022, it looks like figures may not update correctly unless (a) saved, (b) resized, (c) explicitly told to update, or (d) interactive plotting is turned on. That is:

  • Saving the figure with
fig.savefig("FILE.EXT")
  • Resizing the window using the mouse
  • Explicitly updating with
fig.canvas.draw()
  • Turning interactive plotting on with
plt.ion()

This is with Spyder 5.2.2.

Simple Examples

If you are here to see some basic ways to get plots to work, this is the section for you! The main steps for creating most (relatively simple) plots are:

  • Import appropriate modules
  • Create figure and axis handles
  • Make plot
  • Add title, labels, etc.
  • Save the plot

Note also that depending on your development environment, graphics may show up in their own window or in the console. These examples generally assume you are having graphics show up in their own window - it makes it much easier to make alterations to graphs after their initial creation. If you are using Spyder, the way to get graphs to show up in their own window is:

  • Within the Tools menu select Preferences
  • From the Preferences list on the left select IPython console
  • In the right window, click the Graphics tab
  • In the second section (Graphics backend), select Automatic
  • Click the OK button at the bottom right of the window.

Plotting a few points

Here is a complete example that will generate and save a single figure window with a single set of axes within it:

Example1 PunPlot.png
# Imports
import matplotlib.pyplot as plt
# Data 
x = [1, 2, 4]
y = [2, 1, 5]
# Set up figure and axis
fig = plt.figure(num=1, clear=True)
ax = fig.add_subplot(1, 1, 1)
# Plot using red circles
ax.plot(x, y, 'ro')
# Set labels and turn grid on
ax.set(xlabel='x', ylabel='y', title='Three Points')
ax.grid(True)
# Use space most effectively
fig.tight_layout()
# Save as a PNG graphics file
fig.savefig('Example1.png')

Note that the plt.figure(num=1, clear=True) line does not work correctly in some versions of matplotlib - the way you will detect the problem is if you re-run the code and there either looks to be two different plots on top of each other or your current plots takes up less of the screen and the text gets dark. If that happens to you, look at the fix in the #Clearing_Figures section below.

Plotting a couple lines

This example uses the numpy module to create an array and apply trig and basic math to that array. It also shows how to use the label kwarg in concert with the legend method to produce a legend. Finally, notice in the ax.set() method that there is an r in front of the ylabel text. If you want to use basic \(\LaTeX\) commands in labels, titles, or text, you need to pass them in raw strings. Finally, the markevery command allwys you to specify how often to put a data marker when plotting a data set.

Example2 PunPlot.png
# Imports
import matplotlib.pyplot as plt
import numpy as np
# Data 
t = np.linspace(0, 2*np.pi, 101)
x1 = 1 + 2 * np.cos(t)
x2 = 2 + 1 * np.sin(t)
# Set up figure and axis
fig = plt.figure(num=1, clear=True)
ax = fig.add_subplot(1, 1, 1)
# Plot using red circles
ax.plot(t, x1, 'gs-', label='First Line', markevery=10)
ax.plot(t, x2, 'bd:', label='Second Line', markevery=20)
# Set labels and turn grid on
ax.set(xlabel='Time $t$, sec', ylabel=r'Value $\xi$, m', title='Two Lines')
ax.grid(True)
ax.legend(loc='best')
# Use space most effectively
fig.tight_layout()
# Save as a PNG file
fig.savefig('Example2.png')

The plt.plot() Function

The plt.plot() function is used to plot sets of data on a 2-D grid. What follows comes from matplotlib.pyplot's help function (some paragraphs have been snipped out). The line styles, symbols, and colors are formatted as a clearer table.

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 (and) the following color abbreviations are supported:
Line code Meaning
- solid
: dotted
-. dash-dot
-- dashed
Symbol code Meaning Symbol code Meaning
. point s square
, pixel p pentagon
o circle * star
v triangle (down) h hexagon 1
^ triangle (up) H hexagon 2
< triangle (left) + plus
> triangle (right) x x-mark
1 tri (down) D diamond
2 tri (up) d thin diamond
3 tri (left) vline
4 tri (right) _ hline
Color code Meaning
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).

The plt.savefig() Function

The plt.savefig() function is used to save a plot to a file. The type of file is determined by the extension of the file name. For example,

plt.savefig('blah.png')

will save the file to a portable network graphics file, while

plt.savefig('blah.pdf')

will save it to a PDF. From the help file for the command, "Most backends support png, pdf, ps, eps and svg." If you have created a figure handle, you should use the safefig on that handle - that is, use

fig.savefig('blah.png')

instead of using plt.

Example

Example showing a figure split into 2x3 subplots with subplot 3 holding an empty axis and subplot 4 holding a graph of the unit step function.

The following Python code demonstrates how to fill the fourth window of a 2x3 plot figure and save the figure as a PNG file; the resulting figure is in the thumbnail at right.

# Import required modules
import numpy as np
import matplotlib.pyplot as plt

# Create a 100 numbers between -1 and 1
x = np.linspace(-1, 1, 100); 

# This formula for usf is one of several ways to define the unit step function
def usf(t):
    return (t>=0)*1.0; 

# Create a figure with two rows of three figures
fig = plt.figure(num=1, clear=True)
ax = fig.subplots(2, 3)

# Plot the values of the function usf(x) against the vector x in bottom left
ax[1][0].plot(x, usf(x)) 

# Change the axes so the function is not covered by the subplot box
ax[1][0].axis([-1, 1, -1, 2])

# Set labels and title
ax[1][0].set(title='Unit Step Function', xlabel='x', ylabel='u(x)')

# Clear the rest other than bottom left and top right
fig.delaxes(ax[0][0])
fig.delaxes(ax[0][1])
fig.delaxes(ax[1][1])
fig.delaxes(ax[1][2])

# Issue tight layout to fix label overlaps
fig.tight_layout()

# Send the current figure to a file named usfplot.png
fig.savefig('usfplot.png')

Example Redux

Example showing a figure split into 2x3 subplots with subplot 3 holding an empty axis and subplot 4 holding a graph of the unit step function.

The following Python code demonstrates how to fill the fourth window of a 2x3 plot figure and save the figure as a PNG file; the resulting figure is in the thumbnail at right. In this case, instead of creating 6 subplots and deleting four of them, the code below only adds the subplots we want.

# Import required modules
import numpy as np
import matplotlib.pyplot as plt

# Create a 100 numbers between -1 and 1
x = np.linspace(-1, 1, 100); 

# This formula for usf is one of several ways to define the unit step function
def usf(t):
    return (t>=0)*1.0; 

# Create a figure 
fig = plt.figure(num=1, clear=True)

# Create axes for panels 3 and 4 in a 2x3 configuration
ax3 = fig.add_subplot(2, 3, 3)
ax4 = fig.add_subplot(2, 3, 4)

# Plot the values of the function usf(x) against the vector x in bottom left
ax4.plot(x, usf(x)) 

# Change the axes so the function is not covered by the subplot box
ax4.axis([-1, 1, -1, 2])

# Give the current subplot a title
ax4.set_title('Unit Step Function')

# Set the x-label
ax4.set_xlabel('x')

# Set the y-label
ax4.set_ylabel('u(x)')

# Issue tight layout to fix label overlaps
fig.tight_layout()

# Send the current figure to a file named usfplot.png
fig.savefig('usfplot.png')

Setting Up Subplots

See Python:Plotting/Subplots for more on setting up subplots.

Python Settings

For this course, you will generally want to have your graphics set to automatic; to make this change in Spyder:

  • Open the preferences window
    • On Windows, go to the Tools menu near the top right of the window and select Preferences
    • On MACs, go to the python menu near the top left of the window and select Preferences
  • In the Preferences window at the left select IPython console
  • In the right half of the Preferences window, select the Graphics tab
  • In the Backend pulldown, select Automatic
  • Click OK in the Preferences window

General Plotting Tips

You must make sure that your data sets are presented properly. Here are some guidelines:

  • Include axis labels that have, when appropriate, units. You should also include a description of the variable, or the variable itself, or both. For example, on p. 285 of Chapra[1], there is a plot of force versus velocity. Appropriate x axis labels would include any of the following:
ax.set(xlabel='$v$, m/s')
ax.set(xlabel='$v$ (m/s)')
ax.set(xlabel='Velocity, m/s')
ax.set(xlabel='Velocity (m/s)')
ax.set(xlabel='Velocity ($v$), m/s')
ax.set(xlabel='Velocity ($v$, m/s)')
Note that you can use basic $$\LaTeX$$ commands (Greek letters, superscripts, etc) but if you do, you need to make sure the kwarg gets the raw version of the string; specifically, if you want to have the Greek letter $$\theta$$ and you try
ax.set(xlabel='$\theta(t)$, rad')
you will end up getting "heta(t), rad" since the "\t" is processed as a tab character. If you use:
ax.set(xlabel=r'$\theta(t)$, rad')
you will get the correct result.
  • You should be consistent within a single graph as far as which version of axis labels you use, and you should pick which format you believe conveys the information most efficiently and effectively.
  • Make sure your titles make sense. Typically, the title will read "DEP. VAR vs. INDEP. VAR for DESCRIPTION" where DEP. VAR is your dependent variable (typically on the y axis), INDEP. VAR is your independent variable (typically on the x axis), and DESCRIPTION is something that will differentiation this particular plot from others that might have the same variables (for example a data, or an experiment number).
  • Data points should be represented by symbols and model equations should be represented by lines. Be sure to use a legend to identify each item plotted if there are multiple data sets on the same graph.
  • Typically, you will want to set the axis limits such that no data point is on the figure boundary. Certainly you do not want a line to be plotted on top of a figure boundary. After you make a plot, if there is a data point on an edge, look at the current axes and go out a little bit. Just make sure if you end up fundamentally changing your code that you adjust the axes accordingly.

Using Different Line Styles

Most of the time, you will be plotting three or fewer different lines on a single window, and they can thus be distinguished from one another in Python by using different line styles: Note the legend command argument. The default location is upper right but that may land on the data. Telling it to use the best location will have Python look for the most-blank part of the plot. There are many other options for location - try help(plt.legend) and look at the section about loc in the Other Parameters.

Using Different Point Styles

Sometimes there will be more data sets on a graph than there are line styles in Python. In cases like this, you may think to use symbols at the points. The problem with that becomes clear if you have a large number of data points - you do not want to try to jam hundreds of symbols onto a curve. The left graph in the figure below shows what a disaster that could be. Instead, you can plot a line with all your data but then tell Python to plot points only every so often. This is done with the markevery kwarg. The right side of the figure shows the result of this operation.

Using Different Scales

In the above example, two different scales were used for the data sets - a refined scale for the line and a rougher scale for the data points themselves. In other cases, it is the line that will need the rougher scale. As an example, assume you want Python to numerically find the minimum of the function \(y=3x^2+11x-2\) using the built-in min command. To get the most precise answer possible, you will want to give Python a very large number of points - say, 1 million. That code might look like:

fun = lambda x: 3*x**2 + 11*x - 2
xVals = np.linspace(-3.0, 3.0, int(1e6))
yVals = fun(xVals)
yMin = min(yVals)
xMin = xVals[np.where(yVals==yMin)]
print(xMin, yMin)

Since the domain is 6 and there are 1e6 points, the spacing between points is approximately 6e-06. The x-coordinate of the answer then should be very close to the actual answer. In fact, Python determines that the minimum value is -1.208333333332658e+01 and its x-coordinate is -1.833334833334833e+00 - very close to finding the minimum of y=-1.208333333333333e+01 at x=-1.833333333333333e+00

The problem, however, is that if you want to graph this, one million points is somewhat absurd. Unless you are planning to zoom in on the graph, there are around 1000x as many points as necessary/realistic/useful for a plot like this. Instead, you should either recalculate the equation using a smaller set of independent data or plot only some of the data. Code for the former might be:

xPlot = np.linspace(-3, 3, 100)
yPlot = fun(xPlot)
fig = plt.figure(num=1, clear=True)
ax = fig.add_subplot(1, 1, 1)
ax.plot(xPlot, yPlot, 'k-')
ax.set(xlabel = 'x', ylabel = 'y', title = '$y=3x^2+11x-2$')
ax.grid(True)

while code for the latter might be:

fig.plot(xVals[::1000], yVals[::1000], 'k-')
ax.set(xlabel = 'x', ylabel = 'y', title = '$y=3x^2+11x-2$')
ax.grid(True)

The advantage of the first version above is that your domain definitely remains the same - the xPlot variable spans the same [-3, 3] as the xVals set even though it has three orders of magnitude fewer values. The disadvantage is that you will have to re-perform all the calculations on this new set.

The advantage of the second version, then, is that you are using prior data and thus do not have to recalculate anything. The disadvantage is, you might miss out on the tail end of the data. In the above example, slicing from the initial value to the end by taking every 1000 means the last value copied over will be 999001st (in index 990000). This means the maximum value used on the x-axis is xVals[999000] or approximately 2.994 instead of the 3 in the original. If that is a major concern, you should make sure the end of your refined data scale can be "reached" if your incremented indices start with 1 and have some increment. As an example, choosing to have 1000001 points in the original instead of 1000000 means going from 0 by 1e3 to the end will actually end at the 1000000th, and last, index.

Putting Text on a Plot

The ax.text(x, y, MyText) command will plot the text string MyText at the (x,y) coordinate. There are several important aspects to the text command you must remember to use it properly.

First, the text command will not make the figure change its axes to fit the data automatically. Try typing:

fig = plt.figure(num=1, clear=True)
ax = fig.add_subplot(1, 1, 1)
ax.text(2, 2, 'Hi')

The figure will try to show the word Hi in the top right but it will not be in the axis limits. You need to change the axis limits by using the axis command. In this case,

ax.axis((0, 3, 1, 4))
ax.text(2, 3, 'Hi 2')

will make the x axis go from 0 to 3 and the y axis go from 1 to 4, more than enough space to say "Hi 2" at (2,3)!

Next, note that text placed on plots will stay until the axes are cleared, so you can use multiple ax.text commands to get several labels on one plot. You can also use the fig object to place text - in this case, the coordinates for the x and y values will be numbers between 0 and 1 where 0,0 is the bottom left of the figure and 1,1 is the top right.

Updates / Comments

Clearing Figures

The clear kwarg in plt.figure() or plt.subplots() does not always seem to work with older versions of matplotlib. Sometimes, future calls to plt.subplots() and fig.tight_layout() will cause the active area of the plot to become smaller and also will cause the axes to be redrawn without removing the old axes. If you have that problem, you will need to replace

fig = plt.figure(num=1, clear=True)

with

fig = plt.figure(num=1)
fig.clf()

to explicitly run the clf method on the new (or old) figure.

Questions

Post your questions by editing the discussion page of this article. Edit the page, then scroll to the bottom and add a question by putting in the characters *{{Q}}, followed by your question and finally your signature (with four tildes, i.e. ~~~~). Using the {{Q}} will automatically put the page in the category of pages with questions - other editors hoping to help out can then go to that category page to see where the questions are. See the page for Template:Q for details and examples.

External Links

References