Tableaux à partir de données

Composer une colonne de nombres avec r est l’erreur la plus fréquente dans les tableaux LaTeX. Alignez 182.5, 95.0, 1450.25 et 7 à droite et les séparateurs décimaux se retrouvent décalés d’exactement un chiffre (5pt), tandis que le 7 isolé atterrit au rang des dixièmes au lieu des unités. Cela se mesure. Le remède tient en un caractère de la spécification de colonne : passez à la colonne S de siunitx et les séparateurs décimaux s’alignent à 0,001pt près. Cette page traite de cet alignement, puis de la lecture d’un .csv à la compilation avec csvsimple, pgfplotstable et datatool, et enfin de l’approche moderne de tabularray. Le principe reste le même : laisser les données dans le fichier de données et les faire lire par LaTeX.

Aligner les nombres sur la virgule — la colonne S de siunitx

Chargez \usepackage{siunitx} et remplacez le r de la spécification de colonne par S. La colonne S analyse le nombre et l’aligne sur le séparateur décimal, si bien qu’une colonne dont les entrées ont des nombres de chiffres différents redevient lisible. Relisez les coordonnées des glyphes dans le PDF et l’écart saute aux yeux. Dans une colonne r, les séparateurs décimaux de 182.5 et 95.0 sont à x = 62,0 tandis que celui de 1450.25 est à x = 57,0 — environ 5pt d’écart, exactement la largeur d’un chiffre — et le 7 se place au rang des dixièmes plutôt qu’à celui des unités. Avec les mêmes données en S[table-format=4.2], les quatre chiffres des unités se terminent tous à x = 122,11 et chaque séparateur décimal part du même endroit. La dispersion est inférieure à 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}

Deux points comptent. D’abord, régler table-format=<chiffres entiers>.<décimales> sur la plus grande valeur de la colonne. Un S nu fonctionne, mais réserve moins bien la largeur : dans le même exemple, la colonne est passée de 88,44pt à 98,44pt. À l’inverse, une valeur trop petite comme table-format=2.1 ne provoque aucune erreur dans siunitx 3 et l’alignement tient toujours ; le nombre déborde simplement de l’espace réservé et risque de heurter la colonne voisine. Ensuite, protéger par des accolades {…} tout ce qui pourrait passer pour un nombre. Le manuel de siunitx le dit exactement ainsi : si le matériau peut être pris pour une partie d’un nombre, il doit être protégé par des accolades. À l’inverse, un texte ordinaire comme Sample est désormais centré correctement en v3 même sans accolades.

Voici à quoi ressemble, mesuré, ce « pourrait être pris pour ». Écrivez une tête de colonne 2024 sales sans accolades et siunitx récupère le 2024 initial comme nombre, l’aligne sur les chiffres des autres rangées, puis compose le sales restant comme matériau suiveur. L’écart entre 2024 et sales s’ouvre à 7,75bp, contre 3,32bp — une espace-mot ordinaire — lorsque le tout est enveloppé en {2024 sales}. Aucune erreur n’est signalée : seule l’apparence casse, alors n’oubliez pas les accolades sur un intitulé contenant un nombre. Dans l’argument de \multicolumn ou \multirow, la colonne S est inutilisable ; on emploie alors la forme macro \tablenum[table-format=4.2]{1450.25}, que le manuel décrit comme « en effet une version macro de la colonne S ».

Le package siunitx dans son ensemble — la syntaxe de \num, \qty et \unit, les incertitudes en notation entre parenthèses comme \num{1.234(5)}, les exposants, les différences entre v2 et v3 — relève de la page « Unités (siunitx) ». Répartissez le travail ainsi : \qty pour les grandeurs dans le texte, la colonne S pour les nombres dans les tableaux, et le style numérique reste cohérent dans tout le document. Côté tableau, une seule convention typographique est à retenir : l’unité va dans la tête de colonne, pas dans chaque cellule. Le manuel de booktabs inscrit la même règle parmi ses consignes.

Pourquoi faire lire le CSV par LaTeX

Parce qu’on cesse alors de modifier le manuscrit chaque fois qu’un chiffre change. Les résultats d’expérience et les tableaux de synthèse sortent presque toujours d’un tableur ou d’un instrument sous forme de CSV (texte séparé par des virgules). Les recopier à la main dans des cellules tabular est fastidieux, et chaque rangée ajoutée, chaque valeur corrigée est une occasion de plus de se tromper. Renversez la perspective — laissez les données dans le fichier de données et demandez à LaTeX de les lire et de les composer — et il suffit de mettre les données à jour et de recompiler pour que le tableau suive. Réutilisez le même CSV dans le texte, dans des diapositives et en annexe autant que vous voulez, sans erreur de transcription. C’est le prolongement direct de l’habitude LaTeX de séparer structure logique et apparence.

Tous les exemples de cette page utilisent le petit fichier CSV ci-dessous. Sa première ligne est la ligne d’en-tête, qui nomme les colonnes product, price et weight ; le reste est constitué de données. Enregistrez-le sous data.csv à côté de votre .tex et le code qui suit se compile tel quel.

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

Lire un CSV avec csvsimple

Chargez \usepackage{csvsimple}, écrivez la seule ligne \csvautotabular{data.csv}, et tout le CSV devient un tabular. La première ligne est composée en tête de tableau avec des filets, ce qui suffit amplement pour jeter un œil au contenu. Ce qui manque, c’est la maîtrise de la mise en forme. Pour décider de l’alignement, des filets et des colonnes à imprimer, utilisez \csvreader — la véritable pièce maîtresse du package. Un détail à connaître : dans la v2.6.0 livrée avec TeX Live 2024, un simple \usepackage{csvsimple} charge l’ancienne implémentation csvsimple-legacy ; csvsimple.sty contient la ligne \SetKeys{ legacy }. Pour l’implémentation LaTeX3, il faut la demander : \usepackage[l3]{csvsimple} ou \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}

Les quatre parties de \csvreader[options]{fichier}{affectations}{corps} désignent, dans l’ordre, le fichier à lire, la liaison des noms de colonnes à des macros, et ce qu’il faut émettre pour chaque rangée. Écrivez price=\price et \price s’étend, dans le corps, à la valeur de cette rangée. Le cadre vient des options : tabular= est la spécification de colonnes, table head= la ligne d’en-tête, table foot= la clôture, et late after line = \\ ajoute un terminateur de rangée \\ à chaque ligne — l’idiome qui évite un saut superflu après la dernière rangée. Si les noms d’en-tête contiennent espaces ou symboles, laissez les affectations vides et adressez les colonnes par numéro avec \csvcoli, \csvcolii, \csvcoliii : première, deuxième, troisième colonne.

Voici le seul vrai piège de cette page. Les accolades autour de la valeur de tabular dans l’exemple ci-dessus ne sont pas décoratives. Écrivez-la nue, tabular = l r S[table-format=4.2], et la virgule qui sépare les paires clé-valeur entre en conflit avec celle contenue dans S[…] ; la compilation s’arrête sur ! Paragraph ended before \NC@rewrite@S was complete. Suivent ! Missing $ inserted. puis ! Package csvsimple Error: File ',' not existent…, si bien qu’on soupçonne difficilement la spécification de colonnes. Avec les accolades — tabular = {l r S[table-format=4.2]} — tout passe. L’habitude prudente dépasse le cas de S : dès qu’une spécification de colonnes comportant des crochets figure dans une liste clé-valeur, protégez-la par des accolades.

Par défaut, la première ligne est traitée comme un en-tête et exclue des données. Pour un CSV dépourvu de ligne d’en-tête, la forme étoilée \csvreader* lit aussi la première ligne comme des données. Il existe par ailleurs filter pour sélectionner des rangées selon une condition, et \csvstyle / \csvnames pour réutiliser un jeu d’affectations — des possibilités qui débordent le cadre des tableaux vers le traitement ligne par ligne en général, par exemple la génération d’étiquettes d’adresse à partir d’un carnet de contacts.

Façonner le format des nombres avec pgfplotstable

Pour concevoir l’apparence des nombres eux-mêmes, l’option la plus puissante est pgfplotstable. Composant de pgfplots, il se charge par \usepackage{pgfplotstable} et repose sur une commande centrale : \pgfplotstabletypeset[options]{data.csv}. Elle lit le CSV, le met en forme à la précision et au style numérique demandés, et assemble en interne un tabular en sortie. Pour lire un CSV, il faut déclarer le séparateur avec col sep=comma — le défaut est la séparation par espaces. Tout se pilote par options clé-valeur.

OptionEffet
col sep=commaLire en CSV (séparé par des virgules) ; le défaut est l’espace
header=has colnamesTraiter la ligne 1 comme noms de colonnes ; header=false : pas d’en-tête
columnscolumns={a,b,…} choisit les colonnes à imprimer et leur ordre
columns/NAME/.stylecolumns/price/.style={…} applique une mise en forme à une colonne nommée
column nameRemplace l’intitulé imprimé, indépendamment du nom de colonne du CSV
fixedVirgule fixe ; fixed zerofill complète les zéros finaux et precision=n fixe les décimales
sciComposer en notation scientifique (exposant) ; sci zerofill complète la mantisse
string typeColonne de texte ; aucune mise en forme numérique n’est appliquée
dec sep alignAligne la colonne sur le séparateur décimal (nécessite 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 colonne product est en string type (texte), price en entier (precision=0), et weight avec une décimale complétée par des zéros, alignée par dec sep align. Les intitulés sont remplacés par column name, et les filets viennent de commandes booktabs injectées via every head row et every last row. Changez precision seul et le nombre de chiffres des mêmes données change : maîtriser l’aspect des nombres sans toucher au CSV, c’est précisément la spécialité de pgfplotstable. Cela demande aussi de la vigilance : avec ces réglages, 1450.25 s’imprime 1,450.3, arrondi. Retirer des chiffres est une consigne de mise en forme, et l’arrondi se fait en silence : choisissez donc vos chiffres significatifs en connaissance de cause. (Ajoutez 1000 sep={} si vous ne voulez pas de séparateur de milliers.)

pgfplotstable sait aussi dériver des colonnes calculées de celles qu’il lit : définir une colonne calculée au moment de l’usage avec create on use, ou post-traiter les valeurs dans columns/…/.style via postproc cell content. Bref, du travail de tableur entièrement à l’intérieur de LaTeX. Cette puissance alourdit la syntaxe, d’où la règle empirique : pgfplotstable pour les tableaux numériques élaborés, csvsimple pour le simple CSV-vers-tableau. Si le même CSV sert aussi à tracer des courbes, partager l’outillage avec \addplot table de pgfplots plaide encore en sa faveur.

datatool — traiter le CSV comme une base de données

Le troisième package, datatool, lit un CSV comme une base de données et excelle dans le traitement rangée par rangée — le travail de type publipostage. Après \usepackage{datatool}, \DTLloaddb{nom}{data.csv} charge le fichier dans une base nommée. Par défaut, la première ligne est l’en-tête, et ses noms de colonnes deviennent les clés de chaque valeur. Pour un CSV sans en-tête, \DTLloaddb[noheader]{…}{…} nomme automatiquement les colonnes Column1, Column2, … Une fois chargée, \DTLforeach{nom}{affectations}{corps} parcourt les rangées. Les affectations s’écrivent « macro = nom de colonne », comme dans \DTLforeach{db}{\Product=product,\Price=price}{…} ; dans le corps, \Product et \Price s’étendent aux valeurs de la rangée courante.

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}

Ici le CSV est chargé sous le nom goods, et le \DTLforeach placé dans le tabular émet « produit & prix » pour chaque rangée. Notez que le terminateur de rangée \\ se trouve au début du corps, et non à la fin. Placé à la fin, la dernière itération ouvre une rangée vide, si bien que le \bottomrule (ou \hline) qui suit tombe dans une cellule de cette rangée et la compilation s’arrête sur ! Misplaced \noalign. Le mettre en tête et ajouter un \\ après la boucle est la forme sûre. La vraie force de datatool tient moins à la composition de tableaux qu’à la manipulation des données : sommes et moyennes, tris, exclusion de rangées sous condition, le tout par macros, sans compter la génération de bibliographies et de documents fusionnés. À l’inverse, s’il s’agit seulement de transformer un CSV en tableau, csvsimple est plus concis.

Aligner les nombres avec la colonne Q de tabularray

Chargez \UseTblrLibrary{siunitx} et tabularray apporte dans l’environnement tblr le même alignement décimal que la colonne S, sous la forme Q[si={table-format=4.2}]. Ajoutez \UseTblrLibrary{booktabs} et \toprule / \midrule / \bottomrule fonctionnent comme d’habitude, la mise en forme étant entièrement donnée en clé-valeur, par exemple via colspec ou row{1}={font=\bfseries}. Le grand attrait est de ne pas avoir à réapprendre une syntaxe comme >{…} ou \multirow. Mais tabularray lui-même — la conception de tblr, width=, la fusion de cellules, rowsep et colsep — relève de la page « Environnements de tableaux avancés » ; on s’en tient donc ici aux colonnes numériques.

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}

Lequel utiliser

Choisir selon l’usage va le plus vite. Si les données tiennent en quelques dizaines de rangées et qu’il s’agit seulement d’aligner les nombres d’un tableau écrit à la main, la colonne S suffit. Si le CSV évolue sans cesse, passez à l’un des lecteurs. Voici un repère grossier.

  • Vous voulez seulement aligner les séparateurs décimaux — la colonne S de siunitx. Elle s’insère telle quelle dans un tabular écrit à la main ; dans \multicolumn, employez \tablenum.
  • Simple CSV vers tableaucsvsimple. \csvautotabular pour un résultat immédiat, \csvreader quand il faut maîtriser alignement, filets et choix des colonnes.
  • Façonner le nombre de chiffres et le style numérique, ou des colonnes calculéespgfplotstable. Le plus puissant, mais la syntaxe est lourde et l’arrondi silencieux.
  • La manipulation des données est le but (totaux, tris, traitement conditionnel, publipostage) — datatool.
  • Vous préférez la syntaxe clé-valeur, ou une colonne numérique dans un tblr existanttabularray avec la bibliothèque siunitx et Q[si={…}].

Quelle que soit la méthode, ce qui sort au bout est le même vocabulaire tabular : la spécification de colonnes, &, \\ et les filets. Si la lecture fonctionne mais que le tableau reste difficile à lire, la cause est le plus souvent du côté des filets : remplacez \hline par les trois filets de booktabs et déplacez les unités dans les têtes de colonnes, et les mêmes données deviennent bien plus lisibles. Pour un tableau trop large pour la page, voir « Environnements de tableaux avancés » ; pour un tableau qui franchit un saut de page, « Tableaux sur plusieurs pages » ; pour les légendes et le placement, « Placement et mise en forme des tableaux ».