pgfplots
has a library units
that allows to typeset units in axis labels provided through x unit=<unit>
. Through the key \pgfplotsset{unit code/.code={\si{#1}}}
, it can be made to typeset the units using the excellent siunitx
.
My problem is the following: When I provide units with negative exponents, such as \metre\per\second
, they are typeset as m/s
, and not ms^{-1}
as I would like (and as is the standard behaviour of siunitx
). I've tried setting the unit code
to \si[per-mode=reciprocal]{#1}
, but it doesn't change the output. If I supply the unit using x unit=\si{\metre\per\second}
, I get the desired output, but I would rather not call the \si
command explicitly, as in my eyes it would defeat the purpose of using the units
library if I end up typesetting the units myself anyway.
MWE:
\documentclass{article}
\usepackage{siunitx}
\usepackage{pgfplots}
\usepgfplotslibrary{units}
\pgfplotsset{unit code/.code={\si{#1}}}
\begin{document}
\begin{tikzpicture}
\begin{axis}[
x unit=\metre\per\second, %This doesn't do what I want
y unit=\si{\metre\per\second}] %This does, but I don't want to type "\si{...}"
\end{axis}
\end{tikzpicture}
\end{document}
Best Answer
It seems that
pgfplots
passes some additional information through, rather than just the units themselves. This confusessiunitx
, which is looking for a unit only (otherwise it bails out of processing the units). So we need to strip out the additional information. As mentioned in pgfplots - x unit with siunitx, we also need to expand the information passed through as it is 'stored' bypgfplots
:(The
.code 2 args
here is used to throw away the first piece of information in#1
, which is where the main issue is.)For those who would know everything, the full story. If you take a peek at what
#1
in the above actually is, you get{}{\pgfkeysvalueof {/pgfplots/x\space unit}}
. Now, the problem is the braces. The first stage ofsiunitx
processing is to\edef
everything, which leads to{}{\metre\per\second}
(\pgfkeysvalueof
is expandable and the unit macros are protected). The second stepsiunitx
does is to look for anything that is not a unit macro, which I call 'literal' input. That leads to{}{}
, which is not empty and so looks like literal input. So the first 'argument' in the solution throws away the first set of braces, and unbraces the second argument. That means that the value then contains no 'literal' items, and is processed bysiunitx
using the unit processor as desired.Life gets a bit more complicated if there is a literal part to the unit, as in some circumstances the expansion process might take place at the 'wrong' time. That happens for example if there is a need to search-and-replace
.
in a literal unit. Forcing the expansion 'up front' deals with that too.