📓 About This Tutorial

This webpage is a static view of an interactive Jupyter Notebook tutorial. To get the full interactive experience where you can run Python code, modify examples and complete exercises, click the "Open in Colab" button. This will access the Google Colab notebook from a GitHub repository github.com/dchappell2/Computational-Physics and open it in Google Colab. You will need a Google account if you want to open it in Colab.

Note: You can download a pdf of the lecture slides for this chapter here: Chapter 0-3 slides

Chapter 10 - 2D Plots Part 1

Goals:

10.0 Matplotlib and Coding Philosophy

The Python library Matplotlib offers two different approaches to plotting:

This chapter will only cover the state machine approach.

An excellent overview of these approaches can be found at the following link and should be required reading for anyone plotting in Python: https://matplotlib.org/2.0.2/faq/usage_faq.html

10.1 Simple Plot

We will use plotting commands in pyplot, a module within the Matplotlib library. To use this approach one typically imports matplotlib.pyplot as plt. This renames matplotlib.pyplot as simply plt, making it easier to work with.

🔆 Example: Line Plot

import matplotlib.pyplot as plt
import numpy as np

# define a numpy array of 300 x values from 0 to 50
# we use 1e-16 as the lower limit to avo
x = np.linspace(0, 2, 100)

# y = downward-opening parabola
y = x*(2-x)

# plot three functions of x, and define labels for each in the legend
plt.plot(x, y)

plt.xlabel('x')             # label the x and y axes
plt.ylabel('y')
plt.title("Simple Plot")    # give the plot a title

plt.show()                  # display the plot

10.2 Changing line and marker style

The line style can be modified in a few ways: (1) color, (2) solid/dashed/etc and (3) thickness.

Color and Line Style

The simplest way of specifying the line style and color is to include a specification string like this: 'r-', which means create a solid red line.

Here is a list of color specifications that can be included as a string:

Matplotlib has an extensive list of named colors like 'darkblue', 'plum' and 'salmon' in addition to being able to numerically specify the RGB (red, green, blue) components of any possible color. You can also adjust the transparency of the lines or markers. For more info see here: https://matplotlib.org/stable/gallery/color/named_colors.html and here: https://matplotlib.org/stable/users/explain/colors/colors.html

Here is a list of line style specifications that can be included as a string:

Here is a list of marker style specifications that can be included as a string:

🔆 Example: Line Style

# run previous code cell to define x and y arrays

plt.plot(x, y, 'r--')           # dashed red line

plt.xlabel('x')                 # label the x and y axes
plt.ylabel('y')
plt.title("Dashed Red Line")    # give the plot a title
plt.grid(True)                  # turn on grid
plt.show()                      # display the plot

✅ Skill Check 1

Create a plot of $y(x) = \sin (x^2) $ for $0\leq x \leq 10$. Plot the curve as a solid blue line. How many data points do you need to create a smooth curve?

# your code here

import matplotlib.pyplot as plt
import numpy as np

10.3 Changing Line Thickness or Marker Size

🔆 Example: Marker size

Here's an example of a plot with small, green circle markers.

x = np.linspace(0, 2, 100)           # create data to plot
y = x*(2-x)

plt.plot(x, y, 'go', markersize=2)   # small, green circle markers

plt.xlabel('x')                      # label the x and y axes
plt.ylabel('y')
plt.title("Circle Markers")          # give the plot a title
plt.show()                           # display the plot

10.4 Plotting Multiple Curves

To plot multiple curves, we can just call the plot command multiple times before the show() command is called.

🔆 Example: 3 curves on one plot

This example plots a linear, quadratic and cubic function on the same graph.

import matplotlib.pyplot as plt
import numpy as np

# define a numpy array of 100 x values from 0 to 2
x = np.linspace(0, 2, 100)

# plot three functions of x, and define labels for each in the legend
plt.plot(x, x,    label='linear')
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')

plt.xlabel('x')             # label the x and y axes
plt.ylabel('y')
plt.title("Three Curves")   # give the plot a title

plt.legend()                # display the legend

plt.show()                  # display the plot

🔆 Example: Plotting a theoretical curve through data points

Often, when you have experimental data points and a theoretical prediction, the data points are plotted at points (i.e. markers) and the theoretical prediction as a curve.

Here's an example where we generate some synthetic data points using the random number generator and plot the theoretical curve with a solid line.


N = 200

# create array of time values
t = np.linspace(0,2,N)

# Create synthetic data
rng = np.random.default_rng()
z = rng.normal(size=N)
data = 0.1*z + np.sqrt(t)

# define a theoretical curve
y = np.sqrt(t)

# plot three functions of x, and define labels for each in the legend
plt.plot(t, data, 'bo', ms=3,   label='data')
plt.plot(t, y, 'r-', label='theory')

plt.xlabel('t')                      # label the x and y axes
plt.ylabel('y')
plt.title("Theory and Experiment")   # give the plot a title

plt.legend()                # display the legend
plt.show()                  # display the plot

🔆 Example: Plotting data points with error bars

If you have estimates of the errors of your measured data points, you can draw error bars with the plt.errorbar() function. Here's an example of the syntax: plt.errorbar(x, y, yerr=y_err, fmt='o', capsize=3, label='data')

The following example adds error bars to the previous example.

N = 20

y_err = 0.1   # error bar

# create array of time values
t = np.linspace(0,2,N)

# Create synthetic data
rng = np.random.default_rng()
z = rng.normal(size=N)
data = 0.1*z + np.sqrt(t)

# define a theoretical curve
y = np.sqrt(t)

# We replace the plt.plot() command with plt.errorbar() do display error bars
plt.errorbar(t, data, yerr=y_err, fmt='o', capsize=3, label='data')
plt.plot(t, y, 'r-', label='theory')

plt.xlabel('t')
plt.ylabel('y')
plt.title("Theory and Experiment with error bars")

plt.legend()                # display the legend
plt.show()                  # display the plot

✅ Skill Check 2

Copy over your function from Chapter 9, that creates a noisy sine wave. Use the noisy sine wave function to create a noisy data data set with:

Code specifications:

# your code here

✅ Skill Check 3

When two sine waves with almost the same frequencies are added together, the result is the phenomena of beats. We'll assume each wave has the same amplitude and phase. Here's the sum of the two sine waves:

$$y(t) = \sin(\omega_1 t) + \sin(\omega_2 t)$$

The result is a sine wave whose amplitude is modulated with a cosine wave. The modulating cosine wave will have an amplitude of twice the component waves:

$$y_{envelope} = 2 \cos(\omega_3 t)$$

where $\omega_3 = |\omega_2-\omega_1|/2$.

import matplotlib.pyplot as plt
import numpy as np

# your code here

10.5 Multiple plots on one page

The subplot(n,m,k) command can be used to create a grid of subplots where:

The plt.tight_layout() command reduces the margins around the subplots.

🔆 Example: Subplots

In this example, we plot a sine curve in the upper plot and a cosine curve in the lower plot.

import matplotlib.pyplot as plt
import numpy as np

# define a numpy array of 200 values from 0 to 2
x = np.linspace(0, 20, 300)

# top plot
y = np.sin(x)
plt.subplot(2,1,1)     # 2 rows, 1 column, plot # 1
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('sin(x)')

# lower plot
y = np.cos(x)
plt.subplot(2,1,2)     # 2 rows, 1 column, plot # 2
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('cos(x)')

plt.tight_layout()     # reduce margins around the subplots
plt.show()

🔆 Example: More Subplots

We extend the previous example in several ways:

import matplotlib.pyplot as plt
import numpy as np

# create a function to plot
def plot_func(x,y,title):
    plt.plot(x,y,'b-',lw=.5)
    plt.fill_between(x, y, 0, color='lightblue', alpha=0.5)   # shade under curve
    plt.xlabel('x')
    plt.ylabel('y')
    plt.title(title)

# define a numpy array of 300 values from 0 to 1
L = 1
x = np.linspace(0, L, 300)

# Loop to create a grid of 6 subplots
for k in range(1,7):
    P = 2*L/k
    y = np.sin(2*np.pi*x/P)
    plt.subplot(2,3,k)         # create a 2x3 grid and place each subplot accordingly
    plot_func(x,y,f"n = {k}")  # label each subplot with number of modes

plt.tight_layout()
plt.show()

10.6 Adjusting the Figure Size

Sometimes you might want to change the size or aspect ratio of the plot to fit the data being plotted.

🔆 Example: Custom Aspect Ratio

Lets plot a sine wave with lots of periods. We'll want a long figure to match the data:

import matplotlib.pyplot as plt
import numpy as np

# define a numpy array of 200 values from 0 to 2
x = np.linspace(0, 100, 300)
y = np.sin(x)

plt.figure(figsize=(10,1.5))       # create figure with custom size
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Custom Aspect Ratio')

plt.show()

10.7 Plot limits

