13

The automatic power-of-ten scaling of pgfplots is a great feature that unclutters the numeric ticks in the axis (see image). However since the exponent value (e.g. 10^3, 10^4) is difficult to keep consistency between plot that range over different but similar range (e.g. one plot with data between 8000 and 9000 and another between 11000 and 12000. Moreover if the units are SI one tends to confuse the prefixes if the exponent if it is not a multiple of 3.

Is there a way to force pgfplots to choose scaling only for power of ten that are also power of 10^3? For example, 10^6, 10^9, 10^12, but never 10^4, or 10^10.

In this example the power chosen (automatically by pgfplot was 10^10), how can I make it choose 10^9 automatically (with the corresponding change --multiplied by 10-- in the numbers beside the axis)

poweroften

12

The scaling is handled by a macro in pgfplotsticks.code.tex. You can edit this macro to implement the desired behaviour. Once the macro's been patched, you can say

\begin{axis}[scaled ticks=engineering]
\addplot {1/10000*rnd};
\end{axis}

to restrict the scaling factors to exponents that are multiples of three, the so called engineering notation:

With \addplot {1/100*rnd};:

And with \addplot {10000*rnd};:

Here's the code chunk you need to put in your preamble:

\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

And here's the complete code:

\documentclass{article}

\usepackage{pgfplots}
\pgfplotsset{compat=newest}


\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}
\pgfmathsetseed{1}
\begin{tikzpicture}
\begin{axis}[height=4cm, width=8cm]
\addplot {1/100000*x};
\end{axis}
\end{tikzpicture}

\end{document}
  • Great! it works. BTW, it works out-of-the-box without explicitly setting scaled ticks=engineering. In my opinion this should be default in pgfplots, together with a workaround for this tex.stackexchange.com/questions/70774/… – alfC Jul 18 '13 at 4:38
  • @alfC: Oh, it shouldn't have been on by default, I had accidentally set the boolean to true. You might want to open a feature request for adding this function to PGFPlots. – Jake Jul 18 '13 at 11:20
  • 2
    @alfC: The possible options are scaled ticks=true (the default, scales by a power of ten when the values are too large or small), false (no scaling), base 10:<number> (divides by 10^<number>), real:<number> (divides by <number>), manual:{<label>}{<code>} (allows custom scaling and labelling). The new option scale=engineering is the same as scale=true, with the restriction of only allowing exponents that are integer multiples of three. – Jake Jul 18 '13 at 18:19
  • 1
    @alfC: Ah yes, good point! I believe all it takes to fix this is to subtract 2 from the smallest exponent instead of 3. Could you test the edited function? – Jake Jul 19 '13 at 2:18
  • 1
    @alfC: I've put in a line and a comment that lets you turn this behaviour on by default. – Jake Jul 19 '13 at 12:04
5

EDIT 2

With this solution you have only to use the myaxis environment and the job is done with few line in the preamble.

\documentclass{standalone}

\usepackage{pgfplots}
\pgfplotsset{compat=newest}
\usepgflibrary{fixedpointarithmetic}
\usepackage{fp}

\makeatletter
\newcommand{\pgfplotsdrawaxis}{\pgfplots@draw@axis}
\makeatother

\pgfplotsset{after end axis/.append code={
  \pgfmathsetmacro{\maxexponent}{round(log10(\pgfkeysvalueof{/pgfplots/ymax}))}
  \pgfmathparse{-3*floor(\maxexponent/3)}
  \pgfmathsetmacro{\exponent}{\pgfmathresult}
  \def\axisdefaultticklabel{$\pgfmathprintnumber{\tick}$}
  \pgfplotsset{scaled y ticks=base 10:\exponent}
  \pgfplotsdrawaxis}}

\newenvironment{myaxis}[1][]
{\begin{axis}[scaled y ticks=false,#1]%
    \def\axisdefaultticklabel{}}
{\end{axis}}

\begin{document}

\begin{tikzpicture}[fixed point arithmetic]
\begin{myaxis}
\addplot {1000*x^2};
\end{myaxis}

\end{tikzpicture}

\end{document}

EDIT 1

This does the job but needs the specification of the value of ymax in the axis environment

\documentclass{standalone}

\usepackage{pgfplots}
\pgfplotsset{compat=newest}
\usepgflibrary{fixedpointarithmetic}
\usepackage{fp}

\begin{document}

\begin{tikzpicture}[fixed point arithmetic]
\begin{axis}[ymax=2e7]
\pgfmathsetmacro{\maxexponent}{round(log10(\pgfkeysvalueof{/pgfplots/ymax}))}
\pgfmathparse{-3*floor(\maxexponent/3)}%
\pgfmathsetmacro{\exponent}{\pgfmathresult}
\pgfplotsset{scaled y ticks=base 10:\exponent}
\addplot {1000000*x^2};
\end{axis}
\end{tikzpicture}

\end{document}

Original

You can use

scaled ticks=base 10:-9

or for separately change x and y factors scaled x ticks or scaled y ticks but you have to remove the line y tick label style={/pgf/number format/.cd,sci,precision=5} if you are using it in your plot.

  • (scaled ticks=base 10:-9 for the example.) That is too much control, I want pgfplots to choose that number automatically, but still reject things like 10^5, 10^10, 10^11. Some option like scaled ticks=base 10:{only multiple of 3} – alfC Jul 16 '13 at 8:18
  • @alfC answer updated – Red Jul 16 '13 at 12:04
  • Interesting solution, thanks, I accepted Jake's answer because it looks more standard (just another option instead of a new environment). – alfC Jul 19 '13 at 12:13

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.