データから表(CSV / 数値揃え)

数字の列を r(右揃え)で組むのは、LaTeX の表でもっともよくある間違いです。182.595.01450.257 を右揃えで並べると、小数点は ちょうど数字 1 桁ぶん(5pt)ずれ、単独の 7 は一の位ではなく小数第一位の場所に落ちます。実測して確かめられる話です。直し方は列指定を一文字変えるだけ——siunitxS にすると、小数点の位置は 0.001pt 以内でそろいます。このページは、その数値揃えと、.csv をコンパイル時に読み込んで表にする csvsimplepgfplotstabledatatool、そして tabularray の現代的なやり方を扱います。データはデータファイルに置いたまま、LaTeX に読ませるのが本筋です。

LaTeX の表で小数点を揃える——siunitx の S 列

\usepackage{siunitx} を読み込み、列指定の rS に置き換えるだけです。S 列は数値を解析して 小数点(decimal marker)の位置でそろえる ため、桁数の違う数字が並んでも列が読めるようになります。実際に PDF から文字の座標を読み出して比べると差は歴然です。r 列では 182.595.0 の小数点が x = 62.0、1450.25 の小数点は x = 57.0——約 5pt、ちょうど数字 1 桁ぶん ずれています。しかも 7 は一の位ではなく小数第一位の位置に置かれます。同じデータを S[table-format=4.2] で組むと、四つの数の一の位はすべて x = 122.11 で終わり、小数点はすべて同じ位置から始まります。ずれは 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}

要点は二つです。第一に table-format=整数部.小数部 を、その列の最大の値に合わせて指定すること。指定を忘れると S 単独でも動きますが、列幅の確保が甘くなります(同じ例で列幅が 88.44pt → 98.44pt に膨らみました)。逆に table-format=2.1 のように小さく指定しても siunitx 3 は エラーを出さず、揃え自体も保たれます——ただし数値が確保した枠からはみ出し、隣の列と衝突しやすくなります。第二に、数値と間違われうるものは波括弧 {…} で保護する こと。siunitx の説明書は「その素材が数の一部と取り違えられうるなら、波括弧で保護すべきである」と書いています。逆にいえば {Sample} のような普通の文字列は、いまの v3 では波括弧なしでも正しく中央に置かれます。

「取り違えられうる」の実例を測ってみます。見出しに 2024 sales と波括弧なしで書くと、siunitx は先頭の 2024数値として拾い、他の行の数字とそろえたうえで残りの sales を後続の素材として置きます。結果、2024sales のあいだが 7.75bp 開き、波括弧で {2024 sales} と包んだ場合の 3.32bp(ふつうの語間)と比べて明らかに間延びします。エラーは出ません——壊れるのは見た目だけ なので、見出しに数字が入っている表では波括弧を忘れないでください。\multicolumn\multirow の引数のなかで数値を揃えたいときは S 列を使えないので、その場合はマクロ版の \tablenum[table-format=4.2]{1450.25} を使います。説明書によれば「事実上、S 列のマクロ版」です。

\num\qty\unit の書式、不確かさ(\num{1.234(5)} のような括弧付き表記)、指数の扱い、v2 と v3 の違いといった siunitx 全般は「単位(siunitx)」のページが持ち主です。本文中の量は \qty、表の中の数は S 列——と分担しておけば、文書全体で数字の書式がそろいます。表の側だけ覚えておきたいのは、単位は各セルではなく列見出しに書く という組版の作法です。booktabs の説明書も同じことを指針として挙げています。

なぜ CSV を LaTeX に読み込ませるのか

数字が一桁変わるたびに原稿を編集しなくて済むからです。実験結果や集計表は、たいてい表計算ソフトか測定器から CSV(カンマ区切りのテキスト) として出てきます。これを tabular のセルへ手で写すのは退屈なうえ、行を足すたび、値を直すたびに転記ミスの機会が増えます。発想を裏返して データはデータファイルに置いたまま、LaTeX に「読んで組め」と指示する ようにすれば、データを更新して再コンパイルするだけで表が追随します。同じ CSV を本文・スライド・付録で何度使い回しても、転記ミスは起こりません。論理構造と体裁を分けるという LaTeX の流儀の、そのままの延長です。