Matplotlib usually does a good job of setting the plot limits to reasonable values. However, you can also set the limits manually. For example, notice there's space before and after the sine wave above. If you want to remove that space, you can set the plot limits to 0 and 100.

If you want to ensure that the scaling in the x and y directions is the same, you can use plt.axis('equal'). An example of this is shown in the next section.

# define a numpy array of 200 values from 0 to 2
x = np.linspace(0, 100, 300)
y = np.sin(x)

plt.figure(figsize=(10,1.5))       # create figure with custom size
plt.plot(x,y)
plt.xlim(0,100)       # adjust x limits
plt.ylim(-1.5,1.5)    # adjust y limits
plt.xlabel('x')
plt.ylabel('y')
plt.title('Custom Plot Limits')

plt.show()

10.8 Parametric Plots

Rather than plotting $y(x)$ or $x(t)$, it is sometimes useful to plot $y(t)$ vs. $x(t)$, where both the $x$ and $y$ coordinates depend on a third variable $t$. The following shows an example

🔆 Example: Lissajous Figure

Lissajous figures are patterns where two sinusoidal (AC) signals with different frequencies $\omega_1$ and $\omega_2$ are fed into the x and y channels of an oscilloscope. If the frequencies can be expressed as a simple ratio, i.e 2:1, 3:2, etc. then the patterns form simple, closed solutions. Here are the equations:

where the angular frequences $\omega_1 / \omega_2 = n/m$ can be expressed as a ratio of two integers $n$ and $m$. $\phi_0$ is a phase constant between the two signals.

The following example shows a Lissajous figure for $\omega_1 / \omega_2 = 2$ with $\phi_0 = 0$.

Notes:

# define x and y as a function of the independent variable t
t = np.linspace(0, 2*np.pi, 300)

omega_1 = 1    # angular frequency of signal 1
omega_2 = 2    # angular frequency of signal 2
phi0 = 0      # phase difference

x = np.sin(omega_1 * t + phi0)
y = np.sin(omega_2 * t)

plt.figure(figsize=(5,5))
plt.plot(x,y)

# ensure x and y axes have same scaling
plt.axis('equal')

plt.xticks([])        # hide tick marks
plt.yticks([])

# fancy method to display frequency ratio and phase shift in degrees
phi0_deg = np.rad2deg(phi0)
plt.title(rf'{omega_1}:{omega_2}   $\phi_0$ = {phi0_deg:.0f}$^\circ$')

plt.show()

✅ Skill Check 4

Create a matrix of subplots that display Lissajous patterns for different frequency ratios $\omega_1/\omega_2$ and phase shifts $\phi_0$. Here are the specs:


phase = [0, np.pi/3, np.pi/2]   # phase offset

omega_1 = [1, 1, 2, 2, 3]       # angular frequency of signal 1
omega_2 = [1, 2, 1, 3, 4]       # angular frequency of signal 2

# your code here

10.9 Saving your plot to a file

You may want to save your file to include in a paper, report or presentation. The Matplotlib command plt.savefig() lets you save it in a variety of formats. You just need to specify the correct extension:

In each example, we specify the file resolution as 300 dpi, which is recommended for publication-quality plots. You can adjust the size of the plot in inches using the plt.figure(figsize=()) command.

```

from google.colab import files

files.download("sine_wave.pdf")

```

🔆 Example: Saving a figure

import matplotlib.pyplot as plt
import numpy as np
from google.colab import files

plt.figure(figsize=(6,4))       # create figure with custom size

# Create a simple plot
x = np.linspace(0, 2*np.pi, 300)
plt.plot(x,np.sin(x))
plt.xlabel('x')
plt.ylabel('y')
plt.title('Example:  Save to File')

# save figure to your computer
plt.savefig('my_fig.png',dpi=300)   # creates the file in your local directory
files.download("my_fig.png")        # downloads file from Google to your computer

plt.show()                          # this must come after plt.savefig()

✅ Skill Check 5

Include commands in Skill Check 4 to save the plot as a .png file. Make sure you are able to save it onto your computer.

10.10 Additional Plot Commands

Here are some additional plot commands. You can look up more info on their syntax using the links below.

```

plt.loglog(x,y) # log-log plot

plt.semilogx(x,y) # log-linear plot

plt.bar(x,y) # bar plot

plt.polar(phi,r) # polar plot

plt.hist(y) # histogram plot

```

10.10 Resources

Now you know the basics of plotting with Matplotlib. Here are a few resources to learn more: