5

I need to draw some plots with vertical lines from the x axis to the function. For example, I defined the gauss(x) function for the normal distribution and I drew this plot, with a vertical line from (1.96, 0) to (1,96, gauss(1.96)) and the corresponding simmetric line:

\documentclass{book}
  \usepackage{tikz}
  \usepackage{pgfplots} \pgfplotsset{compat=1.16}
  \pgfmathdeclarefunction{gauss}{3}{%
    \pgfmathparse{1/(#3*sqrt(2*pi))*exp(-((#1-#2)^2)/(2*#3^2))}
  }
  \usetikzlibrary{positioning}
  \usetikzlibrary{shapes}
  \usetikzlibrary{arrows}
  \usetikzlibrary{arrows.meta}
  \usetikzlibrary{decorations.pathmorphing}
\begin{document}
\begin{tikzpicture}
  \begin{axis}[%
      domain=-4:4, samples=61, smooth,
      clip=false,
      enlargelimits=upper
    ]
    % normal distribution PDF
    \addplot [thick] {gauss(x,0,1)};
    % right vertical line
    \pgfmathparse{gauss(+1.96,0,1)};
    \pgfmathsetmacro\pgftempa\pgfmathresult;
    \node[coordinate] (a) at (axis cs:+1.96,\pgftempa) {};
    \draw (axis cs:+1.96,0) -- (a);
    % left vertial line
    \pgfmathparse{gauss(-1.96,0,1)};
    \pgfmathsetmacro\pgftempb\pgfmathresult;
    \node[coordinate] (b) at (axis cs:-1.96,\pgftempb) {};
    \draw (axis cs:-1.96,0) -- (b);
  \end{axis}
\end{tikzpicture}
\end{document}

(code above may contain some math errors as it's a MWE from a longer code, just ignore them)

I know, after searching on this forum, that I can't use gauss(x)directly after axis cs, but when I have to draw many lines the code becomes huge, and unreadable. Also, I'd like to avoid using many variables \pgftemp*.

Is there an easier way to evaluate a function which does not require three lines of code? For example, would it be possible to define a new command \evaluatefunction(x) and write axis cs: 1.96,\evaluatefunction(1.96) or \evaluatefunction{gauss(1.96,0,1)}? I already tried this but it does not work:

\newcommand{\evaluatefunction}[1]{%
  \pgfmathparse{#1};
  \pgfmathsetmacro\pgftempa\pgfmathresult;
}
\node[coordinate] (a) at (axis cs:+1.96,\evaluatefunction{gauss(1.96,0,1)}) {};

Compilation of this code does not end, and I have to stop it, so I can't even get an error log.

Thank you!

  • 2
    Welcome to TeX.SX! Can you please expand the code snippet that you have posted to a full minimal working example. A MWE should start with a \documentclass command, include any necessary packages and be as small as possible to demonstrate your problem. It is much easier to help you if we can start with some compilable code that illustrates your problem - at the moment we have to guess what packages etc you are using... –  Jun 13 '18 at 15:14
  • Done, I also forgot to enclose the tikz code in axis environment – Taekwondavide Jun 13 '18 at 15:31
  • 1
    Why the extra parenthesis in \pgfmathparse{#1)};? Should be \pgfmathparse{#1};? – Bordaigorl Jun 13 '18 at 15:50
  • @Bordaigorl yes, I missed it after replacing gauss(#1,0,1) with #1, however it's still not working – Taekwondavide Jun 13 '18 at 16:09
  • Sorry for the many mistakes and missing parts in the code above, I extracted it from a big picture in a document with a huge preamble divided in many files – Taekwondavide Jun 13 '18 at 16:11
  • see https://tex.stackexchange.com/questions/406449, maybe can help – Zarko Jun 13 '18 at 17:15
  • @Zarko in this solution coordinates are still computed before being used, but I'm looking for a way to compute them on the fly (axis cs: 1.96,<compute here gauss(1.96)>) – Taekwondavide Jun 13 '18 at 17:23
  • why? does this has some benefits? sorry, i don't see any :-) – Zarko Jun 13 '18 at 17:24
  • 1
    @Zarko Sorry, did you try what happens when you draw the vertical lines in the OP's MWE with the usual syntax, i.e. with \draw (+1.96,0) -- (1.96,{gauss(+1.96,0,1)}); ? Your code does not help in this regard, I am afraid. If you want so see benefits, just look at my code below, where one can just put \draw (1.96,0) -- (1.96,{gauss(1.96,0,1)}); and no mysterious shifts happen. –  Jun 13 '18 at 17:25
  • @marmot, thank you for explanation. i will check your solution again. it seems that i overlooked this. – Zarko Jun 13 '18 at 17:28
  • 1
    @Zarko Maybe you want to have a look here. Ironically, it was never really clarified why \pgfmathdeclarefunction is "bad" but it (sort of) was agreed on that the syntax I was familiar with id "better". I guess it has to do with the fpu library. –  Jun 13 '18 at 17:38
  • @marmot, thank you very much for this link. i wasn't aware for this question. i also prefer solutions with syntax which i'm familiar :-) – Zarko Jun 13 '18 at 17:47
  • @Zarko I was also not aware of this question before you showed up in this thread. ;-) BTW, I checked whether or not switching fpu off helps, and the answer is no. So most likely it is something else. –  Jun 13 '18 at 17:48
  • @marmot I read somewhere that TikZ does some calculations for placing nodes in the axis c.s. and the use of \pgfmathparse (the PGF engine loaded by TikZ) interferes with those calculations: (1) If you put \pgfmathparse{}\pgfmathresult after axis cs you just get errors; (2) if you write \pgfmathparse{};\node at (axis cs: 0, \pgfmathresult) {} you get the result of calculations made by \node command, that override your parsed expression. I guess that the native PGF used in your code is independent from the engine loaded by TikZ and maybe that's why your solution work and mine does not. – Taekwondavide Jun 13 '18 at 21:32
  • @Taekwondavide I don't know since there are no (obvious) nodes involved in \draw (1.96,0) -- (1.96,{gauss(1.96,0,1)}); but this also did not work properly with your original syntax. Even the point (1.96,0) got shifted and there was a large universal shift of the hole plot. There are a lot of things going on with \nullfont and so on and I do not understand those at all. –  Jun 13 '18 at 21:38

2 Answers2

4

I am not so much familiar with the way you declare a function. However, if I use the way I am familiar with, there is no problem.

  \documentclass{book}
  \usepackage{tikz}
  \usepackage{pgfplots} 
  \pgfplotsset{compat=1.16}
  \tikzset{declare function={gauss(\x,\y,\z)=
  1/(\z*sqrt(2*pi))*exp(-((\x-\y)^2)/(2*\z^2));}}
  \usetikzlibrary{positioning}
  \usetikzlibrary{shapes}
  \usetikzlibrary{arrows}
  \usetikzlibrary{arrows.meta}
  \usetikzlibrary{decorations.pathmorphing}
\begin{document}
\begin{tikzpicture}
  \begin{axis}[%
      domain=-4:4, samples=61, smooth,
      clip=false,
      enlargelimits=upper
    ]
    % normal distribution PDF
    \addplot [thick] {gauss(x,0,1)};
    \draw (1.96,0) -- (1.96,{gauss(1.96,0,1)});
    \draw (-1.96,0) -- (-1.96,{gauss(1.96,0,1)});
  \end{axis}
\end{tikzpicture}
\end{document}

enter image description here

The same result can be obtained with an even more pgfplots like notation:

  \documentclass{book}
  \usepackage{tikz}
  \usepackage{pgfplots} 
  \pgfplotsset{compat=1.16}
  \usetikzlibrary{positioning}
  \usetikzlibrary{shapes}
  \usetikzlibrary{arrows}
  \usetikzlibrary{arrows.meta}
  \usetikzlibrary{decorations.pathmorphing}
\begin{document}
\begin{tikzpicture}
  \begin{axis}[declare function={gauss(\x,\y,\z)=
  1/(\z*sqrt(2*pi))*exp(-((\x-\y)^2)/(2*\z^2));},%
      domain=-4:4, samples=61, smooth,
      clip=false,
      enlargelimits=upper
    ]
    % normal distribution PDF
    \addplot [thick] {gauss(x,0,1)};
    \draw (1.96,0) -- (1.96,{gauss(1.96,0,1)});
    \draw (-1.96,0) -- (-1.96,{gauss(1.96,0,1)});
  \end{axis}
\end{tikzpicture}
\end{document}

On the other hand, \pgfmathdeclarefunction cannot be found in the pgfplots manual. However, on p. 544 the manual warns us that it automatically switches on fpu. This might be where be big overall and the small relative shifts come from, but I am not sure.

  • Thank you! This should be good for me, I'm trying to understand the difference between the two ways of declaring functions before accepting the answer – Taekwondavide Jun 13 '18 at 17:26
  • @Taekwondavide The difference is that declare function can be found in the pgfplots manual, meaning that it is fully supported, whereas \pgfmathdeclarefunction can't be found there. See page 544 of the pgfplots manual. –  Jun 13 '18 at 17:29
  • \pgfmathdeclarefunction is part of TikZ` (or, at least, it's documented in TikZ&PGF manual). I just wonder if there is any difference in how the computation is executed, such as variable size and computationa load – Taekwondavide Jun 13 '18 at 17:38
  • 1
    @Taekwondavide See here for an earlier discussion of the same issues... I guess one may now delve into the codes of TikZ and pgfplots or just accept as an empirical fact that the declare function syntax is better... –  Jun 13 '18 at 17:40
  • 1
    I take a better look to TikZ manual, looks like \pgfmathparse loads the mathematical engine of PGF in TikZ, while your solution is native PGF. I guess that I would have had to use \pgfmathparse only if I hadn't used pgfplot package too. – Taekwondavide Jun 13 '18 at 17:51
  • 1
    @Taekwondavide \tikzset{declare function={gauss(\x,\y,\z)= 1/(\z*sqrt(2*pi))*exp(-((\x-\y)^2)/(2*\z^2));}} works for both TikZ and pgfplots without problems (for me) so far. –  Jun 13 '18 at 17:56
  • yes, it works for me too, I mean that maybe it works in TikZ only if pgfplots is loaded too, and \pgfmathparse is needed otherwise. However, I'm using pgfplots so this is not an issue for me, and I'm going to use your solution. – Taekwondavide Jun 13 '18 at 18:19
3

If you want to draw vertical lines from y = 0 to the function value there is an even simpler way to achieve this using ycomb. Then, starting with marmot's answer this reduces to following.

% used PGFPlots v1.16
\documentclass[border=5pt]{standalone}
\usepackage{pgfplots}
    \pgfplotsset{
        /pgf/declare function={
            gauss(\x,\y,\z) = 1/(\z*sqrt(2*pi))*exp(-((\x-\y)^2)/(2*\z^2));
            mygauss = gauss(\x,0,1);
        }
    }
\begin{document}
\begin{tikzpicture}
    \begin{axis}[
        domain=-4:4,
        samples=61,
        smooth,
        enlargelimits=upper,
    ]
        \addplot [thick] {mygauss};
        % ----------------------------------
        % added/new stuff
        \addplot [
            ycomb,
            samples at={-1.96,1.96},
        ] {mygauss};
        % ----------------------------------
    \end{axis}
\end{tikzpicture}
\end{document}

image showing the result of above code

Stefan Pinnow
  • 29,535
  • Thank you! In many cases I still need to create a \node[coordinate] for other purposes, as in marmot's solution, but in all other cases I'll use this one – Taekwondavide Jun 15 '18 at 14:51
  • Neither can I find a node/coordinate in marmot's answer, nor do I see a problem to add them to my answer ... – Stefan Pinnow Jun 15 '18 at 18:52
  • Yes, sorry, I misspoke. I mean that this is a great solution, which let me avoid to create many unnecessary nodes as in my original code, however In some situations I did not explained here I still need to create the node and use \draw, otherwise I'll definitely use ycomb – Taekwondavide Jun 18 '18 at 00:47