For a small class I want to draw a bivariate normal distribution and show where the means from the two variables meet in the space. Has anyone done this in TikZ?

  • 3
    Could you specify your question a bit? Perhaps show a picture of what you're looking for, and preferably show any attempts and ideas you've come up with. After all, your question certainly isn't whether or not anybody has done this in TikZ yet.
    – doncherry
    Commented Oct 16, 2011 at 9:51
  • 3
    It can't do any harm to link special terms in your question for more information: Multivariate normal distribution - Wikipedia, the free encyclopedia. This way you will get help much faster. Commented Oct 16, 2011 at 10:52

2 Answers 2


Christian was faster, but because I invested the time, here's my take on showing how the bivariate distribution results from the two univariate ones.

There are a couple of things in the code that might be useful for you:

You can define mathematical functions using declare function={<name>(<argument macros>)=<function>;}, which will help to keep the code clean and avoid repetitions.

You can define a new colormap using \pgfplotsset{ colormap={<name>}{<color model>(<distance>)=(<value1>); <color model>(<distance 2>)=(<value2>)} }. This is a very powerful feature, so you should definitely read up on it in the pgfplots manual.

The legend created by colorbar is a whole new plot, so you can configure it with all the usual axis options.

There are different ways for defining 3D functions: \addplot3 {<function>}; will evaluate <function> at every point on a grid and assume the result to be a z-value. \addplot3 ({<x>},{<y>},{<z>}); defines a parametric function in 3D space, which allows you to (among other things) draw three-dimensional lines.




colormap={whitered}{color(0cm)=(white); color(1cm)=(orange!75!red)}

    declare function={mu1=1;},
    declare function={mu2=2;},
    declare function={sigma1=0.5;},
    declare function={sigma2=1;},
    declare function={normal(\m,\s)=1/(2*\s*sqrt(pi))*exp(-(x-\m)^2/(2*\s^2));},
    declare function={bivar(\ma,\sa,\mb,\sb)=
        1/(2*pi*\sa*\sb) * exp(-((x-\ma)^2/\sa^2 + (y-\mb)^2/\sb^2))/2;}]
    colormap name=whitered,
    y domain=-1:4,
    colorbar style={
        anchor=south west,
        height=0.25*\pgfkeysvalueof{/pgfplots/parent axis height},
\addplot3 [surf] {bivar(mu1,sigma1,mu2,sigma2)};
\addplot3 [domain=-1:4,samples=31, samples y=0, thick, smooth] (x,4,{normal(mu1,sigma1)});
\addplot3 [domain=-1:4,samples=31, samples y=0, thick, smooth] (-1,x,{normal(mu2,sigma2)});

\draw [black!50] (axis cs:-1,0,0) -- (axis cs:4,0,0);
\draw [black!50] (axis cs:0,-1,0) -- (axis cs:0,4,0);

\node at (axis cs:-1,1,0.18) [pin=165:$P(x_1)$] {};
\node at (axis cs:1.5,4,0.32) [pin=-15:$P(x_2)$] {};
  • 1
    Looks pretty cool :) Commented Oct 16, 2011 at 11:41
  • 5
    It's a pretty cool package!
    – Jake
    Commented Oct 16, 2011 at 11:42
  • 5
    Note that due to the symmetry of the gaussian, you may want to choose an odd number of samples for the 1d curves to avoid the flat top. Commented Oct 16, 2011 at 11:52
  • 2
    It never stops to impress me. Commented Oct 16, 2011 at 22:24
  • 2
    Just wanted to let everybody who might already have used this code know that there was a tiny but significant typo in the code. 1/(2*pi*\sa*\sa) should have been 1/(2*pi*\sa*\sb). It's fixed now but you might want to check any documents in which you used this beautiful plot.
    – Christian
    Commented May 16, 2013 at 11:48

What you need is to sample a bivariate function and to choose a proper plot type. Typical choices are mesh, surface, or contour plots. The most common is a surface plot, I think.

In any case, TikZ does not support bivariate functions - but you can use pgfplots which is build on top of it and provides simple integration into tikz. I should note that I am author of pgfplots. Here is how it could help here:

enter image description here



    \addplot3[surf,domain=-2:6,domain y=-5:3] 
        {exp(-( (x-\centerx)^2 + (y-\centery)^2)/3 )};
    \node[circle,inner sep=1pt,fill=blue,pin=90:$\mu$] 
        at (axis cs:\centerx,\centery,1) {};

You will see that the example is NO real normal distribution because it is not normalized to unit integral (it is normalized to unit max norm).

But the example shows the key aspects:

  1. choosing surf
  2. specifying the plotting expression
  3. adjust domain and domain y (consider also samples and samples y)
  4. a \node which describes the center in "some way".

Pgfplots also supports mesh plots (use mesh instead of surf) or contour plots (using contour gnuplot instead of surf; in this case, you need gnuplot and the shell-escape feature like pdflatex -shell-escape):

enter image description here

    \begin{axis}[view={0}{90},axis equal]
    \addplot3[contour gnuplot,domain=-2:6,domain y=-5:3] 
        {exp(-( (x-\centerx)^2 + (y-\centery)^2)/3 )};
    \node[circle,inner sep=1pt,fill=blue,pin=90:$\mu$] 
        at (axis cs:\centerx,\centery,1) {};

This second example also shows that view can be used to change the view angles (horizontal and vertical angle) and that axis equal might be of interest to get the axis ratios comparable (I must admit to my shame that the combination view={0}{90},axis equal appears to produce unexpected compilation errors - I will fix that).

Of interest may also be shader=interp or various choices for colormap which are listed in the manual.

  • 2
    Awesome package and great answer Commented Mar 23, 2012 at 17:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .