2.2 Matplotlib: Visualization with Python

References

Matplotlib.org

W3 Schools Numpy Intro

Here are some of the plots that can be rendered with matplotlib.

image.png

2.2.0 Review Numpy arrays used in Matplotlib

There are several ways to generate arrays we use in matplotlib. Often we need to create an evenly spaced x axis for our graph. Here are two numpy methods to create a one dimensional array

  • x=np.arrange(5): This creates a one dimensional array of 5 integers starting at 0 and ending at 4.
  • x=np.arrange(1,10,.5): By adding three arguments, the starting value is 1, the stop value is 10 and the stepsize=.5 In this format, the first element will be 1.0, while the last element will be 4.5. We say that the array is inclusive of the start value, but exclusive of the end value.
  • x=np.linspace(START, STOP, NUMBER): START is the starting value in the array. The array includes the STOP value, and there are NUMBER of elements in the array.
In [7]:
# Ex2.2.0
import numpy as np
x=np.arange(5)
print(x,'\n\n')
x=np.arange(1,10,.5)
print(x,'\n\n')
x=np.linspace(0, 10, 5)
print(x,'\n\n')
[0 1 2 3 4] 


[1.  1.5 2.  2.5 3.  3.5 4.  4.5 5.  5.5 6.  6.5 7.  7.5 8.  8.5 9.  9.5] 


[ 0.   2.5  5.   7.5 10. ] 


Specialized arrays

We can generate an array of random numbers using the following:

  • x=np.random.randint(LOW, HIGH, SIZE):This method will draw pseudorandom integers in the range from LOW to HIGH. SIZE is the size of the array. SIZE can be an integer or a tuple (for multidimensional arrays of random integers. If we want to generate an array of logarithms to create a log-log plot, for example, we can use:
  • np.log(START, STOP, NUM) The first number in the array will be $10^{START}$ , while STOP (inclusive) is $10^{STOP}$ . SIZE defaults to 50, but it can be specified. In the example no value is given for SIZE so the default is used.
In [13]:
# Ex2.2.0
# generating a one dimensional array
import numpy as np
x=np.random.randint(1, 50, 20)
print(x,'\n\n')
# generating a two dimensional array: here we generate 2 rows and 5 columns
x=np.random.randint(1, 50, (2,5))
print(x,'\n\n')
x=np.logspace(0,2,50)
print(x)
[ 5 36 34 37 14  5  6 14 19 24  1  9 34  9 47 48 35  6 34 12] 


[[ 7  5 33 48 29]
 [34 24 15 12 22]] 


[  1.           1.09854114   1.20679264   1.32571137   1.45634848
   1.59985872   1.75751062   1.93069773   2.12095089   2.32995181
   2.55954792   2.8117687    3.0888436    3.39322177   3.72759372
   4.09491506   4.49843267   4.94171336   5.42867544   5.96362332
   6.55128557   7.19685673   7.90604321   8.68511374   9.54095476
  10.48113134  11.51395399  12.64855217  13.89495494  15.26417967
  16.76832937  18.42069969  20.23589648  22.22996483  24.42053095
  26.82695795  29.47051703  32.37457543  35.56480306  39.06939937
  42.9193426   47.14866363  51.79474679  56.89866029  62.50551925
  68.6648845   75.43120063  82.86427729  91.0298178  100.        ]

A Simple First Plot (Ex2.2.1)

The first example is a simple plot plot in which we plot a cubic polynomial $y=x^3$ versus $0 \le x \le 2$ . image.png We can add additional arguments to plt.plot(c=‘color_string’,linestyle=‘style_string' )

The basic colors are:'b' as blue 'g' as green 'r' as red 'c' as cyan 'm' as magenta 'y' as yellow'k' as black'w' as white.

Simple line styles can be defined using the strings "solid", "dotted", "dashed" or "dashdot".

Experiment: In Ex2.2.1 try the following:

  • In line 7 change the color of the plot
  • In line 7 change the line style
In [3]:
# Ex2.2.1
import matplotlib.pyplot as plt
import numpy as np
    # Creates Numpy array for x values
x = np.linspace(0, 2, 100)
    # Computes y(x) for each graph
plt.plot(x, x**3, label='cubic')
    # creates labels of the two axes
plt.xlabel('x')
plt.ylabel('y')
    # creates a title
plt.title("plotting $y=x^3$ ")
    # displays the final plot
plt.show()

Ex 2.2.2 Adding Multiple Plots to a Single Axis

image.png Experiment: In Ex2.2. try the following:
Change this example to plot two line-plots, one for sin(x) and the second for sin(x)cos(x). let x range from 0 to 2np.pi. Change the labels for the graphs. NOTE: The legend box is automatically relocated to the ‘best’ position so as not to interfere with the lines drawn.

In [10]:
# Ex2.2.2
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100)
# Computes y(x) for each graph
plt.plot(x, x, label='linear')
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')
# creates labels of the two axes
plt.xlabel('x label')
plt.ylabel('y label')
# creates a title
plt.title("Comparing Linear, Quadradic, and Cubic Plots")
# creates the legend box in upper left to identify each of the graphs
plt.legend()
# displays the final plot
plt.show()

Ex 2.2.3 and Ex2.2.4 Lissajous Diagrams

In these two examples we will introduce plt.figure(figsize=(5,5)). (line4). The argument figsize is given by a tuple (W,H) where the width W and the height H are specified in inches. HOWEVER, the actual display depends on the device on which the images will be displayed. This difference occurs because Jupyter notebook is set for 782 dpi, while your monitor is likely different. If you wish, you can determine the scaling factor. On my monitor the scaling is 0.5. So, if I want a plot if 5 in by 5 in, I need to specify 10 by 10.

Other differences in this example is that we are specifying the x and y coordinates as a function of a parameter t. Plotting a sine wave and a cosine wave on x and y axes will produce a circle—one of a class of Lissajous diagrams. Remember, you must use np.sin( ) and np.cos( ) and np.pi in order to specify the functions.

In Ex 2.2.4 we display the Lissajous Diagram for two sine waves of differing frequencies. The ratio of the number of lobes in the Lissajous diagram in the x:y direction is the ratio of the frequencies. 2:3 on the horizontal (x):vertical(y)

In [15]:
# Ex2.2.3
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(5,5))
t=np.linspace(0,np.pi,200)
x=10*(np.sin(2*t))
y=10*(np.cos(2*t))
plt.plot(x, y)
plt.show()
In [31]:
# Ex2.2.4
import matplotlib.pyplot as plt
import numpy as np
plt.figure(figsize=(5,5))
t=np.linspace(0,6*np.pi/3,200)
x=10*(np.sin(3*t))
y=10*(np.sin(2*t))
plt.plot(x, y)
plt.show()

