import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.ticker as mticks
from PIL import Image
import copy
In this notebook, we will go over a few “dos” and “donts” for making plots.
# Set your RC Parameters in MatPlotLib
= copy.deepcopy(mpl.rcParams) rcParamBackup
# Here, I'm going to set the font size too small for illustration. We'll change it back later
'font.size'] = 7 mpl.rcParams[
= {}
data for key, filename in [
'x', 'X.csv'),
('y', 'Y.csv'),
('z', 'Z.csv')
(
]:# Data is stored on the disk in two columns.
# Later on, we will decompose into two variables based on
# two rows. Doing the transpose here with a .T makes it
# easier to do that later
= np.loadtxt(filename, delimiter=',').T data[key]
= plt.subplots(sharex=True, figsize=(12, 4))
fig, ax for key, (xdata, ydata) in data.items():
=key)
plt.loglog(xdata, ydata, label
'top'].set_visible(True)
ax.spines['right'].set_visible(True)
ax.spines['Integration time (s)')
ax.set_xlabel(='best')
ax.legend(loc'Allan deviation (dps)')
ax.set_ylabel(='both', ls='dotted') ax.grid(which
Many of the Allan deviations that I see in the literature look like this. There’s several problems:
- The text is way too small
- The graph is too wide. The important parts of the graph are on the bottom right, far away from the left legend
- The y-axis units take some time to parse (dps? Is that damage per second… no, this is a gyroscope… ahh, degrees)
- Lots of extra lines lead the eyes away from the data
= {'x': 'red', 'y': 'green', 'z': 'blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
=key, color=colordict[key])
plt.loglog(xdata, ydata, label
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (dps)')
ax.set_ylabel(2)
ax.set_aspect(
= 'foo.png'
fname plt.savefig(fname)
This chart is a bit better. The chart junk is gone, and we’ve set the aspect ratio to 2 so we can easily see the slope of -1/2 characteristic of ARW. Furthermore, we’ve also adopted a more standard color mapping for XYZ used in 3D graphics (x is red, y is green, z is blue). We can make some other refinements as we go along.
# opening image using pil
= Image.open(fname).convert("L")
image
# mapping image to gray scale
='gray')
plt.imshow(image, cmap'off')
plt.axis( plt.show()
This is how the graphic looks in grayscale. It’s not great, because the blue is dark, but the red and the green are rather light.
= {'x': 'tab:red', 'y': 'tab:green', 'z': 'tab:blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
=key, color=colordict[key])
plt.loglog(xdata, ydata, label
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (dps)')
ax.set_ylabel(2)
ax.set_aspect(
= 'foo.png'
fname plt.savefig(fname)
We can use the tableau color map to take a little bit of the saturation out. The colors look a little more professional and less cartoony
# opening image using pil
= Image.open(fname).convert("L")
image
# mapping image to gray scale
='gray')
plt.imshow(image, cmap'off')
plt.axis( plt.show()
Gray scale is still a little hard to see which color is which.
= {'x': 'xkcd:light red', 'y': 'xkcd:kiwi green', 'z': 'xkcd:mid blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
=key, color=colordict[key])
plt.loglog(xdata, ydata, label
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (dps)')
ax.set_ylabel(2)
ax.set_aspect(
= 'foo.png'
fname plt.savefig(fname)
# opening image using pil
= Image.open(fname).convert("L")
image
# mapping image to gray scale
='gray')
plt.imshow(image, cmap'off')
plt.axis( plt.show()
Yes, here we have set the green to be very light, and the blue to be very dark. They are now discernable in black and white. However, you may be better off just using dashed or dotted curves to differentiate.
= {'x': 'tab:red', 'y': 'tab:green', 'z': 'tab:blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
* 3600, label=key, color=colordict[key])
plt.loglog(xdata, ydata
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (deg/h)')
ax.set_ylabel(2)
ax.set_aspect(
= 'foo.png'
fname plt.savefig(fname)
= {'x': 'tab:red', 'y': 'tab:green', 'z': 'tab:blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
* 3600, label=key, color=colordict[key])
plt.loglog(xdata, ydata
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (deg/h)')
ax.set_ylabel(2)
ax.set_aspect('left'].set_position(('data', 1))
ax.spines[
= 'foo.png'
fname plt.savefig(fname)
In this graphic, we’ve scaled the y-axis to show it in deg/h. Furthermore, we’ve also moved the x-axis to be at the 10^0 vertical line, which more easily shows the location where the the line crosses the y-axis.
'font.size'] = 12
mpl.rcParams['figure.figsize'] = [5, 4]
mpl.rcParams[= {'x': 'tab:red', 'y': 'tab:green', 'z': 'tab:blue'}
colordict = plt.subplots(sharex=True)
fig, ax for key, (xdata, ydata) in data.items():
=key, color=colordict[key])
plt.loglog(xdata, ydata, label
'Integration time (s)')
ax.set_xlabel(='best', frameon=False)
ax.legend(loc'Allan deviation (°/s)')
ax.set_ylabel(2)
ax.set_aspect(10, numticks=30))
ax.xaxis.set_major_locator(mticks.LogLocator(=[2,3,4,5,6,7,8,9], numticks=100))
ax.xaxis.set_minor_locator(mticks.LogLocator(subs
'adev.svg') plt.savefig(
And here we’ve made the font size larger again. We’ll then export this as an SVG, make our annotations, and export again.
This is an improvement over