12

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}
  • \fullexpandargs is a macro from xstring why do you have it here? – percusse Mar 31 '15 at 17:20
  • @percusse I removed it. It was a mistake. – Jonathan Komar Apr 1 '15 at 7:11
8
+200

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.

  • Thanks for the great explanation! Such a refreshing answer…until the end haha. Despite your report, I am going to keep my hope alive for a solution. pgfplotstable has too much potential for my purposes :) – Jonathan Komar Apr 2 '15 at 8:09
  • Awesome. So it is possible. Is it also possible to post-process cell content starting after row 0? My intention: I'd like to format row 0 (the new header row) bold and orange. Everything else should be black and not formatted by the pgf settings. You mentioned "reseting" row 0… – Jonathan Komar Apr 7 '15 at 5:24
  • @macmadness86 It is, as usual, possible to post-process cell content starting at row 0. So you need to exclude row 0 by hand if you want to post-process cell content starting after row 0. But if you want Everything else black and not formatted then it is called formatting only row 0, which is demonstrated in the second section above. – Symbol 1 Apr 7 '15 at 7:06
  • Thanks for your latest update! It is helpful to see different ways of doing things. I found another way to highlight specific rows. Please don't delete anything! I think this is a great reference. – Jonathan Komar Apr 7 '15 at 7:16
7

I think you have too high expectations from pgfplotstable. While it is certainly true that it does many amazing things, it is still just a wrapper around the typical TeX tabular and table mechanisms. So in the end what you end up with is an auto generated tabular code. And you have to use the package towards that mindset.

Here for example, you are changing the colors of everything twice since it is the way to go in the usual colortbl and so on package practice. The idea of this package is automation of certain properties of the table, not offering an alternative syntax hence you have to really pay attention to which property is set when and where. You can intercept values of the table at one place but you have to change the formatting at somewhere else. And that takes some time to get used to.

\documentclass{article}
\usepackage{booktabs,pgfplotstable}
\pgfplotsset{compat=1.12}
\makeatletter
\pgfplotstableset{
  paint row/.style={
    /pgfplots/table/typeset cell/.code={%
      \ifnum\c@pgfplotstable@colindex=\c@pgfplotstable@numcols\relax%
      \pgfkeyssetvalue{/pgfplots/table/@cell content}{\textcolor{#1}{##1}\\}%
      \else\pgfkeyssetvalue{/pgfplots/table/@cell content}{\textcolor{#1}{##1}&}%
      \fi%
    },
  }
}
\makeatother
\newcommand\test{\texttt{test}}
\begin{document}
\pgfplotstabletypeset[
  string type,
  every head row/.style={before row=\toprule,after row=\midrule,paint row=orange},
  every last row/.style={after row=\bottomrule},
  every row 1 column 1/.style={postproc cell content/.style={@cell content=\Large\test}},
]{
col1 col2 col3
here more stuff
for good looks
}
\end{document}

enter image description here

  • "So in the end what you end up with is an auto-generated tabular code." <-- The gravity of that statement is incredible. That is exactly why pgfplotstable is so great! I'm dealing with a multiple of documents (edited by many people at any time via subversion) that require the same look. Maybe my expectation are too high, but you can probably see why I am so excited! Thanks for your insights too. – Jonathan Komar Apr 2 '15 at 8:00
  • I tried typesetting your code and I ended up with: Package pgfkeys Error: Choice '1.12' unknown in choice key '/pgfplots/compat/anchors' I am going to ignore this key. – Jonathan Komar Apr 2 '15 at 8:06
  • @macmadness Update your pgfplots package or use compat=newest. But update it anyways. – percusse Apr 2 '15 at 11:02
  • Ok. After updating it worked, however, your answer still fails to meet my requirement of writing \texttt{}or some other macro in a cell. I mean, I know you got one cell to appear as if a macro were there, but that solution is unscalable (therefore defeating the purpose of pgfplotstable). I am dealing with hundreds of tables and I am trying to bring some order into their formatting. – Jonathan Komar Apr 2 '15 at 12:52
  • 1
    @macmadness86 OK that I understand now. The first row is a special row for pgfplotstable. It also names that column with whatever is given in the table. So it is trying to name the column with a typesetting command which obviously won't work. So a simple substitution works anywhere else (try it together with removing the every row 1 col 1 stuff). But for the header row you need to add more precautions. That's why you are having trouble. I would not do that anyways. Because this package is towards readily made data --> tabular automation. Not to replicate tabular. – percusse Apr 2 '15 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.