2.5 Multiple Plots on a Single x-y Coordinate Axis

The following example and explanation is adapted from a matplotblog by Tejas Sanap. Consider the following problem from physics. Suppose we want to plot the velocity and the displacement of an object as it falls in earth’s gravitational field (). The equations of motion become: $$v=gt$$ $$d=.5gt^2$$ image.png Notice that using a common y axis scale, we compress the velocity plot. It would be nice to be able to include a y-axis for displacement and one for velocity.

In [23]:
# Ex2.2.5
import matplotlib.pyplot as plt
import numpy as np
g=9.8
t=np.linspace(0,10,100)
v=g*t
d=.5*g*t**2
plt.figure(figsize=(9,7))
plt.plot(t,d,c='b')
plt.plot(t,v,c='r')
plt.xlabel('time')
plt.ylabel('distance and velocity')
plt.grid(True)
plt.show()

Object Hierarchy and controlling figures

matplotlib is based on an object hierarchy. The BIGGEST PROBLEM in getting started with matplotlib is to confuse Axis with Axes. In this context, Axes is not the plural of Axis. fig_archit.jpg The relationship among matplotlib objects is shown in the preceeding diagram. Let’s start with what is called an Axes. The Axes is comprised of a line2D, Text, XAxis, and YAxis. In fact, there may be more than one Axes as a child of Figure. Likewise, each of the sub objects of Axes may have multiple instances. Think of the last example in which both the velocity and distance are plotted. Each Axis (X and Y) have labels. Each label has ticks.

Ex2.2.6 Two Plots Side by Side

Returning to the physics problem described earlier, we realize that in order to have a better looking rendition of the data, we should plot distance and velocity on two separate Axes, each with a different Axis, and scale. We can use one Figure containing two Axes. How do we accomplish this?. image.png

In [1]:
# Ex2.2.6
import matplotlib.pyplot as plt
import numpy as np
g=9.8
t=np.linspace(0,10,100)
v=g*t
d=.5*g*t**2
# Creates an axes ax1 in a figure named fig
fig, ax = plt.subplots(1,2)
ax1=ax[0]
ax2=ax[1]
# Creates the labels for each Axis associated with ax1
ax1.set_ylabel("distance (m)")
ax1.set_xlabel("time")

