Tablas a partir de datos

Componer una columna de números con r es el error más habitual en las tablas de LaTeX. Alinee 182.5, 95.0, 1450.25 y 7 a la derecha y los separadores decimales quedan exactamente a un dígito (5pt) de distancia, mientras que el 7 solitario aterriza en las décimas en vez de en las unidades. Se puede medir. El remedio es un carácter en la especificación de columna: cambie a la columna S de siunitx y los marcadores decimales se alinean con una precisión de 0,001pt. Esta página trata de esa alineación y después de leer un .csv en tiempo de compilación con csvsimple, pgfplotstable y datatool, además del enfoque moderno de tabularray. El principio es siempre el mismo: deje los datos en el archivo de datos y que LaTeX los lea.

Alinear números por el decimal: la columna S de siunitx

Cargue \usepackage{siunitx} y sustituya la r de la especificación de columna por S. La columna S analiza el número y lo alinea por el marcador decimal, de modo que una columna cuyas entradas tienen distinto número de cifras vuelve a ser legible. Lea las coordenadas de los glifos del PDF y la diferencia salta a la vista. En una columna r, los separadores decimales de 182.5 y 95.0 quedan en x = 62,0 mientras que el de 1450.25 está en x = 57,0 —unos 5pt de diferencia, exactamente el ancho de un dígito— y el 7 se coloca en las décimas en lugar de en las unidades. Con los mismos datos en S[table-format=4.2], las cuatro cifras de las unidades acaban todas en x = 122,11 y cada marcador decimal parte del mismo punto. La dispersión es menor de 0,001pt.

latex
\usepackage{siunitx}
\usepackage{booktabs}

% table-format = <integer digits>.<decimal digits> of the widest entry
\begin{tabular}{l S[table-format=4.2]}
  \toprule
  Sample & {Mass / \unit{\gram}} \\
  \midrule
  A &  182.5  \\
  B &   95.0  \\
  C & 1450.25 \\
  D &    7    \\
  \bottomrule
\end{tabular}

Importan dos cosas. Primero, fije table-format=<dígitos enteros>.<decimales> al mayor valor de la columna. Una S pelada funciona, pero reserva peor el ancho: en el mismo ejemplo la columna pasó de 88,44pt a 98,44pt. Al revés, indicarlo demasiado corto, como table-format=2.1, no produce ningún error en siunitx 3 y la alineación se mantiene; el número simplemente desborda el espacio reservado y puede chocar con la columna siguiente. Segundo, proteja con llaves {…} todo lo que pueda confundirse con un número. El manual de siunitx lo dice así: si el material puede confundirse con parte de un número, debe protegerse con llaves. A la inversa, un texto corriente como Sample ya se centra correctamente en la v3 aunque no lleve llaves.

Así se ve, medido, ese «podría confundirse». Escriba un encabezado como 2024 sales sin llaves y siunitx toma el 2024 inicial como el número, lo alinea con las cifras de las demás filas y compone el sales restante como material posterior. El hueco entre 2024 y sales se abre hasta 7,75bp, frente a 3,32bp —un espacio entre palabras normal— cuando todo se envuelve como {2024 sales}. No se emite ningún error: solo se rompe el aspecto, así que no olvide las llaves en un encabezado que contenga un número. Dentro del argumento de \multicolumn o \multirow no puede usarse una columna S; allí se recurre a la forma de macro \tablenum[table-format=4.2]{1450.25}, que el manual describe como, en efecto, una versión de macro de la columna S.

El paquete siunitx en su conjunto —la sintaxis de \num, \qty y \unit, las incertidumbres en notación entre paréntesis como \num{1.234(5)}, los exponentes y las diferencias entre v2 y v3— corresponde a la página «Unidades (siunitx)». Reparta el trabajo así: \qty para las magnitudes en la prosa y la columna S para los números en las tablas, y el estilo numérico se mantendrá coherente en todo el documento. Del lado de la tabla solo hay que recordar una convención tipográfica: la unidad va en el encabezado de columna, no en cada celda. El manual de booktabs recoge la misma regla entre sus pautas.

Por qué dejar que LaTeX lea el CSV

Porque así deja de editar el manuscrito cada vez que cambia un dígito. Los resultados experimentales y las tablas de resumen salen casi siempre de una hoja de cálculo o de un instrumento como CSV (texto separado por comas). Copiarlos a mano en celdas de tabular es tedioso, y cada fila añadida y cada valor corregido es otra ocasión de teclear mal. Dele la vuelta a la idea —deje los datos en el archivo de datos y pídale a LaTeX que los lea y los componga— y bastará con actualizar los datos y recompilar para que la tabla siga. Reutilice el mismo CSV en el cuerpo, en diapositivas y en un apéndice tantas veces como quiera, sin errores de transcripción. Es la prolongación directa de la costumbre de LaTeX de separar estructura lógica y apariencia.

Todos los ejemplos de esta página usan el pequeño archivo CSV siguiente. Su primera línea es la fila de encabezado, que nombra las columnas product, price y weight; el resto son datos. Guárdelo como data.csv junto a su .tex y el código que sigue compila tal cual.

data.csv
product,price,weight
Apple,380,182.5
Orange,120,95.0
Melon,1280,1450.25

Leer un CSV con csvsimple

Cargue \usepackage{csvsimple} y escriba la única línea \csvautotabular{data.csv}: todo el CSV se convierte en un tabular. La primera línea se compone como encabezado con líneas, lo que basta de sobra para echar un vistazo al contenido. Lo que no da es control sobre el formato. Para decidir la alineación, las líneas y qué columnas imprimir, use \csvreader, el verdadero caballo de batalla del paquete. Un detalle que conviene saber: en la v2.6.0 que acompaña a TeX Live 2024, un \usepackage{csvsimple} a secas carga la implementación antigua csvsimple-legacy; csvsimple.sty contiene la línea \SetKeys{ legacy }. Si quiere la implementación de LaTeX3, pídala: \usepackage[l3]{csvsimple} o \usepackage{csvsimple-l3}.

latex
\usepackage{csvsimple}
\usepackage{booktabs}
\usepackage{siunitx}

% NOTE the braces around the column spec: an unbraced S[...] breaks the key list
\csvreader[
  tabular        = {l r S[table-format=4.2]},
  table head     = \toprule Product & {Price} & {Weight} \\ \midrule,
  table foot     = \bottomrule,
  late after line = \\]
{data.csv}
{product=\product, price=\price, weight=\weight}
{\product & \price & \weight}

Las cuatro partes de \csvreader[opciones]{archivo}{asignaciones}{cuerpo} son, por orden, el archivo que se lee, la vinculación de nombres de columna a macros y lo que se emite por cada fila. Escriba price=\price y \price se expandirá, dentro del cuerpo, al valor de esa fila. El marco procede de las opciones: tabular= es la especificación de columnas, table head= la fila de encabezado, table foot= el cierre y late after line = \\ añade un terminador de fila \\ a cada línea, el modismo que evita un salto sobrante tras la última fila. Cuando los nombres de encabezado contienen espacios o símbolos, deje vacías las asignaciones y dirija las columnas por número con \csvcoli, \csvcolii, \csvcoliii: primera, segunda y tercera columna.

Aquí está la única trampa de verdad de esta página. Las llaves alrededor del valor de tabular en el ejemplo anterior no son adorno. Escríbalo desnudo, tabular = l r S[table-format=4.2], y la coma que separa los pares clave-valor choca con la coma interior de S[…]; la compilación se detiene con ! Paragraph ended before \NC@rewrite@S was complete. Después informa de ! Missing $ inserted. y ! Package csvsimple Error: File ',' not existent…, con lo que resulta difícil sospechar de la especificación de columnas. Con llaves —tabular = {l r S[table-format=4.2]}— compila. El hábito seguro va más allá de S: siempre que una especificación de columnas con corchetes vaya dentro de una lista clave-valor, protéjala con llaves.

Por omisión, la primera línea se trata como encabezado y se excluye de los datos. Para leer un CSV sin fila de encabezado, la forma con asterisco \csvreader* toma también la primera línea como datos. Además existe filter para seleccionar filas por condición, y \csvstyle / \csvnames para reutilizar un juego de asignaciones: prestaciones que van más allá de las tablas hacia el procesamiento fila a fila en general, como generar etiquetas de dirección a partir de una lista de contactos.

Diseñar el formato numérico con pgfplotstable

Si quiere diseñar el aspecto de los propios números, la opción más potente es pgfplotstable. Forma parte de pgfplots, se carga con \usepackage{pgfplotstable} y tiene un comando central: \pgfplotstabletypeset[opciones]{data.csv}. Lee el CSV, lo formatea con la precisión y el estilo numérico pedidos y ensambla internamente un tabular como salida. Para leer un CSV hay que declarar el separador con col sep=comma: el valor por omisión es la separación por espacios. Todo se controla mediante opciones clave-valor.

OpciónQué hace
col sep=commaLeer como CSV (separado por comas); por omisión, por espacios
header=has colnamesTratar la línea 1 como nombres de columna; header=false: sin encabezado
columnscolumns={a,b,…} elige qué columnas imprimir y en qué orden
columns/NAME/.stylecolumns/price/.style={…} aplica formato a una columna concreta
column nameSustituye el encabezado impreso, con independencia del nombre en el CSV
fixedComa fija; fixed zerofill rellena ceros finales y precision=n fija los decimales
sciComponer en notación científica (exponente); sci zerofill rellena la mantisa
string typeColumna de texto; no se aplica ningún formato numérico
dec sep alignAlinea la columna por el separador decimal (necesita array)
latex
\usepackage{pgfplotstable}
\usepackage{booktabs}
\pgfplotsset{compat=1.18}

\pgfplotstabletypeset[
  col sep = comma,
  header  = has colnames,
  columns = {product, price, weight},
  columns/product/.style = {string type, column name = Product},
  columns/price/.style   = {column name = Price, fixed, precision = 0},
  columns/weight/.style  = {column name = {Weight / g}, fixed, fixed zerofill,
                            precision = 1, dec sep align},
  every head row/.style  = {before row = \toprule, after row = \midrule},
  every last row/.style  = {after row = \bottomrule},
]{data.csv}

La columna product es string type (texto), price es entera (precision=0) y weight lleva un decimal con relleno de ceros, alineada con dec sep align. Los encabezados se sustituyen con column name, y las líneas provienen de comandos de booktabs inyectados mediante every head row y every last row. Cambie solo precision y los mismos datos cambian de número de cifras: controlar el aspecto de los números sin tocar el CSV es justamente para lo que sirve pgfplotstable. También exige cuidado: con estos ajustes, 1450.25 se imprime como 1,450.3, redondeado. Quitar cifras es una instrucción de formato y el redondeo ocurre en silencio, así que elija las cifras significativas de forma deliberada. (Añada 1000 sep={} si no quiere el separador de millares.)

pgfplotstable también puede derivar columnas calculadas de las que lee: defina una columna que se calcula al usarse con create on use, o posprocese valores dentro de columns/…/.style mediante postproc cell content. En suma, trabajo de hoja de cálculo hecho enteramente dentro de LaTeX. Toda esa potencia encarece la sintaxis, así que la regla práctica es: pgfplotstable para tablas numéricas elaboradas, csvsimple para el simple CSV a tabla. Si además representa gráficamente el mismo CSV, compartir maquinaria con \addplot table de pgfplots es un argumento más a su favor.

datatool: tratar el CSV como una base de datos

El tercer paquete, datatool, lee un CSV como una base de datos y destaca en el procesamiento fila a fila, ese trabajo de combinación de correspondencia. Tras \usepackage{datatool}, \DTLloaddb{nombre}{data.csv} carga el archivo en una base con nombre. Por omisión, la primera línea es el encabezado y sus nombres de columna se convierten en las claves de cada valor. Para un CSV sin encabezado, \DTLloaddb[noheader]{…}{…} nombra las columnas Column1, Column2, … automáticamente. Una vez cargada, \DTLforeach{nombre}{asignaciones}{cuerpo} recorre las filas. Las asignaciones se escriben «macro = nombre de columna», como en \DTLforeach{db}{\Product=product,\Price=price}{…}, y dentro del cuerpo \Product y \Price se expanden a los valores de esa fila.

