Pgfplots: Generate points randomly

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,
]
\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
• 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
• 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

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:

\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,
]

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.

\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,
]