# Creates the labels for each Axis associated with ax2
ax2.set_ylabel("velocity (m/s)")
ax2.set_xlabel("time")
# Plots the data for d in ax1 and the data for v in ax2
ax1.plot(t, d, c='b')
ax2.plot(t, v, c='g')
# Set display parameters
fig.set_size_inches(10,5)
plt.show()

Ex2.2.7 Two Plots with a Common x_axis but Different y_axis

Although we will eventually create 2 axes objects, we begin with creating a single object ax1 (line 9). Notice that the arguments of the subplots() function is null. That means that we get the default of a single axes object, which we name ax1. After we set the labels for each axis object, we use the method .plot applied to the ax1 object to associate the distance data with the curve in ax1.

At this point we do something different. In line 16 we create a clone of ax1 which we define to be ax2. When we set the ylalbel for ax2 (line 18), we create a twin in which the two axes share the same x_axis, but each has a different y_axis.

After we add data to ax2, set the figure size, we than display the plot.

In [5]:
# Ex2.2.7
import matplotlib.pyplot as plt
import numpy as np
g=9.8
t=np.linspace(0,10,100)
v=g*t
d=.5*g*t**2
# Creates an axes ax1 in a figure named fig
fig, ax1 = plt.subplots()
# Creates the labels for each Axis associated with ax1
ax1.set_ylabel("distance (m)")
ax1.set_xlabel("time")
# Plots the data for distance
ax1.plot(t, d, c='b')
# Creates an axes ax2 which is a twin of ax1
ax2 = ax1.twinx() 
# Creates the labels for each Axis associated with ax2
ax2.set_ylabel("velocity (m/s)")
ax2.plot(t, v, c='g')
# Set display parameters
fig.set_size_inches(7,5)
plt.show()

Ex 2.2.8 Scatter Data

The data table displays sales of ice cream as a function of the temperature. In this example we create a scatter plot of this empirical data. This example is taken from https://www.mathsisfun.com/data/scatter-xy-plots.html image.png In this example the emperical data is entered into two lists T and S. After setting the labels, we specify that we want to plot a scatter plot by using plt.scatter(T,S). In this example, we are HIDING THE DETAIL OF THE FIGURE OBJECTS. They are being generated by default behind the sceens. We could, however, have greater control over the plots by defining the axes, etc. In this case, our plot is simple, and we need not do this.

Compare lines 5 in this example: plt.xlabel('Temperature degrees C')
to line 12 in the preceeding example: ax1.set_xlabel("time")
In Ex 2.2.8 we have defined ax1 and are explicitly adding the xlabel as a object to the x-axis object. In Ex 2.2.9 this is hidden. The programmer needs to decide which is more important simplicity over greater control of the graphic display.

In [42]:
# Ex 2.2.9
import matplotlib.pyplot as plt
T=[14.2,16.4,11.9,15.2,18.5,22.1,19.4,25.1,23.4,18.1,22.6,17.2]
S=[215,325,185,332,406,522,412,614,544,421,445,408]
plt.xlabel('Temperature degrees C')
plt.ylabel('Ice Cream Sales in $')
plt.scatter(T,S)
plt.show()

Plotting Histograms

In the last example we plotted empirical data which was stored in two lists. In this example we want to plot a histogram of a finite sample drawn from a normally distributed random variable. For that we can use the following Numpy function: np.random.normal ( ). There are three key arguments: the mean, standard deviation and the sample size. In Ex 2.2.10 we set the mean and the standard deviation in lines 5 & 6. We want to plot a series of histograms for different sample sizes: 500,1000,1500, and 2000. Thus, we generate the sampled histogram data and the histogram plotting function embedded in a for loop with nsize in range(500.2001,500). Why 2001? Recall that the end value of the range function is exclusive while start number is inclusive. Thus, the last value of nsize that will be executed in the loop is 2000.