このページの例はすべて、次の小さな CSV ファイルを使います。1 行目が 見出し行 で、列名は productpriceweight。残りがデータです。.tex と同じディレクトリに data.csv として置けば、以下のコードはそのままコンパイルできます。

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

csvsimple で CSV を読み込む

\usepackage{csvsimple} を読み込み、\csvautotabular{data.csv} と一行書けば、CSV 全体がそのまま tabular になります。1 行目は罫線付きの見出しとして組まれ、中身をとりあえず眺めたいときにはこれで十分です。ただし整形の自由はほとんどありません。揃え方・罫線・出す列を自分で決めたいなら \csvreader ——csvsimple の本体はこちらです。なお TeX Live 2024 同梱の v2.6.0 は、\usepackage{csvsimple} だけだと 旧来の実装 csvsimple-legacy を読み込みますcsvsimple.sty\SetKeys{ legacy } と書かれています)。LaTeX3 実装を使いたければ \usepackage[l3]{csvsimple}\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}

\csvreader[オプション]{ファイル}{割り当て}{本体} の四つの部分がそれぞれ、読み込むファイル、列名からマクロへの束縛、1 行ごとに出力する中身を担当します。price=\price と書けば、本体のなかで \price がその行の値に展開されます。外枠はオプション側で、tabular= が列指定、table head= が見出し行、table foot= が末尾、late after line = \\各行の末尾に行終端 \\ を足す 指定です(最終行のあとに余計な改行を残さないための定石)。見出し名に空白や記号が含まれるときは、割り当てを空にして \csvcoli\csvcolii\csvcoliii(第 1・2・3 列)で番号参照できます。

このページで唯一の「ハマりどころ」がここにあります。上の例で tabular の値を 波括弧で囲っている のは飾りではありません。tabular = l r S[table-format=4.2] と裸で書くと、キー・値リストの区切りであるカンマが S[…] の内側のカンマと衝突し、! Paragraph ended before \NC@rewrite@S was complete. で止まります。続けて ! Missing $ inserted.! Package csvsimple Error: File ',' not existent… まで出るので、原因が列指定にあるとは気づきにくい。tabular = {l r S[table-format=4.2]} と波括弧で包めば通ります。S 列に限らず、角括弧つきの列指定をキー値の中に書くときは必ず波括弧で守る と覚えておくと安全です。

既定では 1 行目は 見出し行 として扱われ、データからは除かれます。見出し行のない CSV を読むときは、星付きの \csvreader* を使うと 1 行目もデータとして読まれます。ほかに、条件で行を選ぶ filter、割り当ての組を使い回す \csvstyle / \csvnames があり、表に限らず「1 行ごとの処理」全般に使えます——住所録から宛名ラベルを生成するような用途です。

pgfplotstable で数値の書式まで作り込む

数字の 見え方 自体を設計したいなら、もっとも強力なのは pgfplotstable です。pgfplots の一部で、\usepackage{pgfplotstable} で読み込み、中心の命令は一つ——\pgfplotstabletypeset[オプション]{data.csv}。CSV を読み、指定した桁数と数値表記に整形し、内部で tabular を組み立てて出力します。CSV を読ませるには区切り文字を col sep=comma で宣言してください(既定は空白区切りです)。制御はすべて キー・値 で行います。

オプション働き
col sep=commaCSV(カンマ区切り)として読む。既定は空白区切り
header=has colnames1 行目を列名として扱う。header=false なら見出し行なしとみなす
columnscolumns={a,b,…} で出力する列とその順序を選ぶ
columns/NAME/.stylecolumns/price/.style={…} のように、特定の列にだけ書式を適用する
column name印字される見出しを差し替える(CSV の列名とは別にできる)
fixed固定小数点。fixed zerofill で末尾のゼロを補い、precision=n で小数桁数を決める
sci指数(科学)表記で組む。sci zerofill は仮数部のゼロを補う
string typeテキスト列。数値としての整形を一切かけない
dec sep align小数点の位置で列を揃える(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}

product 列は string type(テキスト)、price は整数(precision=0)、weight は小数 1 桁でゼロ補完し dec sep align で小数点揃え。見出しは column name で差し替え、罫線は every head rowevery last row から booktabs の命令を差し込んでいます。ここで precision を変えるだけで同じデータの桁数が変わる ——CSV に手を触れずに数字の見え方を制御できるのが pgfplotstable の真骨頂です。同時に注意も要ります。この設定だと 1450.251,450.3 と丸められて出ます。桁を落とすのは書式の指定であって、四捨五入は黙って行われるので、有効数字を意図して決めてください(三桁区切りが不要なら 1000 sep={} を添えます)。

