[Tex/LaTex] How to use variables (or formatting commands) in a header row of a \pgfplotstable table

pgfplotstabletables

My first reaction to reading the pgfplotstable manual was:

Why would anybody in their right mind use the tabular environment for generating tables?

Advantages of pgfplotstable

  • No need to specify number of columns
  • Global formatting settings
  • Local formatting settings that can override global settings when necessary (etc. longtable needed, changes in cell alignment, etc.)

In my experiments with pgfplotstable, I've run into some bumps:

Problem

  • columns/<name>/.style is the issue. Formatting commands and new commands cannot be used as column identifiers (called "names" in pgfplotstable). I need to find a way to keep the text names in the header row (for pdf output), but tell pgfplotstable to use column indexes (e.g. 0,1,2,etc.) for uniquely identifying the columns.
  • I use a \texttt{} in my command and the file fails to typeset.
  • I also use \texttt{} inside of a \newcommand to demonstrate both cases.

Example of Issue

Given:

\newcommand\test{\texttt{hello}.bye}
\pgfplotstabletypeset{%
col1 & \test{} & col3\\
here & more & stuff\\
}%

Results in:

columns/col1/.style={}
columns/\texttt{hello}.bye/.style={} % Not a valid column name!
columns/col3/.style={}

Obviously the second column (index 1) does not work as Percusse mentioned in a comment.

This leaves me with the conlusion that the solution might involve automatically stripping the formatting from column names OR forcing pgfplotstable to use indexes (0,1,2, etc.) in place of names. In either case, the columns must become valid keys for pgfplotstable to reference:**

columns/col1/.style={}
columns/hello.bye/.style={}
columns/col3/.style={}

OR

columns/0/.style={}
columns/1/.style={}
columns/2/.style={}

Example that works

Desired result: replace a cell with \test{}.

\documentclass{article}
\usepackage{fontspec}
\usepackage{booktabs}
\usepackage{pgfplotstable}
\pgfplotstableset{% Global config
    every head row/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
    col sep=&,
    row sep=\\,
    column type=l,
    column type={>{\fontseries{bx}\selectfont\color{orange}}l}, %see sec 2.6 for defining column types
    string type,
    postproc cell content/.append style={ % see sec 3.2
    /pgfplots/table/@cell content/.add={\fontseries{\seriesdefault}\selectfont\color{black}}{}}
}%
\newcommand\test{\texttt{test}.bye}

\begin{document}
\pgfplotstabletypeset{%
col1 & col2 & col3\\ % <-- I want to replace a cell with \test{} in a header row
here & more & stuff\\
for & good & looks\\
}%
\end{document}

Example that does not work

Desired result: replace a cell with \test{}.

\documentclass{article}
\usepackage{fontspec}
\usepackage{booktabs}
\usepackage{pgfplotstable}
\pgfplotstableset{% Global config
    every head row/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
    col sep=&,
    row sep=\\,
    column type=l,
    column type={>{\fontseries{bx}\selectfont\color{orange}}l}, %see sec 2.6 for defining column types
    string type,
    postproc cell content/.append style={ % see sec 3.2
    /pgfplots/table/@cell content/.add={\fontseries{\seriesdefault}\selectfont\color{black}}{}}
}%
\newcommand\test{\texttt{test}.bye}

\begin{document}
\pgfplotstabletypeset{%
col1 & \test{} & col3\\ % <-- I want to replace a cell with \test{} in a header row
here & more & stuff\\
for & good & looks\\
}%
\end{document}

Huge thanks to Symbol 1 and Guuk for making this possible:

The ability to:

  • globally format specific rows (see Pgfplotstable one row in bold)
  • override global formatting with standard latex formatting (important detail: also in header rows by cheating—see Symbol 1's answer)
  • include commands within header rows (again cheating—see Symbol's answer)

enter image description here

Demonstration

\documentclass{article}
\usepackage{booktabs}
\usepackage{pgfplotstable}
\usepgflibrary{decorations.fractals}

\pgfplotstableset{
    string type,col sep=&,row sep=\\,
    header=false,
    every head row/.style={output empty row},
    every row no 0/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
    highlightrow/.style={
        postproc cell content/.append code={
           \count0=\pgfplotstablerow
            \advance\count0 by1
            \ifnum\count0=#1
            \pgfkeysalso{@cell content/.add={\bfseries\color{red}}{}}
            \fi
        },
    },
    highlightrow={1}
}%

\begin{document}

\pgfplotstabletypeset{
    \LaTeX & \textit{italic} & \textcolor{orange}{orange} & \reflectbox{reflect} & $e^{i\pi}+1=0$ & \tikz\draw[decoration=Koch snowflake]decorate{decorate{decorate{decorate{(0,0)--(1,0)}}}}; \\
    Lorem & ipsum & dolor & sit & amet & consectetur \\
    adipisicing & elit & sed & do & eiusmod & tempor \\
}

\end{document}

Best Answer

Show me a solution

This is "a" solution that uses row no 0 as the header. (usually the header is row no -1). So PgfplotsTable will no longer put the entries inside \csname. Notice that you need to reset the style for row no 0 if you assign some style for some columns.

\documentclass{article}
\usepackage{booktabs}
\usepackage{pgfplotstable}
\usepgflibrary{decorations.fractals}

\begin{document}

\pgfplotstabletypeset[
    string type,col sep=&,row sep=\\,
    header=false,
    every head row/.style={output empty row},
    every row no 0/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
]{
    \LaTeX & \textit{italic} & \textcolor{orange}{orange} & \reflectbox{reflect} & $e^{i\pi}+1=0$ & \tikz\draw[decoration=Koch snowflake]decorate{decorate{decorate{decorate{(0,0)--(1,0)}}}}; \\
    Lorem & ipsum & dolor & sit & amet & consectetur \\
    adipisicing & elit & sed & do & eiusmod & tempor \\
}

\end{document}

Combining with cell-processing (update)

There are ≥3 stages of cell-processing. Perhaps typeset cell is the latest one. (It is even too late to be a processing because there is nothing to do with the literal content then.) Anyway, these keys will probably work as usual.

œ

\pgfplotstabletypeset[
    string type,col sep=&,row sep=\\,
    header=false,
    every head row/.style={output empty row},
    every row no 0/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
    /pgfplots/table/typeset cell/.code={
        \ifnum\pgfplotstablerow=0
            \ifnum\pgfplotstablecol=\pgfplotstablecols
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\bfseries\color{orange}#1\\}
            \else
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{\bfseries\color{orange}#1&}
            \fi
        \else
            \ifnum\pgfplotstablecol=\pgfplotstablecols
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{#1\\}
            \else
                \pgfkeyssetvalue{/pgfplots/table/@cell content}{#1&}
            \fi
        \fi
    },
]{
    \LaTeX & \textit{italic} & \textcolor{red}{R}\textcolor{green}{G}\textcolor{blue}{B} & \reflectbox{reflect} & $e^{i\pi}+1=0$ & \tikz\draw[decoration=Koch snowflake]decorate{decorate{decorate{decorate{(0,0)--(1,0)}}}}; \\
    Lorem & ipsum & dolor & sit & amet & consectetur \\
    adipisicing & elit & sed & do & eiusmod & tempor \\
}

Show me the official Solution

Same output as before

\pgfplotstabletypeset[
    string type,col sep=&,row sep=\\,
    every head row/.style={before row=\toprule,after row=\midrule},
    every last row/.style={after row=\bottomrule},
    columns/LaTeX/.style         ={column name=\LaTeX},
    columns/italic/.style        ={column name=\textit{italic}},
    columns/orange/.style        ={column name=\textcolor{orange}{orange}},
    columns/reflect/.style       ={column name=\reflectbox{reflect}},
    columns/eipi+1/.style        ={column name={$e^{i\pi}+1=0$}},
    columns/Koch snowflake/.style={column name={\tikz\draw[decoration=Koch snowflake]decorate{decorate{decorate{decorate{(0,0)--(1,0)}}}};}},
]{
    LaTeX & itslic & orange & reflect & eipi+1 & Koch snowflake \\
    Lorem & ipsum & dolor & sit & amet & consectetur \\
    adipisicing & elit & sed & do & eiusmod & tempor \\
}

Show me the reason

Turns out that this is a basic TeX phenomenon which is totally reasonable but no one will ever, logically, think about it. In the following example, I tried to pack \ttfamily into the name of a control sequence.

\documentclass{minimal}
\begin{document}
    \expandafter\let\csname\ttfamily test\endcsname\relax
\end{document}

Then

  • \ttfamily is expanded;
  • a \protect is unveiled;
  • TeX read \protect before \endcsname and complained.
./236210 copy.tex:7: Missing \endcsname inserted.
<to be read again> 
                   \protect 
l.7   \expandafter\let\csname\ttfamily
                                      test\endcsname\relax
?

(\protect is \relax, it cannot be part of csname. Otherwise it is logically contradictive, right?)


Back to pgfplotstable. This package did plenty of magics and inevitably it put the entry inside a csname. Then BOOM: Since \test led to \texttt to \ttfamily and to \protect, TeX complained!


So the next question may be whether I can replace \newcommand by \edef? For instance

\xdef\test{{\ttfamily test}}

The answer is still NO but for different reason. In fact, due to the design of font-switches, there is internally something like

\xdef \font@name {\curr@fontshape}

But since there is another \edef outside, the following line is executed

\xdef \OT1/cmr/m/n/10 {\OT1/cmr/m/n/10 }

So Once you apply \test... BOOM.


The following paraphrase even gives another error.

\xdef\test{\textit{test}}

Conclusion: Never rely on any specific package for, especially, tables.

Related Question