latex
\usepackage{datatool}
\usepackage{booktabs}

\DTLloaddb{goods}{data.csv}

% the row break goes at the START of the loop body, not the end
\begin{tabular}{l r}
  \toprule
  Product & Price
  \DTLforeach{goods}{\Product=product, \Price=price}{%
    \\ \Product & \Price}
  \\ \bottomrule
\end{tabular}

Aquí el CSV se carga como goods, y el \DTLforeach dentro del tabular emite «producto y precio» por cada fila. Observe que el terminador de fila \\ va al principio del cuerpo, no al final. Si lo pone al final, la última iteración abre una fila vacía y el \bottomrule (o \hline) siguiente cae dentro de una celda de esa fila, con lo que la compilación se detiene con ! Misplaced \noalign. Anteponerlo y añadir un \\ tras el bucle es la forma segura. La verdadera fuerza de datatool está más en manipular datos que en componer tablas: puede sumar y promediar números, ordenar y excluir filas por condición mediante macros, y se usa para generar bibliografías y documentos combinados. A la inversa, si solo necesita convertir un CSV en una tabla, csvsimple es más conciso.

Alinear números con la columna Q de tabularray

Cargue \UseTblrLibrary{siunitx} y tabularray lleva al entorno tblr la misma alineación decimal que la columna S, en la forma Q[si={table-format=4.2}]. Añada \UseTblrLibrary{booktabs} y \toprule / \midrule / \bottomrule funcionan como siempre, mientras que el formato se indica por completo en clave-valor, por ejemplo con colspec o row{1}={font=\bfseries}. El gran atractivo es no tener que reaprender sintaxis como >{…} o \multirow. Pero tabularray en sí —el diseño de tblr, width=, la combinación de celdas, rowsep y colsep— corresponde a la página «Entornos de tabla avanzados», así que aquí nos ceñimos a las columnas numéricas.

latex
\usepackage{tabularray}
\UseTblrLibrary{booktabs}
\UseTblrLibrary{siunitx}

\begin{tblr}{colspec = {l r Q[si={table-format=4.2}]}}
  \toprule
  Product & Price & {{{Weight}}} \\
  \midrule
  Apple  &  380 &  182.5  \\
  Orange &  120 &   95.0  \\
  Melon  & 1280 & 1450.25 \\
  \bottomrule
\end{tblr}

Cuál usar

Elegir por finalidad es lo más rápido. Si los datos son unas pocas decenas de filas y solo quiere que se alineen los números de una tabla escrita a mano, la columna S basta. Si el CSV cambia continuamente, pase a uno de los lectores. He aquí una guía aproximada.

  • Solo quiere alinear los separadores decimales — la columna S de siunitx. Encaja directamente en un tabular escrito a mano; dentro de \multicolumn use \tablenum.
  • CSV a tabla, sin máscsvsimple. \csvautotabular para un resultado inmediato, \csvreader cuando necesite controlar alineación, líneas y qué columnas aparecen.
  • Diseñar el número de cifras y el estilo numérico, o columnas calculadaspgfplotstable. El más potente, pero de sintaxis pesada y con redondeo silencioso.
  • La manipulación de datos es el objetivo (totales, ordenación, procesamiento condicional, documentos combinados) — datatool.
  • Prefiere la sintaxis clave-valor, o quiere una columna numérica en un tblr existentetabularray con la biblioteca siunitx y Q[si={…}].

Sea cual sea el método, lo que sale al final es el mismo vocabulario de tabular: la especificación de columnas, &, \\ y las líneas. Si la lectura funciona pero la tabla sigue siendo difícil de leer, la causa suele estar en las líneas: cambie \hline por las tres de booktabs y traslade las unidades a los encabezados de columna, y los mismos datos se leerán mucho mejor. Para una tabla demasiado ancha para la página vea «Entornos de tabla avanzados»; para una que cruza un salto de página, «Tablas que abarcan páginas»; para leyendas y colocación, «Colocación y estilo de tablas».