pgfplotstable は読み込んだ列から 計算列 を導くこともできます。create on use で「使うときに計算する列」を定義したり、columns/…/.style のなかで postproc cell content を使って値を後処理したり——要は表計算に近い作業を LaTeX の中で完結させられます。その分だけ構文は重くなるので、目安は 凝った数値表なら pgfplotstable、単純な CSV → 表なら csvsimple。同じ CSV から図も描くなら、pgfplots\addplot table と道具立てを共有できるのも利点です。

datatool——CSV をデータベースとして扱う

三つめの datatool は、CSV を データベース として読み込み、行ごとの処理(差し込み印刷のような作業)を得意とします。\usepackage{datatool} のうえで \DTLloaddb{名前}{data.csv} と書けば、名前付きのデータベースに取り込まれます。既定では 1 行目が見出しで、その列名が各値の キー になります。見出し行のない CSV は \DTLloaddb[noheader]{…}{…} とすると列名が Column1Column2… と自動で振られます。読み込んだあとは \DTLforeach{名前}{割り当て}{本体} で行を順に走査します。割り当ては「マクロ = 列名」の向きで、\DTLforeach{db}{\Product=product,\Price=price}{…} のように書き、本体では \Product\Price がその行の値に展開されます。

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}

ここでは CSV を goods として読み込み、tabular の中の \DTLforeach が行ごとに「品名 & 価格」を出力しています。行終端の \\ が本体の末尾ではなく先頭にある ことに注意してください。末尾に置くと最後の反復が空の行を開いてしまい、直後の \bottomrule(や \hline)がその行のセルの中に落ちて ! Misplaced \noalign. で止まります。先頭に置き、ループの後ろに \\ を一つ足すのが安全な書き方です。datatool の本領は表組みよりも データの操作 の側にあり、数値の合計・平均、並べ替え、条件による行の除外をマクロで扱えるほか、参考文献や差し込み文書の生成にも使われます。逆に、CSV を表にするだけなら csvsimple のほうが簡潔です。

tabularray の Q 列で数値を揃える

tabularray\UseTblrLibrary{siunitx} を読み込むと、Q[si={table-format=4.2}] という形で S 列と同じ数値揃えを tblr 環境に持ち込めます。\UseTblrLibrary{booktabs} を併せれば \toprule\midrule\bottomrule もそのまま使え、書式は colspecrow{1}={font=\bfseries} のようにすべてキー・値で指定します。従来の >{…}\multirow のような構文を覚え直す必要がないのが大きな利点です。ただし tabularray そのもの(tblr の設計思想、width=、セル結合、rowsepcolsep)は「高機能な表環境」のページが持ち主なので、ここでは 数値列だけ に絞ります。

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}

どれを使うべきか

目的から選ぶのが早道です。データが数十行までで、手で書いた表の数値だけそろえたいなら S 列で足ります。CSV が更新されるなら読み込み系に移りましょう。おおまかな指針は次のとおりです。

  • 数値の小数点を揃えたいだけsiunitxS 列。手書きの tabular にそのまま挿せます。\multicolumn の中では \tablenum
  • 素朴に CSV → 表csvsimple\csvautotabular で即席、\csvreader で揃え方・罫線・列の選択まで制御。
  • 桁数や数値表記を作り込む/計算列が要るpgfplotstable。最強だが構文は重く、丸めは黙って起こります。
  • データ操作そのものが目的(合計・並べ替え・条件処理・差し込み文書) — datatool
  • キー・値で書きたい/既存の tblr 表に数値列を足したいtabularraysiunitx ライブラリの Q[si={…}]

どの方法でも、最終的に出てくるのは同じ tabular の語彙——列指定、&\\、罫線——です。読み込みが正しく動いているのに表が読みにくいなら、原因はたいてい罫線の側にあります。\hlinebooktabs の三本に替え、単位を列見出しに移すだけで、同じデータがはるかに読みやすくなります。ページ幅に収まらない表は「高機能な表環境」、ページをまたぐ長い表は「ページをまたぐ表」、キャプションと配置は「表の配置・整形」を参照してください。