Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

How can I adjust the placement of y unit with pgfplots? I'm trying to put it near the top-left corner of the graph box. However, there may (but does not have to) be a scientific multiplier. As shown in the professionally type-set picture below, the unit should be displayed just after the multiplier and of course be lined up with it as if written as a whole: $10^3$[m]. In case values on y axis were smaller and exponential notation wasn't shown, the [m] unit should be still rendered above the top-left corner, but preferably closer to the left edge.

output graph

I'm aware of the fact that the [m] unit in the picture is drawn along with y axis label. I don't need to put the whole y label near the multiplier, but just the unit. In fact I don't need the axis label at all. Is it possible to control how the scientific multiplier is formatted and append a unit?

I'm not sure whether it's important, but for the sake of completeness, let me add that I have incorporated Jake's tick scaling solution from here to have only exponents, which are a multiple of 3.

MWE provided:

\documentclass{minimal}
\usepackage{pgfplots}
\usepgfplotslibrary{units}

\makeatletter

\newif\ifpgfplots@scaled@x@ticks@engineering
\pgfplots@scaled@x@ticks@engineeringfalse
\newif\ifpgfplots@scaled@y@ticks@engineering
\pgfplots@scaled@y@ticks@engineeringfalse
\newif\ifpgfplots@scaled@z@ticks@engineering
\pgfplots@scaled@z@ticks@engineeringfalse

\pgfplotsset{
    scaled x ticks/engineering/.code=
    \pgfplots@scaled@x@ticks@engineeringtrue,
    scaled y ticks/engineering/.code=
    \pgfplots@scaled@y@ticks@engineeringtrue,
    scaled z ticks/engineering/.code=
    \pgfplots@scaled@y@ticks@engineeringtrue,
    %    scaled ticks=engineering  % Uncomment this line if you want "engineering" to be on by default
}

\def\pgfplots@init@scaled@tick@for#1{%
    \global\def\pgfplots@glob@TMPa{0}%
    \expandafter\pgfplotslistcheckempty\csname pgfplots@prepared@tick@positions@major@#1\endcsname
    \ifpgfplotslistempty
    % we have no tick labels. Omit the tick scale label as well!
    \else
    \begingroup
    \ifcase\csname pgfplots@scaled@ticks@#1@choice\endcsname\relax
    % CASE 0 : scaled #1 ticks=false: do nothing here.
    \or
    % CASE 1 : scaled #1 ticks=true:
    %--------------------------------
    % the \pgfplots@xmin@unscaled@as@float  is set just before the data
    % scale transformation is initialised.
    %
    % The variables are empty if there is no datascale transformation.
    \expandafter\let\expandafter\pgfplots@cur@min@unscaled\csname pgfplots@#1min@unscaled@as@float\endcsname
    \expandafter\let\expandafter\pgfplots@cur@max@unscaled\csname pgfplots@#1max@unscaled@as@float\endcsname
    %
    \ifx\pgfplots@cur@min@unscaled\pgfutil@empty
    \edef\pgfplots@loc@TMPa{\csname pgfplots@#1min\endcsname}%
    \expandafter\pgfmathfloatparsenumber\expandafter{\pgfplots@loc@TMPa}%
    \let\pgfplots@cur@min@unscaled=\pgfmathresult
    \edef\pgfplots@loc@TMPa{\csname pgfplots@#1max\endcsname}%
    \expandafter\pgfmathfloatparsenumber\expandafter{\pgfplots@loc@TMPa}%
    \let\pgfplots@cur@max@unscaled=\pgfmathresult
    \fi
    %
    \expandafter\pgfmathfloat@decompose@E\pgfplots@cur@min@unscaled\relax\pgfmathfloat@a@E
    \expandafter\pgfmathfloat@decompose@E\pgfplots@cur@max@unscaled\relax\pgfmathfloat@b@E
    \pgfplots@init@scaled@tick@normalize@exponents
    \ifnum\pgfmathfloat@b@E<\pgfmathfloat@a@E
    \pgfmathfloat@b@E=\pgfmathfloat@a@E
    \fi
    \xdef\pgfplots@glob@TMPa{\pgfplots@scale@ticks@above@exponent}%
    \ifnum\pgfplots@glob@TMPa<\pgfmathfloat@b@E
    % ok, scale it:
    \expandafter\ifx % Check whether we're using engineering notation (restricting exponents to multiples of three)
    \csname ifpgfplots@scaled@#1@ticks@engineering\expandafter\endcsname
    \csname iftrue\endcsname
    \divide\pgfmathfloat@b@E by 3
    \multiply\pgfmathfloat@b@E by 3
    \fi
    \multiply\pgfmathfloat@b@E by-1
    \xdef\pgfplots@glob@TMPa{\the\pgfmathfloat@b@E}%
    \else
    \xdef\pgfplots@glob@TMPa{\pgfplots@scale@ticks@below@exponent}%
    \ifnum\pgfplots@glob@TMPa>\pgfmathfloat@b@E
    % ok, scale it:
    \expandafter\ifx % Check whether we're using engineering notation (restricting exponents to multiples of three)
    \csname ifpgfplots@scaled@#1@ticks@engineering\expandafter\endcsname
    \csname iftrue\endcsname
    \advance\pgfmathfloat@b@E by -2
    \divide\pgfmathfloat@b@E by 3
    \multiply\pgfmathfloat@b@E by 3
    \fi
    \multiply\pgfmathfloat@b@E by-1
    \xdef\pgfplots@glob@TMPa{\the\pgfmathfloat@b@E}%
    \else
    % no scaling necessary:
    \xdef\pgfplots@glob@TMPa{0}%
    \fi
    \fi
    \or
    % CASE 2 : scaled #1 ticks=base 10:
    %--------------------------------
    \c@pgf@counta=\csname pgfplots@scaled@ticks@#1@arg\endcsname\relax
    %\multiply\c@pgf@counta by-1
    \xdef\pgfplots@glob@TMPa{\the\c@pgf@counta}%
    \or
    % CASE 3 : scaled #1 ticks=real:
    %--------------------------------
    \pgfmathfloatparsenumber{\csname pgfplots@scaled@ticks@#1@arg\endcsname}%
    \global\let\pgfplots@glob@TMPa=\pgfmathresult
    \or
    % CASE 4 : scaled #1 ticks=manual:
    \expandafter\global\expandafter\let\expandafter\pgfplots@glob@TMPa\csname pgfplots@scaled@ticks@#1@arg\endcsname
    \fi
    \endgroup
    \fi
    \expandafter\let\csname pgfplots@tick@scale@#1\endcsname=\pgfplots@glob@TMPa%
}
\makeatother

\begin{document}
    \begin{tikzpicture}
        \begin{axis}[scaled ticks=engineering, y unit=m,height=4cm]
        \addplot [domain=0:1] {10000*\x};
        \end{axis}
    \end{tikzpicture}

    The multiplier should be printed as $\cdot10^3$[m] instead of merely $\cdot10^3$.
\end{document}
share|improve this question
    
So you'll never have a ylabel that contains anything other than the unit? In that case, the solution would probably be quite easy. If you do want to have the option to have a label along the axis and the unit next to the multiplier, one would have to redefine some stuff from the units library. Also, are you sure you want the square brackets around the unit? –  Jake Aug 11 '13 at 16:43
    
In this particular case I'll never need ylabel for description, because I'm trying to save space on sides. ylabel can be sacrificed for the purpose of holding a unit, while description appears in plot title. Square brackets aren't necessary anymore, if the unit is placed just after the multiplier. –  rook Aug 11 '13 at 18:54
add comment

2 Answers 2

up vote 4 down vote accepted
+50

Here's an approach that doesn't use the units library, but instead defines a new key y unit label=<text> that places the <text> behind the scale label (if it exists) or at the position of the scale label (if it doesn't).

If you put the following code in your preamble...

\makeatletter
\long\def\ifnodedefined#1#2#3{%
    \@ifundefined{pgf@sh@ns@#1}{#3}{#2}%
}

\pgfplotsset{
    y unit label/.style={
        y tick scale label style={
            name=y tick scale label,
            inner xsep=0pt
        },
        after end axis/.append code={
            \ifnodedefined{y tick scale label}{%
                \tikzset{
                    every y unit label/.append style={
                        anchor=base west, at=(y tick scale label.base east)
                    }
                }
            }{%
                \tikzset{
                    every y unit label/.append style={
                        /pgfplots/every y tick scale label
                    }
                }
            }
            \node [every y unit label] {#1};
        }
    }
}
\makeatother

... you can write the following ...

\begin{axis}[
        scaled ticks=engineering,
        y unit label=m,
        height=4cm,
        ]
\addplot [domain=0:1] {10000*\x};
\end{axis}

... to get ...

... or ...

\begin{axis}[
        scaled ticks=engineering,
        y unit label=m,
        height=4cm,
        ]
\addplot [domain=0:1] {10000*\x};
\end{axis}

... to get ...


Complete code:

\documentclass{article}
\usepackage{pgfplots}

\makeatletter

\newif\ifpgfplots@scaled@x@ticks@engineering
\pgfplots@scaled@x@ticks@engineeringfalse
\newif\ifpgfplots@scaled@y@ticks@engineering
\pgfplots@scaled@y@ticks@engineeringfalse
\newif\ifpgfplots@scaled@z@ticks@engineering
\pgfplots@scaled@z@ticks@engineeringfalse

\pgfplotsset{
    scaled x ticks/engineering/.code=
    \pgfplots@scaled@x@ticks@engineeringtrue,
    scaled y ticks/engineering/.code=
    \pgfplots@scaled@y@ticks@engineeringtrue,
    scaled z ticks/engineering/.code=
    \pgfplots@scaled@y@ticks@engineeringtrue,
    %    scaled ticks=engineering  % Uncomment this line if you want "engineering" to be on by default
}

\def\pgfplots@init@scaled@tick@for#1{%
    \global\def\pgfplots@glob@TMPa{0}%
    \expandafter\pgfplotslistcheckempty\csname pgfplots@prepared@tick@positions@major@#1\endcsname
    \ifpgfplotslistempty
    % we have no tick labels. Omit the tick scale label as well!
    \else
    \begingroup
    \ifcase\csname pgfplots@scaled@ticks@#1@choice\endcsname\relax
    % CASE 0 : scaled #1 ticks=false: do nothing here.
    \or
    % CASE 1 : scaled #1 ticks=true:
    %--------------------------------
    % the \pgfplots@xmin@unscaled@as@float  is set just before the data
    % scale transformation is initialised.
    %
    % The variables are empty if there is no datascale transformation.
    \expandafter\let\expandafter\pgfplots@cur@min@unscaled\csname pgfplots@#1min@unscaled@as@float\endcsname
    \expandafter\let\expandafter\pgfplots@cur@max@unscaled\csname pgfplots@#1max@unscaled@as@float\endcsname
    %
    \ifx\pgfplots@cur@min@unscaled\pgfutil@empty
    \edef\pgfplots@loc@TMPa{\csname pgfplots@#1min\endcsname}%
    \expandafter\pgfmathfloatparsenumber\expandafter{\pgfplots@loc@TMPa}%
    \let\pgfplots@cur@min@unscaled=\pgfmathresult
    \edef\pgfplots@loc@TMPa{\csname pgfplots@#1max\endcsname}%
    \expandafter\pgfmathfloatparsenumber\expandafter{\pgfplots@loc@TMPa}%
    \let\pgfplots@cur@max@unscaled=\pgfmathresult
    \fi
    %
    \expandafter\pgfmathfloat@decompose@E\pgfplots@cur@min@unscaled\relax\pgfmathfloat@a@E
    \expandafter\pgfmathfloat@decompose@E\pgfplots@cur@max@unscaled\relax\pgfmathfloat@b@E
    \pgfplots@init@scaled@tick@normalize@exponents
    \ifnum\pgfmathfloat@b@E<\pgfmathfloat@a@E
    \pgfmathfloat@b@E=\pgfmathfloat@a@E
    \fi
    \xdef\pgfplots@glob@TMPa{\pgfplots@scale@ticks@above@exponent}%
    \ifnum\pgfplots@glob@TMPa<\pgfmathfloat@b@E
    % ok, scale it:
    \expandafter\ifx % Check whether we're using engineering notation (restricting exponents to multiples of three)
    \csname ifpgfplots@scaled@#1@ticks@engineering\expandafter\endcsname
    \csname iftrue\endcsname
    \divide\pgfmathfloat@b@E by 3
    \multiply\pgfmathfloat@b@E by 3
    \fi
    \multiply\pgfmathfloat@b@E by-1
    \xdef\pgfplots@glob@TMPa{\the\pgfmathfloat@b@E}%
    \else
    \xdef\pgfplots@glob@TMPa{\pgfplots@scale@ticks@below@exponent}%
    \ifnum\pgfplots@glob@TMPa>\pgfmathfloat@b@E
    % ok, scale it:
    \expandafter\ifx % Check whether we're using engineering notation (restricting exponents to multiples of three)
    \csname ifpgfplots@scaled@#1@ticks@engineering\expandafter\endcsname
    \csname iftrue\endcsname
    \advance\pgfmathfloat@b@E by -2
    \divide\pgfmathfloat@b@E by 3
    \multiply\pgfmathfloat@b@E by 3
    \fi
    \multiply\pgfmathfloat@b@E by-1
    \xdef\pgfplots@glob@TMPa{\the\pgfmathfloat@b@E}%
    \else
    % no scaling necessary:
    \xdef\pgfplots@glob@TMPa{0}%
    \fi
    \fi
    \or
    % CASE 2 : scaled #1 ticks=base 10:
    %--------------------------------
    \c@pgf@counta=\csname pgfplots@scaled@ticks@#1@arg\endcsname\relax
    %\multiply\c@pgf@counta by-1
    \xdef\pgfplots@glob@TMPa{\the\c@pgf@counta}%
    \or
    % CASE 3 : scaled #1 ticks=real:
    %--------------------------------
    \pgfmathfloatparsenumber{\csname pgfplots@scaled@ticks@#1@arg\endcsname}%
    \global\let\pgfplots@glob@TMPa=\pgfmathresult
    \or
    % CASE 4 : scaled #1 ticks=manual:
    \expandafter\global\expandafter\let\expandafter\pgfplots@glob@TMPa\csname pgfplots@scaled@ticks@#1@arg\endcsname
    \fi
    \endgroup
    \fi
    \expandafter\let\csname pgfplots@tick@scale@#1\endcsname=\pgfplots@glob@TMPa%
}
\makeatother


\makeatletter
\long\def\ifnodedefined#1#2#3{%
    \@ifundefined{pgf@sh@ns@#1}{#3}{#2}%
}

\pgfplotsset{
    y unit label/.style={
        y tick scale label style={
            name=y tick scale label,
            inner xsep=0pt
        },
        after end axis/.append code={
            \ifnodedefined{y tick scale label}{%
                \tikzset{
                    every y unit label/.append style={
                        anchor=base west, at=(y tick scale label.base east)
                    }
                }
            }{%
                \tikzset{
                    every y unit label/.append style={
                        /pgfplots/every y tick scale label
                    }
                }
            }
            \node [every y unit label] {#1};
        }
    }
}
\makeatother


\pgfplotsset{compat=1.8}
\begin{document}
    \begin{tikzpicture}
        \begin{axis}[
                scaled ticks=engineering,
                y unit label=m,
                height=4cm,
                ]
        \addplot [domain=0:1] {10*\x};
        \end{axis}
    \end{tikzpicture}

\end{document}
share|improve this answer
    
Thank you for your time. This is exactly what was wanted. –  rook Aug 20 '13 at 7:18
add comment

Not a general approach, but a stopgap in case no other solution presents itself. In this case, turn off the y-axis label, and overlay the image with the desired text at the specified location. The relevant lines that changed relative to your MWE were the following:

\usepackage{stackengine}
\def\stackalignment{l}
\begin{document}
\topinset{[m]}{%
    \begin{tikzpicture}
        \begin{axis}[scaled ticks=engineering, height=4cm]
        \addplot [domain=0:1] {10000*\x};
        \end{axis}
    \end{tikzpicture}%
}{.03in}{.8in}

enter image description here

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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