Utilizing matplotlib plots in LaTeX documents via pgf export - and how to automate it

26 Jun 2025 - tsp
Last update 26 Jun 2025
Reading time 5 mins

We have all been there - writing up a paper or a thesis in our favorite typesetting system (i.e. the only sane one to use for any serious publication - LaTeX). We build up our text via appropriate annotations, apply our layout classes, write our math just as easy as it gets - but then we have to include figures that we plot our own. We would like to use consistent styling (same fonts, same font sizes, same styles, etc.) but somehow just rendering our figures in an external program and inserting them using the traditional includegraphics does not do the trick - the labels are off, the fonts not the same, any change in scaling destroys consistency. And we have to manually rebuild our graphics whenever something changes. Would it not be nice to have an automated and simple way of generating our graphics? There is a pretty nice one when already utilizing a Makefile to build your document and using Pythonโ€™s matplotlib to generate your graphics indeed. Itโ€™s using the pgf file format.

How to

The matplotlib script

For simplicity letโ€™s assume we want to draw a simple sine- and cosine wave in our document. So letโ€™s first write a simple matplotlib script that does that - but use the pgf backend and file format to store our result. We call this sinecosine.py (note that our Python file should have the same name as the pgf output - just a different file ending. We will not enforce this in this example though).

# Before importing the plt object we have to set the PGF backend

import matplotlib as mpl
mpl.use('pgf')

# Then do imports as usual

import matplotlib.pyplot as plt
import numpy as np

# Our parameters

f = 10 # We do calculation at 10 Hz
omega = 2 * np.pi * f
t = np.linspace(0, 1, 1000) # One second period with 1000 sampling steps
A = 1.0 # Amplitude

f1 = A * np.sin(omega * t)
f2 = A * np.cos(omega * t)

fig,ax = plt.subplots()
ax.plot(t, f1, label = "Sine")
ax.plot(t, f2, label = "Cosine")
ax.grid()
ax.set_xlabel("Time [s]")
ax.set_ylabel("Function value [arb.]")
ax.legend()

plt.savefig('sinecosine.pgf', format = 'pgf')

This will generate the following graph for us:

The Makefile

The next step is the Makefile that we utilize to build our document. A very simple Makefile for GNU make to build a document could look like the following:

LATEX_FLAGS = -halt-on-error -interaction=nonstopmode
LATEX = pdflatex
BIBTEX = bibtex

FIGURES_DIR := figures
FIGURES_SCRIPTS := $(wildcard $(FIGURES_DIR)/*.py)
PGF_FILES := $(FIGURES_SCRIPTS:.py=.pgf)

all: main.pdf clean

.phony: clean cleanall graphics

graphics: $(PGF_FILES)

$(FIGURES_DIR)/%.pgf: $(FIGURES_DIR)/%.py
	@echo "Generating $@ from $<"
	cd $(FIGURES_DIR) && python3.11 $(<F)

main.pdf: main.tex main.bib graphics

	$(LATEX) $(LATEX_FLAGS) main.tex
	-$(BIBTEX) main
	$(LATEX) $(LATEX_FLAGS) main.tex
	$(LATEX) $(LATEX_FLAGS) main.tex

clean:

	rm -rf *.aux *.bbl *.blg *.log *.out *.toc *.lof *.lot *.ist *.glo *.acn

cleanall: clean

	rm -rf main.pdf
	rm -rf $(FIGURES_DIR)/*.pgf

As one can see in this Makefile Iโ€™ve defined an environment variable that describes where figures are stored (FIGURES_DIR). From this I derive a list of Python scripts called FIGURES_SCRIPTS as well as the associated pgf filenames in PGF_FILES

FIGURES_DIR := figures
FIGURES_SCRIPTS := $(wildcard $(FIGURES_DIR)/*.py)
PGF_FILES := $(FIGURES_SCRIPTS:.py=.pgf)

A simple wildcard rule describes how to build the pgf files by executing the Python script - in my case with python3.11. As soon as a pgf file from the FIGURES_DIR is occuring as a dependency in any target the following rule is executed to build the pgf file:

$(FIGURES_DIR)/%.pgf: $(FIGURES_DIR)/%.py
	@echo "Generating $@ from $<"
	cd $(FIGURES_DIR) && python3.11 $(<F)

As a last step Iโ€™ve added a simple rule graphics that depends on all known pgf files that are generateable from the Python files in the figures directory. This allows for a more readable Makefile and our main rule for main.pdf depends on it:

graphics: $(PGF_FILES)

This way the Makefile only invokes the Python scripts whenever one of the script files has changed to save compilation time which is rather crucial for larger documents.

The remaining part of the Makefile is just standard way to build a TeX document. Multiple invocations of pdflatex and a matching bibtex call are used to generate the indices and the resulting document. An accompanying clean rule (that is .PHONY is it gets executes even in case nothing has changed) allows to delete all intermediate files. We do not delete our generated pgf files of course though we do not store them in out git repository.

How to include in LaTeX

Now to the last part is to utilize the graphics inside our LaTeX document. This is very simple. We utilize the pgf libraries:

\usepackage{pgf}
\usepackage{pgfkeys}
\usepackage{pgfmath}

At the appropriate position we just include the figure file:

\begin{figure}
    \input{figures/sinecosine.pgf}
    \caption{Our sine and cosing PGF rendering}
    \label{fig:sinecosine}
\end{figure}

A complete sample

To sum up here is a complete sample available as a GitHub GIST:

Conclusion

The above example has shown a very simple and scaleable way on how to automatically generate consistent plots for your LaTeX document in a very simple and easy way. It again shows the power and flexibility of toolchains like LaTeX, GNU make and Pythons matplotlib.

This article is tagged:


Data protection policy

Dipl.-Ing. Thomas Spielauer, Wien (webcomplains389t48957@tspi.at)

This webpage is also available via TOR at http://rh6v563nt2dnxd5h2vhhqkudmyvjaevgiv77c62xflas52d5omtkxuid.onion/

Valid HTML 4.01 Strict Powered by FreeBSD IPv6 support