Una gráfica pegada en un artículo suele reconocerse de un vistazo. No porque la curva se vea mal, sino porque los números de los ejes están en otra tipografía. La herramienta de LaTeX que arregla esto de raíz es pgfplots: en lugar de recibir el gráfico como imagen, lo construye a partir de números con el mismo motor de composición que el texto que lo rodea. Esta página cubre el entorno axis de pgfplots y \addplot, la lectura de un archivo .dat, la delegación del cálculo a gnuplot, y la importación de figuras hechas en R o Python arrastrando consigo las fuentes. De paso cita los mensajes de error que TeX Live 2024 produjo de verdad: la mayoría de quienes llegan aquí buscando tienen uno delante.
Por qué una imagen pegada nunca encaja del todo en la página
La razón no es la resolución sino la tipografía y el grosor de línea. Una hoja de cálculo usa por defecto una letra de palo seco mientras el artículo va compuesto en una serif; el lector que no sabría nombrar ninguna de las dos percibe igualmente el desajuste. Además, reducir una imagen hasta la caja de texto reduce también sus trazos, de modo que los filetes de dentro de la figura acaban más finos que los de fuera. pgfplots recibe solo las coordenadas y coloca los números de las marcas y la leyenda con la fuente del propio documento y a través de su propia composición matemática, así que ninguno de los dos desajustes puede aparecer. Escribe $\sin x$ en una etiqueta de eje y obtendrás exactamente los glifos que usan tus fórmulas.
Lo segundo que rinde en la práctica es la separación de los datos. Haz que el gráfico apunte a un .dat o un .csv en vez de escribir las coordenadas a mano: si se repite la medición, basta con cambiar el archivo y recompilar y la figura se actualiza. Artículo, diapositivas y apéndice leen los mismos números, así que los errores de transcripción no llegan a producirse. En el terreno de las tablas, pgfplotstable aplica la misma idea. pgfplots está construido sobre TikZ/PGF, de modo que toda la figura vive dentro de un tikzpicture y los nodos y decoraciones de TikZ se mezclan sin problema. TikZ en sí —\draw, el sistema de coordenadas, por qué la compilación es lenta— corresponde a la página de TikZ.
Qué pasa si omites \pgfplotsset{compat=1.18}
Aparece un aviso y las figuras conservan los valores por defecto antiguos. Si compilas sin compat con el pgfplots 1.18.1 que trae TeX Live 2024, el registro dice: "Package pgfplots Warning: running in backwards compatibility mode (unsuitable tick labels; missing features). Consider writing \pgfplotsset{compat=1.18} into your preamble." El propio aviso indica el número que debes usar, así que basta con seguirlo.
\usepackage{pgfplots}
\pgfplotsset{compat=1.18} % pin the release whose defaults you wantEl mecanismo existe porque pgfplots ha ido cambiando sus propios valores por defecto de una versión a otra. Si cambia el formato de las etiquetas de marca o el espacio que reserva un eje, la misma fuente produce otra figura. compat es la clave que declara con qué convenciones componer y garantiza que recompilar un manuscrito de hace cinco años no mueva las figuras. TeX Live 2024 trae pgfplots 1.18.1, del 15 de mayo de 2021, así que compat=1.18 es el nivel más alto que puedes pedir ahí. Pon la línea en todo documento nuevo y, en uno existente, no la subas a la ligera: en cuanto lo hagas, las figuras pueden desplazarse.
axis y \addplot: el gráfico mínimo que funciona
Coloca un entorno axis dentro de un tikzpicture: cada \addplot que contenga añade una curva más. El reparto es limpio: todo lo del marco —etiquetas, marcas, rejilla, posición de la leyenda— es una opción de axis, y todo lo de una curva concreta —color, marcadores, guiones— es una opción de \addplot. Una vez captada esa estructura de dos capas, lo demás es consultar nombres de opciones.
\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}Esto muestrea la parábola y = x² en 100 puntos sobre −3 ≤ x ≤ 3 y la dibuja como curva azul suave; grid=major pone una rejilla tenue sobre las marcas principales y \addlegendentry añade una línea de leyenda. Dos detalles que conviene interiorizar. Encierra siempre etiquetas y títulos entre llaves: con xlabel={$x$}, una coma o un ] dentro no rompe el análisis de opciones. Y las leyendas admiten dos formas: un \addlegendentry{…} tras cada curva, o un legend entries={A,B,...} agrupado en las opciones de axis; la posición se fija con legend pos=north west y similares.
| Opción | Qué hace |
|---|---|
xlabel= / ylabel= | Etiquetas de los ejes x e y; entre llaves como xlabel={$x$}, las fórmulas van seguras |
title= | Un título colocado sobre el gráfico |
xmin= xmax= ymin= ymax= | Fijar el rango visible para que los ejes no se muevan al crecer los datos |
grid= | grid=major traza en las marcas principales, grid=both también en las menores |
legend pos= | Posición de la leyenda: north west, etc.; outer north east la saca del marco |
xtick= | Indicar las marcas (xtick={0,1,2}); xtick=data las alinea con los datos |
width= / height= | Tamaño final de la figura; width=\linewidth iguala el ancho del texto |
ybar / xbar | Convertirlo en barras verticales / horizontales; varios \addplot se agrupan solos |
Los valores por defecto de domain y samples, y por qué sin sale mal
Los valores por defecto son 25 puntos sobre el intervalo −5:5 y trigonometría en grados. Lo primero explica de inmediato por qué una curva se ve angulosa; lo segundo es la causa más habitual del «mi gráfico tiene la forma equivocada» en pgfplots. Lo curioso es que ninguno de los dos pertenece a pgfplots: su propio /pgfplots/samples/.initial está vacío y el valor cae a la capa PGF de debajo. En TeX Live 2024, tikz.code.tex contiene las líneas \def\tikz@plot@samples{25} y \def\tikz@plot@domain{-5:5}, y ahí están los valores reales.
El asunto de los grados se ve más rápido en cifras. En TeX Live 2024, \pgfmathparse{sin(1)} vale 0.01746, mientras que \pgfmathparse{sin(deg(1))} da 0.84143. El primero es el seno de un grado, el segundo el de un radián, y deg() no es más que el conversor de radianes a grados: deg(1) = 57.29578. Así que escribir \addplot {sin(x)} coloca sin 1° en x = 1 y produce una onda extraña de periodo 360. La forma correcta es 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))};Tres formas de alimentar \addplot: función, coordenadas o archivo .dat
O bien una expresión en {...}, o bien puntos en coordinates {...}, o bien un archivo con table {nombre}. Las dos primeras aparecieron arriba. En la práctica se usa la tercera: ante un archivo de texto separado por espacios, pgfplots toma la primera línea como nombres de columna y dibuja por defecto la columna 1 como x y la columna 2 como 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}Los nombres de columna distinguen mayúsculas. Si la cabecera dice x pero escribes table[x=X, y=y], TeX Live 2024 se detiene con: "! Package pgfplots Error: Sorry, could not retrieve column 'X' from table '...'. Please check spelling (or introduce name aliases)." Como fallo es de los amables: el propio mensaje sugiere revisar la ortografía o añadir un alias. Para CSV separado por comas usa table[col sep=comma, x=x, y=y] {data.csv}; las líneas que empiezan por # o % se saltan como comentarios. Transformar datos o derivar columnas calculadas es tarea del paquete hermano pgfplotstable.
Barras, ejes logarítmicos y 3D con \addplot3
Un gráfico de barras es solo ybar añadido a axis; un eje logarítmico, solo otro nombre de entorno en lugar de axis; y el 3D, solo \addplot3. No tener que reaprender la sintaxis es una virtud de diseño de pgfplots: la forma de escribir \addplot se conserva. En un gráfico de barras, apilar varias llamadas a \addplot las desplaza solas hasta formar barras agrupadas, y si el eje x debe llevar cadenas como años, se combina symbolic x coords con 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}Para ejes logarítmicos, cambia el nombre del entorno: loglogaxis para log-log, semilogxaxis solo para x, semilogyaxis solo para y. En 3D, \addplot3 vuelve tridimensional el axis por sí solo; indica surf para una superficie o mesh para una malla y escribe la función en las dos variables x e y. El punto de vista gira con view={acimut}{elevación}.
\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 no viene con TeX Live
Esto importa lo bastante como para decirlo primero: usar \addplot gnuplot {...} implica instalar gnuplot por tu cuenta, aparte. Mira dentro del directorio de binarios de TeX Live 2024 y encontrarás asy (Asymptote) y mpost (MetaPost), pero no gnuplot. gnuplot es un programa de gráficos independiente, desarrollado sin relación con TeX, y tiene que venir de Homebrew o del gestor de paquetes de tu distribución.
¿Para qué llamarlo entonces? El analizador incorporado en pgfplots corre sobre TeX, lo que lo hace poco apto para expresiones complicadas o muchísimas muestras. Escribir \addplot gnuplot {...} subcontrata el cálculo a gnuplot y pgfplots solo dibuja las coordenadas que vuelven. El mecanismo es sorprendentemente sencillo: TeX escribe un archivo de script para gnuplot, gnuplot lo ejecuta y escribe una tabla de números en un archivo, y TeX la lee de vuelta. Al procesar \addplot[blue] gnuplot[domain=0:10] {sin(x)}; en TeX Live 2024, el archivo .gnuplot generado llevaba estas directivas (el nombre real del trabajo se sustituye aquí por job): set table "job.pgf-plot.table"; set format "%.7e"; set samples 25; set dummy x; plot [x=0:10] sin(x);
De esa única línea se leen dos cosas. Primero, el valor por defecto de samples, 25, pasa tal cual a gnuplot. Segundo, la expresión viaja en la sintaxis propia de gnuplot: por eso el operador de potencia es el ** de gnuplot y no el ^ de pgfplots, y las funciones trigonométricas usan radianes por defecto. La misma curva seno se escribe {sin(deg(x))} con el analizador integrado, pero {sin(x)} a través de gnuplot. Y como TeX debe lanzar un programa externo, --shell-escape es obligatorio (también escrito -write18).
% 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}Ejecútalo donde falte gnuplot y TeX Live 2024 informa: "! 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'." Si gnuplot falta de verdad, justo encima aparece el propio "sh: gnuplot: command not found" del intérprete. Fíjate en lo que añade el mensaje: puedes ejecutar a mano gnuplot <file>.gnuplot sobre el archivo generado. Lo único que pgfplots necesita de verdad es el archivo .table; de hecho, al colocar una .table escrita a mano y recompilar, la figura salió sin gnuplot por ninguna parte. En una integración continua donde no se pueda instalar gnuplot, incluir la .table generada en el repositorio es una salida practicable.
Traer figuras de R y Python con sus fuentes
El análisis ya está hecho en R o Python, pero prefieres no pegar un PNG. La respuesta es hacer que cada herramienta emita código TikZ/PGF e incluirlo con \input: la figura se compone entonces como parte del documento, con su tipografía y sus fórmulas iguales a las del cuerpo. Conviene aclarar de entrada que ninguna de estas herramientas forma parte de TeX Live: cada una viene del gestor de paquetes de su lenguaje.
En R, tikzDevice, de CRAN, aporta un dispositivo gráfico que escribe la salida gráfica estándar de R —tanto gráficos base como ggplot2— en código TikZ. Abre el dispositivo con tikz(), ejecuta tu código de dibujo habitual, ciérralo con dev.off() y tendrás un .tex. Su rasgo distintivo es que consulta a LaTeX los anchos de cadena y las métricas de fuente al colocar el texto, y por eso la salida coincide con la tipografía del cuerpo y puedes poner fórmulas de LaTeX en una etiqueta de eje. Con standAlone=TRUE emite un documento completo compilable por separado.
library(tikzDevice)
tikz("plot.tex", width = 4, height = 3)
plot(cars$speed, cars$dist,
xlab = "Speed", ylab = "Distance")
dev.off()Del lado de Python hay dos caminos, y se confunden con facilidad. El primero es el backend pgf incluido en matplotlib: no hace falta ningún paquete extra. Un matplotlib.use("pgf") y luego savefig("figure.pgf") produce un archivo .pgf. Lo crucial: lo que emite es PGF puro, no código de pgfplots. Al inspeccionar un archivo realmente generado por matplotlib 3.11.0 no aparece ni un \addplot ni un entorno axis; el cuerpo es una sucesión de órdenes de bajo nivel como \pgfqpoint, \pgfpathlineto y \pgfsetstrokecolor. Es decir, los ejes no son un eje compuesto por pgfplots sino un eje dibujado con líneas. Y el documento solo necesita \usepackage{pgf}, no \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")Aquí acecha una trampa. El .pgf generado empieza con comentarios que piden poner \usepackage{pgf} en el preámbulo; hazlo, llama a \input{figure.pgf} y TeX Live 2024 se detiene con: "! Undefined control sequence. <recently read> \mathdefault". La causa es que matplotlib envuelve las etiquetas de marca en \mathdefault{...} al escribirlas, pero no mete la definición en el .pgf. Su backend_pgf.py inyecta esa definición en un preámbulo interno solo cuando matplotlib genera el PDF por su cuenta. El remedio es una línea: \providecommand{\mathdefault}[1]{#1} en el preámbulo y compila. Ten en cuenta además que pgf.texsystem vale xelatex por defecto, así que si el documento se compone con pdfLaTeX conviene indicarlo como en el ejemplo anterior.
\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}El otro camino de Python es tikzplotlib (antes matplotlib2tikz), que convierte una figura de matplotlib en código pgfplots. Escríbela con tikzplotlib.save("figure.tex"), luego carga \usepackage{pgfplots} y \pgfplotsset{compat=...} y haz \input{figure.tex}. Como los ejes son de verdad un entorno axis y no PGF puro, puedes retocar marcas y leyendas desde LaTeX a posteriori: esa es la diferencia decisiva con el backend pgf. Ten en cuenta, eso sí, que tikzplotlib ya no se mantiene; un fork, matplot2tikz, se desarrolla como sucesor con una API casi idéntica. Ambos se instalan desde PyPI y ninguno forma parte de TeX Live.
Cuando hay demasiados puntos y la compilación no termina
Primero, adelgazar los puntos: es lo que más ayuda. Toda la aritmética de pgfplots ocurre por expansión de macros de TeX, así que una dispersión de decenas de miles de puntos es francamente lenta y puede incluso alcanzar el techo de memoria de TeX. Y sobre el papel, si dos puntos vecinos quedan más juntos que la resolución de impresión, esos puntos no se ven de todos modos. Los remedios siguientes van más o menos por orden de efecto.
- Adelgazar los puntos. Dibuja cada k-ésimo punto con
each nth point=ky descarta datos fuera de rango con filtros comofilter discard if not. En una dispersión el resultado suele verse igual. - Externalización. Con
\usepgfplotslibrary{external}y\tikzexternalize, cada figura se compila una vez en su propio PDF y después solo se incluye. Recompilar el cuerpo se vuelve muchísimo más ligero, a cambio de necesitar--shell-escape. - Un motor con más memoria.
lualatextiene límites de memoria de TeX más holgados y se atasca menos con figuras grandes. - Renderizar antes, en origen. Si un gráfico sigue siendo demasiado pesado, importar un resultado dibujado en R, Python o gnuplot (sección anterior) es la vía más práctica.
La externalización y gnuplot exigen ambos --shell-escape, así que si compilas en integración continua o en un contenedor debes activarlo también allí; de lo contrario, una compilación que pasaba en local falla del otro lado. Ese fallo es muy común, así que conviene repasar la configuración en la página de Docker / CI.