4

I have a very simple pgf plot, like this:

\pgfmathdeclarefunction{gauss}{2}{%
  \pgfmathparse{1/(#2*sqrt(2*pi))*exp(-((x-#1)^2)/(2*#2^2))}%
}

\begin{tikzpicture}
  \begin{axis}[
      height=4cm,
      width=.8\textwidth,
  xlabel=Observable,
  ylabel=Number of Events,
      samples=100,
      domain=-1:3,
      ticks=none,
      axis x line=bottom,
      axis y line=left,
      enlarge y limits=true,
    ]
    \addplot[black,no marks] {exp(-x)};
    \addplot[red,no marks] {0.3*gauss(0,0.2) + exp(-x)};
  \end{axis}
\end{tikzpicture}

I would like to add a new data row with randomly generated data points with the following properties:

  • They should be equidistant in x, with a distance that I can choose
  • They should scatter around the red line in y, with a deviation that I can choose
  • They should have error bars that always correspond to +/- the square root of the value.

I suppose this can be achieved with some foreach magic in pgfplots, but I'm unsure as to what is the most elegant way to achieve this.

I would like to avoid having to generate the points with an external script (which I can of course do), but would rather have everything done with pgf directly.

  • Take a look at the pgf manual, section 90.3, Pseudo-random numbers. – Huang_d Jun 8 '17 at 14:06
  • @Huang_d: yes, that is very helpful already. but how do I make all these numbers into a data set that I can plot? – carsten Jun 8 '17 at 14:07
  • 3
    It might actually be a lot easier to make those points externally, using say in R or python and then just plot them. Generating points via math in tikz is fairly slow. Also have the advantage that the plot does not change each time you recompile. – daleif Jun 8 '17 at 14:11
  • @daleif: I understand your argument and think it is valid, but I would still like to know how to do it with plain pgf as well. The document needs to be very portable, so relying on any type of shell-redirect or Makefile is not so nice. – carsten Jun 8 '17 at 14:19
  • 2
    To expand on what @Huang_d said. As rand makes a pseudo-random number between -1 and 1, you can use e.g. \addplot[only marks,blue,mark=*,mark size=1pt] {0.3*gauss(0,0.2) + exp(-x) + 0.5*rand}; to plot the same as the red curve, but adding a number between -0.5 and 0.5. Some more work is needed for the error bar though. – Torbjørn T. Jun 8 '17 at 14:29
6

You can use the random number functions rnd and rand directly in an \addplot expression. rand makes a pseudo random number between -1 and 1, so you can use something like

\addplot[only marks,blue,mark=*,mark size=1pt] {0.3*gauss(0,0.2) + exp(-x) + 0.5*rand};

to make the noisy plot. As you mention pgfplots has a method for making an error bar relative to the function value, so you can do something like this:

enter image description here

\documentclass[border=10pt,
               tikz]{article}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.14}
\pgfmathdeclarefunction{gauss}{2}{%
  \pgfmathparse{1/(#2*sqrt(2*pi))*exp(-((x-#1)^2)/(2*#2^2))}%
}
\begin{document}

\begin{tikzpicture}
  \begin{axis}[
      height=4cm,
      width=.8\textwidth,
      xlabel=Observable,
      ylabel=Number of Events,
      samples=100,
      domain=-1:3,
      ticks=none,
      axis x line=bottom,
      axis y line=left,
      enlarge y limits=true,
    ]
    \addplot[black,no marks] {exp(-x)};
    \addplot[red,no marks]  {0.3*gauss(0,0.2) + exp(-x)};

    \addplot[
       only marks,
       blue,
       mark=*,
       mark size=0.7pt,
       error bars/.cd,
         y dir=both,
         y fixed relative=0.1] {0.3*gauss(0,0.2) + exp(-x) + 0.5*rand};
  \end{axis}
\end{tikzpicture}
\end{document}

More general

(There may well be much better ways of doing this.) You can make a table of numbers from scratch using pgfplotstable. That allows you to generate the function and the noise separately, and calculate whatever error you want. It is a bit verbose, and it also takes some time to compile, so daleif's suggestion of making the datafiles elsewhere is probably good.

enter image description here

\documentclass[border=10pt,
               tikz]{article}
\usepackage{pgfplotstable}
\pgfplotsset{compat=1.14}
\tikzset{
declare function={
 gauss(\m,\s,\x)={1/(\s*sqrt(2*pi))*exp(-((\x-\m)^2)/(2*\s^2))};
}
}

\pgfplotstableset{
 create on use/index/.style={create col/set list={0,...,99}}
}
% make a new table with 100 rows,  populate 
\pgfplotstablenew[columns={index}]{100}{\datatable}

% make an x-column
\pgfplotstablecreatecol[
  create col/assign/.code={
    % calculate expression
    \pgfmathparse{(\thisrow{index}/\pgfplotstablerows)*4-1}
    % save to table
    \pgfkeyslet{/pgfplots/table/create col/next content}\pgfmathresult
}
]{x}{\datatable}

% make a y-column without random noise
\pgfplotstablecreatecol[
  create col/assign/.code={
    \pgfmathparse{0.3*gauss(0,0.2,\thisrow{x}) + exp(-\thisrow{x})}
    \pgfkeyslet{/pgfplots/table/create col/next content}\pgfmathresult
}
]{y}{\datatable}
% make random noise
\pgfplotstablecreatecol[
  create col/assign/.code={
    \pgfmathparse{0.2*rand}
    \pgfkeyslet{/pgfplots/table/create col/next content}\pgfmathresult
}
]{ynoise}{\datatable}

% error, as square root of absolute value
\pgfplotstablecreatecol[
  create col/assign/.code={
    \pgfmathparse{sqrt(abs(\thisrow{ynoise}))}
    \pgfkeyslet{/pgfplots/table/create col/next content}\pgfmathresult
}
]{yerr}{\datatable}

\begin{document}
%\pgfplotstabletypeset\datatable

\begin{tikzpicture}
  \begin{axis}[
      height=4cm,
      width=.8\textwidth,
      xlabel=Observable,
      ylabel=Number of Events,
      samples=100,
      domain=-1:3,
      ticks=none,
      axis x line=bottom,
      axis y line=left,
      enlarge y limits=true,
    ]
    \addplot[black,no marks] {exp(-x)};
    \addplot[red,no marks] table[x=x,y=y] {\datatable};

    \addplot[
       only marks,
       blue,mark=*,
       mark size=0.5pt,
       error bars/.cd,
         y dir=both,
         y explicit]
    table[x=x,y expr=\thisrow{y}+\thisrow{ynoise},y error=yerr] {\datatable};
  \end{axis}
\end{tikzpicture}
\end{document}

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.