# Make pgfplots typeset axis units as reciprocals using siunitx

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}


-

It seems that pgfplots passes some additional information through, rather than just the units themselves. This confuses siunitx, 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' by pgfplots:

\documentclass{article}
\usepackage{siunitx}
\usepackage{pgfplots}
\usepgfplotslibrary{units}
\makeatletter
\pgfplotsset{
unit code/.code 2 args=
\begingroup
\protected@edef\x{\endgroup\si{#2}}\x
}
\makeatother
\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}


(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 of siunitx processing is to \edef everything, which leads to {}{\metre\per\second} (\pgfkeysvalueof is expandable and the unit macros are protected). The second step siunitx 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 by siunitx 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.

-
By the way, I usually us x label and y label, so I've never actually seen this even though I regularly use pgfplots! – Joseph Wright Feb 26 '11 at 19:03
Unfortunately this doesn't work any more if I insert \usepackage[per-mode=fraction,fraction-function=\sfrac]{siunitx} in the preamble. And it gets worse when I add sticky-per=true to the options. – Thorsten Donig Feb 26 '11 at 19:07
@Thorsten: If you want to use \sfrac, you need to load the xfrac package. Due to the way the LaTeX2e kernel handles options, I strongly recommend setting this up after loading siunitx: \usepackage{siunitx,xfrac} first then \sisetup{per-mode=fraction,fraction-function=\sfrac,sticky-per=true} afterward (but still in the preamble). – Joseph Wright Feb 26 '11 at 19:30
@Jake: I've always done things by hand - perhaps I'll try this next time I need to do some plots (always useful to try new ideas) – Joseph Wright Feb 26 '11 at 19:31
@Thorsten: Just to clarify what I mean, siunitx does not load xfrac itself, so assumes that whatever you put for fraction-function = ... has been defined somewhere. The rest is up to the user (at least at the moment). – Joseph Wright Feb 26 '11 at 19:32