18

How can I mark the maximum or minimum value of a function like x^2 in pgfplots?

\begin{tikzpicture}
   \begin{axis}
      \addplot+[mark=none] plot {x^2};
   \end{axis}
\end{tikzpicture}
Daniel
  • 183

2 Answers2

26

You can use the scatter mechanism for this, which allows you to execute code before each marker is drawn. If you set point meta rel=per plot, the macro \pgfplots@metamin contains the lowest meta value, and \pgfplots@metamax contains the largest meta value. By default, the meta value is the same as the y value.

I've defined two new styles, mark min and mark max that will switch off the plot markers for all but the smallest and largest value, respectively.

If you also want to print the coordinates of the extremal values, you need to make the x-coordinate available using visualization depends on=<value> \as <\macroname>.

\documentclass{standalone}
\usepackage{pgfplots}

\makeatletter
\pgfplotsset{
    /tikz/max node/.style={
        anchor=south
    },
    /tikz/min node/.style={
        anchor=north
    },
    mark min/.style={
        point meta rel=per plot,
        visualization depends on={x \as \xvalue},
        scatter/@pre marker code/.code={%
            \ifx\pgfplotspointmeta\pgfplots@metamin
                \def\markopts{}%
                \node [min node] {
                    \pgfmathprintnumber[fixed]{\xvalue},%
                    \pgfmathprintnumber[fixed]{\pgfplotspointmeta}
                };
            \else
                \def\markopts{mark=none}
            \fi
            \expandafter\scope\expandafter[\markopts,every node near coord/.style=green]
        },%
        scatter/@post marker code/.code={%
            \endscope
        },
        scatter,
    },
    mark max/.style={
        point meta rel=per plot,
        visualization depends on={x \as \xvalue},
        scatter/@pre marker code/.code={%
        \ifx\pgfplotspointmeta\pgfplots@metamax
            \def\markopts{}%
            \node [max node] {
                \pgfmathprintnumber[fixed]{\xvalue},%
                \pgfmathprintnumber[fixed]{\pgfplotspointmeta}
            };
        \else
            \def\markopts{mark=none}
        \fi
            \expandafter\scope\expandafter[\markopts]
        },%
        scatter/@post marker code/.code={%
            \endscope
        },
        scatter
    }
}
\makeatother

\begin{document}
\begin{tikzpicture}
 \begin{axis}
    \addplot +[mark min, every node near coord/.style=] plot {x^2};
    \addplot +[mark max] plot {-x^2+5*x+5};
\end{axis}
\end{tikzpicture}

\end{document}
Jake
  • 232,450
  • Very cool! I'll be using this. Is there any way to extract the (x,y) values of the max/min point? I'm imagining using it as a node and placing the coordinate above or below the mark. – qubyte Feb 29 '12 at 04:22
  • 1
    @MarkS.Everitt: Good idea, I've edited my answer accordingly. – Jake Feb 29 '12 at 04:55
  • Is this version dependent? I am still running pgfplots 1.4.1, and the code provided doesn't work for me. I get an error at the addplot command (Package pgfkeys Error: I do not know the key '/tikz/visualization depends on' and I am going to ignore it.) – Stephen Bosch May 10 '12 at 15:57
  • 1
    @StephenBosch: Yeah, I think this feature was only introduced in version 1.5. It's a good idea to update, pgfplots is being so actively developed that you'd miss out on a lot of nifty features if you stick with an old version. – Jake May 10 '12 at 16:04
  • @Jake: Okay, I finally went to a source install of texlive, and I'm running pgfplots 1.5.1 now. It works with the functions in your example, but not with mine; mine's a 4th degree polynomial and I'm only interested in the local maximum for the domain I've set. It seems to ignore the domain completely. Am I doing something wrong, or is there a way to fix it so it will work? – Stephen Bosch May 10 '12 at 23:11
  • @StephenBosch: Hm, it should work. Maybe it's best if you could open a new question with a minimal example document that reproduces the issue. – Jake May 11 '12 at 06:16
  • @StephenBosch: Thanks for the question! I found the error and came up with a better approach. I've also edited my answer to this question to include the new method. – Jake May 11 '12 at 09:39
  • Here is a follow up question, may be you can have a look? Thank you! – Robert Seifert Oct 30 '15 at 07:45
  • Im always so amazed how people come up with this! However, the paraneters ymax and ymin for mark max and mark min respectively seem to make problems, in that the nodes stop being displayed. When it does work, it displays the global extrema, not the local extrema, as they seem to do in the picture. Maybe I have made a mistake somewhere? – Concerto Feb 21 '21 at 14:30
7

It's not an answer to the question but with pgf 2.1 CVS and without pgfplots (why ?) we can draw something interesting with the datavisualization library. Perhaps It would be possible to use pgfplots with this library.

Actually I don't know how to add Jake's style.

enter image description here

Update code I cleaned the code

\documentclass[11pt]{article}
\usepackage{tikz}
\usetikzlibrary{
  datavisualization,
  datavisualization.polar,% not useful here but interesting to know
  datavisualization.formats.functions}

\begin{document}  

  \pgfdvdeclarestylesheet{my colors}
{
  default style/.style={visualizer color=black},
  1/.style={visualizer color=blue},
  2/.style={visualizer color=red},
}

\begin{tikzpicture}[baseline,scale=2]
  \datavisualization [ scientific axes=clean,
                       visualize as smooth line/.list={f,g},% not sin and cos
                       style sheet=my colors,
                       f={label in legend={text=$f(x)$}},
                       g={label in legend={text=$g(x)$}},
                       data/format=function ]
  data [set=f] {
    var x : interval [-5:5];
    func y =  \value x * \value x;
  } 
    info {
    \draw [blue,fill=blue!20,] (visualization cs: x={(0)}, y=0) circle [radius=1pt]
      node [below,font=\footnotesize] {min point};
  }
  data [set=g] {
    var x : interval [-5:5];
    func y = - \value x * \value x +5* \value x +5;
  } 
      info {
    \draw [red,fill=red!20] (visualization cs: x={(2.5)}, y=11.25) circle [radius=1pt]
      node [above,font=\footnotesize] {max point};
  }  ;
\end{tikzpicture}

\end{document} 
Alain Matthes
  • 95,075