# How to plot a multivariate normal distribution?

Plotting univariate distributions is really simple:

``````using Distributions
using StatsPlots

norm = fit(Normal, rand(100))
plot(norm)
``````

However, how would I plot a multivariate distribution? The above code doesn’t work for the multivariate case:

``````mvnorm = fit(MvNormal, [rand(0.0:100.0, 100) rand(0.0:100.0, 100)]')

julia> plot(mvnorm)
ERROR: MethodError: no method matching iterate(::MvNormal{Float64,PDMats.PDMat{Float64,Array{Float64,2}},Array{Float64,1}})
Closest candidates are:
iterate(::Core.SimpleVector) at essentials.jl:600
iterate(::Core.SimpleVector, ::Any) at essentials.jl:600
iterate(::ExponentialBackOff) at error.jl:218
...
``````

Here’s an example from Wikipedia of what an ideal output would look like:

2 Likes

Doesn’t look like there are any shortcuts for it in StatsPlots.

You can just get the `pdf` values on a grid and do a surface plot though.

``````Z = [pdf(mvnorm,[i,j]) for i in 0:100, j in 0:100]
plot(0:100,0:100,Z,st=:surface)
``````
7 Likes

Yes. It’d be nice to add a recipe for that to StatsPlots

2 Likes

This is great, thanks! Any idea how to change the color of the grid lines when doing `st=:wireframe`? I’ve tried every single color keyword argument/alias I can find in the docs and none of them work! Or, when using `st=:surface` do you know how to change the alpha? Again, I’ve tried all the alpha-related kwargs and none seem to do the trick.

I was able to hack together something that looks decent. Below is the code as well as the output for some data I’m working with:

``````x = 0:maximum(X)
y = 0:(maximum(Y)/length(x))+1:maximum(Y) # because x and y are of different lengths
z = [pdf(mvnorm, [i, j]) for i in x, j in y]

pyplot()

plot(x, y, z, linetype=:surface, legend=false, color=:blues)
plot!(x, y, z, linetype=:wireframe, legend=false, color=:black, width=0.1)
``````

2 Likes

Looks nice. `wireframe` color should follow the `linecolor` keyword. `alpha` for `surface` isn’t implemented
BTW no need to call `collect` all the time (and you can delete the first assignment to `y`).

1 Like

Just a warning/note about x-y ordering when creating the `Z` grid: if you’re plotting as a contour/heatmap, then you’ll want to reverse the `for x in X, y in Y` ordering to be `for y in Y, x in X` so that the grid maps to the x-y axes correctly. This is subtle, as it will not always present itself as a problem depending on the parameters of the `MvNormal`.

Here’s an example plotting a bivariate normal distribution as filled contours:

``````using Distributions
using Plots

μ = [0, 0]
Σ = [1  0.9;
0.9 10]
p = MvNormal(μ, Σ)

X = range(-8, 8, length=100)
Y = range(-8, 8, length=100)
Z = [pdf(p, [x,y]) for y in Y, x in X] # Note x-y "for" ordering
contourf(X, Y, Z, color=:viridis)
``````

To avoid `Z` altogether, you can pass a function `f` to handle calculating the z-values:

``````f(x,y) = pdf(p, [x,y])
contourf(X, Y, f, color=:viridis)
``````

2 Likes