The problem
I would love to have production-ready plots directly from python.
The Solution
First of all download the following dependencies
pip install numpy
pip install matplotlib
pip install svglib
pip install reportlab
You also need to have latex installed on your system. Check the complete workflow for installation here.
We are now going to go step by step into understanding the complete workflow of building a plot for your scientific work.
- Figure: It is the complete image that you get out of matplotlib
- Axes: It is the actual plotting area as seen in the grey background in the picture below.
Selecting the proper plot size
It is very important to understand that figures and plots are a way of communicating your ideas and findings to a bigger community. Typically most of the research documents are made for an A4 size sheet of paper. The width of an A4 size paper is 210mm, take out the 30mm right and left margin and you are left with 150mm of usable space. That is the space that you should always use while designing your figures. Thus, you only have two width options
- 150mm if you are designing for full width
- 70mm if you are designing for a two-column layout
It is inside this space constraint that you have to draw. Thus, we will begin by setting up the space inside matplotlib
import matplotlib.pyplot as plt
import numpy as np
mm = 1/25.4 # millimeters in inches
fig = plt.figure(figsize=(70*mm,50*mm), dpi=200) # create a figure object
fig.subplots_adjust(left=0.1, right=0.9, top=0.75, bottom=0.15)
ax = fig.add_subplot(1, 1, 1) # create an axes object in the figure
plt.close() # Closes the plot in jupyter while creating axes
plt.figure
command will set the size of the figure and with the help of plt.subplots_adjust
we can adjust the padding around the plot as a percentage of the width and height of the figure. The arguments left
, right
, bottom
and top
are fractional units (of the total figure dimensions).
Tell matplotlib to use LaTeX
Now we will tell python to use latex fonts and also set the font size to 10. This is the font size that we use for the main content of our article. It is very important to have consistency in the font size and your figure font size should always
match your article font size.
plt.rcParams.update({
"text.usetex": True,
"font.family": "serif",
"font.size": 10,
"pdf.fonttype":42})
The pdf.fonttype
option will force python to use TrueType
fonts
Plot
Now the figure is ready to plot and save as a pdf. The figure will have the correct physical dimensions and the correct font size.
X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
C,S = np.cos(X), np.sin(X)
ax.plot(X, C, color="blue", linewidth=1.5)
ax.plot(X, S, color="red", linewidth=1.5)
Export the file to pdf
If you do not wish to edit your file you can now directly export it as a pdf using
fig.savefig("plot.pdf", format="pdf")
But, if you wish to edit your file in a vector-based editing program then the file generated from the above command would result in the wrong rendering of text. To prevent this we can save the figure as SVG and then convert that to pdf. Writing to SVG will convert the text to a path and thus it will render properly in a vector-based editing program. To do that use the following code
fig.savefig("plot.svg", format="svg")
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF
drawing = svg2rlg("plot.svg")
renderPDF.drawToFile(drawing, "plot.pdf")
Complete code
import matplotlib.pyplot as plt
import numpy as np
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPDF
plt.rcParams.update({
"text.usetex": True,
"font.family": "serif",
"font.size": 10,
"pdf.fonttype":42})
mm = 1/25.4 # millimeters in inches
fig = plt.figure(figsize=(70*mm,50*mm), dpi=200) # create a figure object
fig.subplots_adjust(left=0.1, right=0.9, top=0.75, bottom=0.15)
ax = fig.add_subplot(1, 1, 1) # create an axes object in the figure
plt.close() # Closes the plot in jupyter while creating axes
X = np.linspace(-np.pi, np.pi, 256,endpoint=True)
C,S = np.cos(X), np.sin(X)
ax.plot(X, C, color="blue", linewidth=1.5, linestyle="-", label="cosine",
zorder=-1)
ax.plot(X, S, color="red", linewidth=1.5, linestyle="-", label="sine",
zorder=-2)
ax.set_xlim(X.min()*1.1, X.max()*1.1)
ax.set_xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],
[r'$-\pi$', r'$-\pi/2$', r'$0$', r'$+\pi/2$', r'$+\pi$'])
ax.set_ylim(C.min()*1.1,C.max()*1.1)
ax.set_yticks([-1, +1],
[r'$-1$', r'$+1$'])
ax.legend(loc='upper left', frameon=True)
t = 2*np.pi/3
ax.scatter([t,],[np.sin(t),], 50, color ='red')
ax.annotate(r'$\sin(\frac{2\pi}{3})=\frac{\sqrt{3}}{2}$',
xy=(t, np.sin(t)), xycoords='data',
xytext=(-50, +30), textcoords='offset points',
arrowprops=dict(arrowstyle="->", connectionstyle="arc3,rad=-.2"))
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65 ))
fig.savefig("plot.svg", format="svg")
drawing = svg2rlg("plot.svg")
renderPDF.drawToFile(drawing, "plot.pdf")
References
- Text rendering with LaTeX
- Rendering math equations using TeX
- Ten Simple Rules for Better Figures
- Matplotlib tutorial for beginner
- Figure size in centimeter
- Working with Fonts in Matplotlib
- Generating PDFs from SVG input
- Matplot lib color demo
- Python Plotting With Matplotlib (Real python)
- Moving to object based plotting