Eine eingefügte Grafik in einem Aufsatz erkennt man meist auf einen Blick. Nicht weil die Kurve schlecht aussieht, sondern weil die Achsenzahlen in der falschen Schrift stehen. Das LaTeX-Werkzeug, das dies an der Wurzel behebt, heißt pgfplots: Es nimmt den Plot nicht als Bild entgegen, sondern baut ihn aus Zahlen mit derselben Satz-Engine wie den umgebenden Text. Diese Seite behandelt die pgfplots-Umgebung axis und \addplot, das Einlesen einer .dat-Datei, die Übergabe der Rechnerei an gnuplot sowie den Import von Abbildungen aus R oder Python samt Schriften. Unterwegs stehen die Fehlermeldungen, die TeX Live 2024 tatsächlich ausgegeben hat — die meisten, die hierher suchen, haben gerade eine davon vor sich.
Warum ein eingefügtes Bild nie ganz zur Seite passt
Der Grund ist nicht die Auflösung, sondern Schrift und Strichstärke. Tabellenkalkulationen setzen standardmäßig eine serifenlose Schrift, der Aufsatz dagegen eine Serifenschrift; auch wer keine der beiden benennen kann, spürt den Bruch. Hinzu kommt: Wird ein Bild auf den Satzspiegel verkleinert, schrumpfen die Striche mit, und die Linien in der Abbildung geraten dünner als die außerhalb. pgfplots übernimmt nur die Koordinaten und setzt Tickzahlen und Legende in der Schrift des Dokuments und über dessen eigenen Mathematiksatz, sodass beide Abweichungen prinzipiell entfallen. Ein $\sin x$ im Achsenlabel liefert exakt die Glyphen des Fließtextsatzes.
Das Zweite, was sich in der Praxis auszahlt, ist die Trennung von den Daten. Statt Koordinaten fest einzutippen, verweist der Plot auf eine .dat- oder .csv-Datei; wird die Messung wiederholt, genügt es, die Datei zu tauschen und neu zu kompilieren — die Abbildung folgt. Aufsatz, Folien und Anhang lesen dieselben Zahlen, Abschreibfehler entstehen gar nicht erst. Auf der Tabellenseite gibt es mit pgfplotstable ein Schwesterpaket derselben Idee. pgfplots selbst baut auf TikZ/PGF auf, die ganze Abbildung liegt also in einem tikzpicture, und TikZ-Nodes und -Dekorationen lassen sich frei beimischen. TikZ selbst — \draw, Koordinatensystem, die Frage nach der langsamen Kompilierung — gehört auf die TikZ-Seite.
Was passiert ohne \pgfplotsset{compat=1.18}
Es erscheint eine Warnung, und die Abbildungen behalten die alten Voreinstellungen. Wird auf dem in TeX Live 2024 enthaltenen pgfplots 1.18.1 ohne compat kompiliert, meldet das Log: "Package pgfplots Warning: running in backwards compatibility mode (unsuitable tick labels; missing features). Consider writing \pgfplotsset{compat=1.18} into your preamble." Die Warnung nennt sogar die passende Nummer, man muss ihr also nur folgen.
\usepackage{pgfplots}
\pgfplotsset{compat=1.18} % pin the release whose defaults you wantDiesen Mechanismus gibt es, weil pgfplots von Version zu Version seine eigenen Voreinstellungen geändert hat. Ändert sich das Format der Tickbeschriftungen oder der Platz, den eine Achse reserviert, ergibt dieselbe Quelle eine andere Abbildung. compat ist der Schlüssel, der erklärt, nach den Konventionen welcher Version gesetzt werden soll, und garantiert, dass sich Abbildungen beim Neukompilieren eines fünf Jahre alten Manuskripts nicht verschieben. TeX Live 2024 liefert pgfplots 1.18.1 vom 15. Mai 2021, damit ist compat=1.18 die höchste dort verfügbare Stufe. In neue Dokumente gehört die Zeile immer; in bestehenden nicht leichtfertig erhöhen — sonst verrutschen Abbildungen unter Umständen.
axis und \addplot: der kleinste funktionierende Plot
In ein tikzpicture kommt eine axis-Umgebung, und jedes \addplot darin legt eine weitere Kurve ab. Die Arbeitsteilung ist klar: alles am Rahmen — Beschriftungen, Ticks, Gitter, Legendenposition — ist eine Option von axis, und alles an einer einzelnen Kurve — Farbe, Marker, Strichmuster — ist eine Option von \addplot. Sitzt diese Zweiteilung, besteht der Rest im Nachschlagen von Optionsnamen.
\begin{tikzpicture}
\begin{axis}[
xlabel = {$x$},
ylabel = {$f(x)$},
title = {A parabola},
grid = major,
]
\addplot[blue, domain=-3:3, samples=100] {x^2};
\addlegendentry{$x^2$}
\end{axis}
\end{tikzpicture}Dies tastet die Parabel y = x² mit 100 Punkten über −3 ≤ x ≤ 3 ab und zeichnet sie als glatte blaue Kurve; grid=major legt ein feines Gitter an die Hauptticks, \addlegendentry ergänzt eine Legendenzeile. Zwei Details lohnen sich. Labels und Titel immer klammern — mit xlabel={$x$} zerlegt ein Komma oder ein ] darin nicht die Optionsauswertung. Und Legenden gibt es zweifach: ein \addlegendentry{…} nach jeder Kurve oder ein gemeinsames legend entries={A,B,...} in den axis-Optionen; die Position steuert legend pos=north west und Verwandte.
| Option | Wirkung |
|---|---|
xlabel= / ylabel= | Achsenbeschriftungen für x und y; als xlabel={$x$} geklammert ist Mathematik sicher |
title= | Ein Titel über dem Plot |
xmin= xmax= ymin= ymax= | Sichtbaren Bereich festlegen, damit Achsen bei wachsenden Daten stehen bleiben |
grid= | grid=major zeichnet an Hauptticks, grid=both zusätzlich an Nebenticks |
legend pos= | Legendenposition: north west usw.; outer north east setzt sie außerhalb des Rahmens |
xtick= | Tickpositionen angeben (xtick={0,1,2}); xtick=data richtet sie an Datenpunkten aus |
width= / height= | Endgröße der Abbildung; width=\linewidth entspricht der Satzspiegelbreite |
ybar / xbar | In ein senkrechtes bzw. waagerechtes Balkendiagramm verwandeln; mehrere \addplot gruppieren automatisch |
Die Voreinstellungen von domain und samples — und warum sin falsch herauskommt
Voreingestellt sind 25 Punkte über dem Intervall −5:5 und Trigonometrie im Gradmaß. Das Erste erklärt sofort, warum eine Kurve eckig wirkt; das Zweite ist die häufigste Ursache für „mein Plot hat die falsche Form“ bei pgfplots. Reizvoll ist, dass keiner der beiden Werte zu pgfplots gehört: Dessen eigenes /pgfplots/samples/.initial ist leer, der Wert stammt aus der darunterliegenden PGF-Schicht. In TeX Live 2024 stehen in tikz.code.tex die Zeilen \def\tikz@plot@samples{25} und \def\tikz@plot@domain{-5:5} — dort liegen die echten Vorgaben.
Das Gradmaß versteht man am schnellsten an Zahlen. Unter TeX Live 2024 ergibt \pgfmathparse{sin(1)} den Wert 0.01746, \pgfmathparse{sin(deg(1))} dagegen 0.84143. Das Erste ist der Sinus von einem Grad, das Zweite der Sinus von einem Bogenmaß, und deg() ist schlicht der Umrechner: deg(1) = 57.29578. Wer also \addplot {sin(x)} schreibt, setzt sin 1° bei x = 1 und erhält eine ungewohnte Welle mit Periode 360. Richtig ist sin(deg(x)).
% wrong: pgfmath reads the argument as degrees
\addplot[domain=0:2*pi, samples=200] {sin(x)};
% right: convert radians to degrees first
\addplot[red, domain=0:2*pi, samples=200] {sin(deg(x))};Drei Wege, \addplot zu füttern: Funktion, Koordinaten oder .dat-Datei
Entweder ein Ausdruck in {...}, oder Punkte in coordinates {...}, oder eine Datei über table {Dateiname}. Die ersten beiden kamen oben vor. In der Praxis zählt der dritte Weg: Bei einer durch Leerraum getrennten Textdatei nimmt pgfplots die erste Zeile als Spaltennamen und plottet standardmäßig Spalte 1 als x und Spalte 2 als y.
x y
0 0.0
1 0.8
2 0.9
3 0.1
4 -0.8
5 -1.0\begin{tikzpicture}
\begin{axis}[xlabel={$x$}, ylabel={$y$}, grid=major]
\addplot[mark=square, teal] table {data.dat};
% naming the columns explicitly is the safer habit:
% \addplot table[x=x, y=y] {data.dat};
\end{axis}
\end{tikzpicture}Spaltennamen sind groß-/kleinschreibungssensitiv. Steht im Header x, im Code aber table[x=X, y=y], bricht TeX Live 2024 ab mit: "! Package pgfplots Error: Sorry, could not retrieve column 'X' from table '...'. Please check spelling (or introduce name aliases)." Unter den Fehlschlägen ist das ein freundlicher — die Meldung nennt gleich Rechtschreibprüfung oder Aliasnamen als Ausweg. Für kommagetrenntes CSV gilt table[col sep=comma, x=x, y=y] {data.csv}; Zeilen mit # oder % am Anfang gelten als Kommentar. Umformen von Daten oder berechnete Spalten übernimmt das Schwesterpaket pgfplotstable.
Balkendiagramme, Log-Achsen und 3D mit \addplot3
Ein Balkendiagramm ist nur ein ybar an axis, eine Log-Achse nur ein anderer Umgebungsname anstelle von axis, und 3D nur \addplot3. Dass keine neue Syntax gelernt werden muss, ist eine Designtugend von pgfplots — die Schreibweise von \addplot bleibt gleich. Bei Balkendiagrammen versetzen sich mehrere \addplot-Aufrufe automatisch zu Gruppenbalken, und wenn die x-Achse Zeichenketten wie Jahreszahlen tragen soll, kombiniert man symbolic x coords mit xtick=data.
\begin{tikzpicture}
\begin{axis}[
ybar,
xlabel = {Year}, ylabel = {Count},
symbolic x coords = {2023, 2024, 2025},
xtick = data,
]
\addplot coordinates {(2023,40) (2024,55) (2025,72)};
\end{axis}
\end{tikzpicture}Für logarithmische Achsen wird der Umgebungsname getauscht: loglogaxis für log-log, semilogxaxis nur für x, semilogyaxis nur für y. Für 3D macht \addplot3 die axis von selbst dreidimensional; surf ergibt eine Fläche, mesh ein Drahtgitter, und die Funktion wird in den zwei Variablen x und y geschrieben. Den Blickwinkel dreht view={Azimut}{Elevation}.
\begin{tikzpicture}
\begin{loglogaxis}[xlabel={$x$}, ylabel={$y$}]
\addplot[domain=1:1000, samples=50] {1/x};
\end{loglogaxis}
\end{tikzpicture}
\begin{tikzpicture}
\begin{axis}[xlabel={$x$}, ylabel={$y$}, zlabel={$z$}]
\addplot3[surf, samples=30, domain=-3:3]
{exp(-x^2 - y^2)};
\end{axis}
\end{tikzpicture}gnuplot gehört nicht zu TeX Live
Das ist wichtig genug, um vorangestellt zu werden: Wer \addplot gnuplot {...} nutzen will, muss gnuplot selbst und getrennt installieren. Im Binärverzeichnis von TeX Live 2024 liegen asy (Asymptote) und mpost (MetaPost) — gnuplot jedoch nicht. gnuplot ist ein eigenständiges Plotprogramm ohne Bezug zu TeX und muss über Homebrew oder die Paketverwaltung der Distribution kommen.
Wozu dann der Aufruf? Der in pgfplots eingebaute Parser läuft auf TeX und eignet sich deshalb schlecht für komplizierte Ausdrücke oder sehr viele Stützstellen. \addplot gnuplot {...} vergibt das Rechnen an gnuplot, und pgfplots zeichnet nur die zurückkommenden Koordinaten. Der Mechanismus ist verblüffend schlicht: TeX schreibt eine Skriptdatei für gnuplot, gnuplot führt sie aus und schreibt eine Zahlentabelle in eine Datei, die TeX wieder einliest. Bei \addplot[blue] gnuplot[domain=0:10] {sin(x)}; unter TeX Live 2024 enthielt die erzeugte .gnuplot-Datei diese Anweisungen (der echte Jobname ist hier durch job ersetzt): set table "job.pgf-plot.table"; set format "%.7e"; set samples 25; set dummy x; plot [x=0:10] sin(x);
Aus dieser einen Zeile lässt sich zweierlei ablesen. Erstens wird die Voreinstellung samples von 25 unverändert an gnuplot durchgereicht. Zweitens geht der Ausdruck in gnuplots eigener Syntax hinüber — deshalb ist der Potenzoperator gnuplots ** statt ^ von pgfplots, und trigonometrische Funktionen arbeiten voreingestellt im Bogenmaß. Dieselbe Sinuskurve heißt über den eingebauten Parser {sin(deg(x))}, über gnuplot aber {sin(x)}. Und weil TeX ein externes Programm starten muss, ist --shell-escape erforderlich (auch -write18 geschrieben).
% compile with: pdflatex --shell-escape document
\begin{tikzpicture}
\begin{axis}[xlabel={$x$}, ylabel={$y$}]
% gnuplot syntax: radians, and ** for powers
\addplot[blue] gnuplot[domain=0:10] {sin(x)};
\end{axis}
\end{tikzpicture}Läuft es dort, wo gnuplot fehlt, meldet TeX Live 2024: "! Package pgfplots Error: Sorry, the gnuplot-result file 'job.pgf-plot.table' could not be found. Maybe you need to enable the shell-escape feature? For pdflatex, this is '>> pdflatex -shell-escape'." Fehlt gnuplot tatsächlich, steht direkt darüber das shell-eigene "sh: gnuplot: command not found". Bemerkenswert ist, was die Meldung anschließt: Man darf stattdessen gnuplot <file>.gnuplot von Hand auf der erzeugten Datei ausführen. Alles, was pgfplots wirklich braucht, ist die .table-Datei — tatsächlich erschien die Abbildung nach dem Ablegen einer handgeschriebenen .table und erneutem Kompilieren, ganz ohne gnuplot. In CI-Umgebungen ohne Installationsmöglichkeit ist es ein gangbarer Ausweg, die erzeugte .table mit ins Repository zu legen.
Abbildungen aus R und Python samt Schriften übernehmen
Die Auswertung liegt bereits in R oder Python vor, ein PNG soll aber vermieden werden. Die Antwort lautet: das jeweilige Werkzeug TikZ/PGF-Code ausgeben lassen und ihn per \input einbinden. Die Abbildung wird dann als Teil des Dokuments gesetzt, Schrift und Formeln passen zum Text. Vorab jedoch: Keines dieser Werkzeuge gehört zu TeX Live; jedes kommt aus der Paketverwaltung der eigenen Sprache.
In R stellt tikzDevice von CRAN ein Grafikgerät bereit, das R-Standardgrafik — Base-Plots wie ggplot2 — als TikZ-Code schreibt. Gerät mit tikz() öffnen, den gewohnten Plotcode ausführen, mit dev.off() schließen: fertig ist eine .tex. Sein Kniff besteht darin, beim Platzieren von Text LaTeX nach Zeichenkettenbreiten und Schriftmetriken zu fragen — genau deshalb passt die Ausgabe zur Textschrift und lässt sich LaTeX-Mathematik in Achsenbeschriftungen setzen. Mit standAlone=TRUE entsteht ein vollständiges, separat kompilierbares Dokument.
library(tikzDevice)
tikz("plot.tex", width = 4, height = 3)
plot(cars$speed, cars$dist,
xlab = "Speed", ylab = "Distance")
dev.off()Auf der Python-Seite gibt es zwei Wege, die gern verwechselt werden. Der erste ist das in matplotlib enthaltene pgf-Backend — kein Zusatzpaket nötig. Ein matplotlib.use("pgf") und danach savefig("figure.pgf") liefert eine .pgf-Datei. Entscheidend ist: Ausgegeben wird reines PGF, kein pgfplots-Code. Eine tatsächlich mit matplotlib 3.11.0 erzeugte Datei enthält kein einziges \addplot und keine axis-Umgebung; der Rumpf besteht aus Low-Level-Befehlen wie \pgfqpoint, \pgfpathlineto und \pgfsetstrokecolor. Die Achsen sind also keine von pgfplots gesetzte Achse, sondern eine als Linien gezeichnete. Und das Dokument braucht nur \usepackage{pgf}, nicht \usepackage{pgfplots}.
import matplotlib
matplotlib.use("pgf")
import matplotlib.pyplot as plt
matplotlib.rcParams.update({
"pgf.texsystem": "pdflatex", # default here is xelatex
"font.family": "serif",
"text.usetex": True,
"pgf.rcfonts": False,
})
fig, ax = plt.subplots(figsize=(4, 3))
ax.plot([0, 1, 2, 3], [0, 1, 4, 9])
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$x^2$")
fig.savefig("figure.pgf")Hier lauert eine Falle. Die erzeugte .pgf beginnt mit Kommentaren, die \usepackage{pgf} in der Präambel empfehlen — genau so gemacht und \input{figure.pgf} aufgerufen, bricht TeX Live 2024 ab mit: "! Undefined control sequence. <recently read> \mathdefault". Der Grund: matplotlib schreibt Tickbeschriftungen in \mathdefault{...} gehüllt heraus, legt die Definition aber nicht in die .pgf. Sein backend_pgf.py schiebt die Definition nur dann in eine interne Präambel, wenn matplotlib selbst das PDF erzeugt. Die Abhilfe ist eine Zeile: \providecommand{\mathdefault}[1]{#1} in der Präambel, und es kompiliert. Zu beachten ist außerdem, dass pgf.texsystem auf xelatex voreingestellt ist; wird das Dokument mit pdfLaTeX gesetzt, gibt man es wie im Beispiel oben ausdrücklich an.
\documentclass{article}
\usepackage{pgf}
\usepackage{lmodern}
% matplotlib wraps tick labels in \mathdefault but never defines it
\providecommand{\mathdefault}[1]{#1}
\begin{document}
\input{figure.pgf}
\end{document}Der zweite Python-Weg ist tikzplotlib (früher matplotlib2tikz), das eine matplotlib-Abbildung in pgfplots-Code umwandelt. Schreiben mit tikzplotlib.save("figure.tex"), dann \usepackage{pgfplots} und \pgfplotsset{compat=...} laden und \input{figure.tex}. Weil die Achsen tatsächlich eine axis-Umgebung sind statt reines PGF, lassen sich Ticks und Legenden nachträglich in LaTeX korrigieren — der entscheidende Unterschied zum pgf-Backend. Zu beachten ist allerdings, dass tikzplotlib nicht mehr gepflegt wird; als Nachfolger entsteht der Fork matplot2tikz mit nahezu gleicher API. Beide kommen aus PyPI, keines gehört zu TeX Live.
Wenn zu viele Punkte die Kompilierung nicht enden lassen
Zuerst die Punkte ausdünnen — das hilft am meisten. Alle Rechnungen von pgfplots laufen über TeX-Makroexpansion, ein Streudiagramm mit Zehntausenden Punkten ist daher ehrlich langsam und kann sogar an TeXs Speichergrenze stoßen. Und auf Papier sind Punkte, deren Abstand unter der Druckauflösung liegt, ohnehin nicht sichtbar. Die folgenden Mittel stehen grob nach Wirkung sortiert.
- Punkte ausdünnen. Mit
each nth point=knur jeden k-ten Punkt zeichnen und Daten außerhalb des Bereichs über Filter wiefilter discard if notverwerfen. Bei Streudiagrammen sieht das Ergebnis meist gleich aus. - Externalisierung. Mit
\usepgfplotslibrary{external}und\tikzexternalizewird jede Abbildung einmal in ein eigenes PDF kompiliert und danach nur noch eingebunden. Das Neukompilieren des Textes wird dramatisch leichter, verlangt aber--shell-escape. - Eine Engine mit mehr Speicher.
lualatexhat lockerere TeX-Speichergrenzen und bleibt bei großen Abbildungen seltener stecken. - Gleich vorgelagert rendern. Bleibt ein Plot zu schwer, ist der Import eines in R, Python oder gnuplot gezeichneten Ergebnisses (voriger Abschnitt) der praktischere Weg.
Externalisierung und gnuplot verlangen beide --shell-escape; wer in CI oder Container baut, muss es auch dort aktivieren — sonst scheitert drüben ein Build, der lokal durchlief. Dieser Fehlschlag ist außerordentlich häufig, ein Blick auf die Docker/CI-Seite lohnt sich.