We are using the plt.hist() fuction which has a first argument as the data passed to the function—this was generated in line 8. While we have not talked about arguments in functions like plt.hist( ), if you were to look at the documentation of this function (or any method embedded in python and its packages, you will find a rather daunting list of arguments. The definition of these functions uses the keyword argument **kwargs. From the point of view of the user, any argument that the user wants to specify is included at the end of the argument list; in this case bins=50, color=‘b’ . The function replaces the default value of the bins and color variable with the value specified. The keyword bins stands for the number of bars that will appear in the histogram plot. Data is collected into these bins, before plotting. The larger the number of bins, the smoother the plot. Incidentally, using color =‘b’ is the same as c=‘b’ an also color=‘blue’. More on color later.

Experiments with Ex 2.2.10

  • Try changing the bin size
  • The equation for the normal distribution is given by $f(x)=\frac{1}{\sigma\sqrt{2\pi}}e^{-\frac{1}{2}\big(\frac{x-\mu}{\sigma}\big)^2}$ . For each of the plots generated in 2.2.10 plot a second line plot of . Define a function to compute f(x) and call it to generate a second set of data for each plot. You can do this like the example Ex2.2.2.
In [2]:
# Ex 2.2.10
import numpy as np
import matplotlib.pyplot as plt
# Generate normally distributed random data
mu=170
sig=10
for nsize in range(500,2001,500):
    data = np.random.normal(mu, sig, nsize)
    plt.hist(data, bins=50, color='b')
    s1='sample size='+str(nsize)
    plt.title(s1)
    plt.show()
In [ ]:
 

2.2.11 3D line plot

In constructing 3-D plots we not only need to import numpy and pyplot, but we must import mplot3d from mpl_toolkits. even though there is no specific reference to this within the program we write, it is used by pyplot (plt) when we define an axes (ax) in line 12. that call to plt.axes has an argument projection=‘3d’ mpl_toolkits.mplot3d provides some basic 3D plotting (scatter, surf, line, mesh) tools. Not the fastest or most feature complete 3D library out there, but it ships with Matplotlib and thus may be a lighter weight solution for some use cases. Check out the mplot3d tutorial for more information.

In the next example we will graph a 3-D plot of a function spiraling in the xyz plane. Specifically that function is defined in lines 14 thru 16. First, z is defined as an array containing 100 points, from 0 to 100. Since z is an array, x and y will also be arrays.

z = np.linspace(0, 1, 100) x = z np.sin(25 z) y = z np.cos(25 z)

If z were to be a constant value, and we were to graph this function in the the x-y plane we would get a circle—a one lobe by one lobe Lissajous diagram (see Ex 2.2.3 . However, if we define an array of values for z using the linspace function from Numpy (line 13) the larger the value of z, the larger the radius of the circle. Now if we plot this in 3-space x,y,z we get a spiral.

In lines 19-21 we establish the labels for the each axis x,y, and z.

Finally, we plot the 3-D line plot. The first three arguments are the x,y, and z arrays defined in 14-16. Optionally, we set the color of the line to green.

Experiment with Ex 2.11

  • Try changing the figure size
  • Delete the specification of color. What does it default to?
In [9]:
# Ex2.2.11
# importing mplot3d toolkits and numpy
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d

# Setting the size of the figure 
plt.figure(figsize=(10,10))
 
# setting the axes for 3-D projection and defining the axes
ax = plt.axes(projection ='3d')

# generating the data
z = np.linspace(0, 1, 100)
x = z * np.sin(25 * z)
y = z * np.cos(25 * z)
 
# establishing the axis labels 
ax.set_xlabel('X', fontsize=20)
ax.set_ylabel('Y',  fontsize=20)
ax.set_zlabel('Z', fontsize=20)

# plotting
ax.plot3D(x, y, z, c='green')
ax.set_title('A spiral path in 3D')
plt.show()

2.2.12 3D Surface Plots

2.2.12.1 Intro to np.meshpoint ( )

meshgrid.jpg In order to plot surfaces we will need to define a mesh of (x,y) values above which we will plot z, the height of the surface. While meshpoint( ) does not generate a matrix of tuples which are the coordinates in the x-y plane, the output of the function creates a 2-D array to represent the values of x and a second 2-D array to represent the values of y. First let’s look at the way we will use the function.

x,y= np.meshgrid(x_value, y_value)

x_value and y_value are lists of values above which we want to plot the surface. In the example program below, both x_value and y_value are 3x1 arrays. The outputs of meshgrid( ) are two 3x3 arrays. Each row in the first array (see red array in figure) repeats the values in the array x_value, while each column (blue array) repeats the values in y_value as columns in the blue array. To the right of these two outputs of meshgrid is an explanation of how these two arrays are used to generate the meshpoints. NOTE: these are not INDICES, but VALUES of x & y respectively, at each mesh point. Line 12 defines z as the sum of x+y. Since x and y are both 3x3 arrays you would expect z to be a 3x3 array. The sum of x and y is done point by point thru the array. You can confirm the result from the statement print(z) line 14.

In [22]:
# demo describing np.meshpoint( )
import numpy as np
# define meshpoint in the x,y plane
x_value=(1, 2, 3)
y_value=(5, 6, 7)
x,y = np.meshgrid(x_value, y_value)
print('the shape of x =',np.shape(x))
print(x,'\n')
print('the shape of y =',np.shape(y))
print(y,'\n')
# define the surface to be plotted
z = x+y
print('the shape of z =',np.shape(z))
print(z)
the shape of x = (3, 3)
[[1 2 3]
 [1 2 3]
 [1 2 3]] 

the shape of y = (3, 3)
[[5 5 5]
 [6 6 6]
 [7 7 7]] 

the shape of z = (3, 3)
[[ 6  7  8]
 [ 7  8  9]
 [ 8  9 10]]

2.2.12.2 Example of Surface Plot

We begin by importing the appropriate packages. There is a new one here called cm from matplotlib. We will come back to describe why and how we use this package later.

Lines 6-9 are the same as we used in 2.2.12.1 . In this example, we will plot a more complex surface defined by:

In this problem since both x_value and y_value are 1x100 arrays, x,y, and z will each be 100x100 arrays.

Line 15 creates a figure named fig and establishes its size. Line 16 defines the axes (remember the difference between axes and axis) as a projection of a 3-d object. The surface plot is defined in line 19. It is given a name surf, and the three 100x100 arrays x,y, and z are arguments. The argument (which is an optional **kwargs is

cmap=cm.coolwarm The package we imported called cm from matplotlib contains a variety of color maps. Here are a few from that package.

image.png

Here are a few of a very large number of colormaps you can use in surface plots. Line 23 of the code places a colorbar mapping z values to the gradient of color. Critical to using this is to name the surface (line 19 defines the surface as surf)

Line 21 sets the title. We have added something new here. Notice the string enclosed between two dollar signs: $\cos(x^2+y^2)$. This indicates that this is LaTeX code. LaTeX is a typesetting language that we use to represent mathematical equations. It is built into many word processors. Using the backslash with cos \cos( ) is interpreted as the cosine function. The ^ represents raised to a power x^2 is $x^2$. For more information on LaTeX you can see http://drfresearch.com/index-other.html .

Line 24 of the code places a colorbar mapping z values to the gradient of color. Critical to using this is to name the surface (line 19 defines the surface as surf)

It is important to realize that the size of the grid you choose will impact the smoothness of the surface plot. Try changing lines 7 and 8 to have 100 points and finally 50 points.

Using the chart above, try different colormap names.

In [29]:
# Ex 2.2.12.2
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

# define meshpoint in the x,y plane
x_value=np.linspace(-2, 2, 1000)
y_value=np.linspace(-2, 2, 1000)
x,y = np.meshgrid(x_value, y_value)

# define the surface to be plotted
z = np.cos(x**2 + y**2)

# set up the figure and the axes
fig = plt.figure(figsize=(15,10))
ax = plt.axes(projection='3d')

# plot the surface
surf=ax.plot_surface(x, y, z,cmap=cm.coolwarm, edgecolor='none')
ax.set_title('Surface plot of $\cos(x^2+y^2)$ \n\n',fontsize=25)

# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
In [30]:
# Ex 2.2.13
from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

# define meshpoint in the x,y plane
x_value=np.linspace(-2, 2, 1000)
y_value=np.linspace(-2, 2, 1000)
x,y = np.meshgrid(x_value, y_value)

# define the surface to be plotted
z = np.cos(x**2 + y**2)

# set up the figure and axes
fig = plt.figure(figsize=(10,10))
ax = plt.axes()

# specify plotting for a contour map
cp=ax.contour(x,y,z, cmap=cm.coolwarm, levels=[-1,-.75,-.5,-.25,0,.25,.5,.75,1])
ax.clabel(cp, fontsize=9, inline=True)
ax.set_title('Contour plot of $\cos(x^2+y^2)$ \n\n',fontsize=25)

plt.show()

Contour Plots

In order to display contour plots, we need to import matplotliblpyplot and numpy (lines 2-3). Second, we need to create an array of values for the x and y axes. Each of these will be a 1D array. The third step is to create a 2-D grid for the x-y axis (line 10).Line 12 sets up the axes for the grid. The agrument (1,1) of plt.subplots establishes that there is one row and one column in the grid. Line 15 creates the Z dimension form the [X,Y] grid values.

In [ ]:
 

© Donald R. Falkenburg
Last updated 4/29/23

In [ ]: