Now that we have discussed the Gaussian distribution at length, we want an overview of some of the most important continuous distributions, much as we did for the discrete ones in the chapter The most important discrete distributions. For this we use the Python module scipy.stats.
SciPy Statistics¶
import numpy as np
np.random.seed(12321)
import scipy.stats as st
# Create a random variable with an N(3,2^2) distribution:
X = st.norm(loc=3, scale=2)
# draw samples:
Xsamples = X.rvs(size=4)
print("some examples:",Xsamples)some examples: [4.02801895 1.9033209 6.49477125 1.83362523]
# Check the distribution:
Xsamples = X.rvs(size=1000)
print("mean:", np.mean(Xsamples), "expected", X.mean()) # 3
print("std:", np.std(Xsamples), "expected", X.std()) # 2
print("3rd moment:", X.moment(3))
print("4th moment:", X.moment(4))
print("entropy:", X.entropy())mean: 3.0004822746684243 expected 3.0
std: 1.9994507762900595 expected 2.0
3rd moment: 62.99999999999999
4th moment: 344.99999999999994
entropy: 2.112085713764618
# Fit the data:
mu, sigma = st.norm.fit(Xsamples)
print(mu, sigma)3.0004822746684243 1.9994507762900595
There are further useful methods, such as st.norm.sf, the ‘survival function’, defined as 1-cdf, or st.norm.logpdf for when you want to work directly with the logarithm of the density function (both are potentially faster than computing it by hand).
Matplotlib¶
For each distribution we will look at plots of the density function (pdf) and the cumulative distribution function (cdf). For this we use the Python module matplotlib.pyplot, which emulates the behaviour of MATLAB. We will come back to Matplotlib in more detail.
As a first example we simulate the continuous distribution on from the chapter Continuous distributions: first one of the numbers is chosen with weights , then a uniformly distributed number from is added. That chapter also works out the expected value , which we can compare with the mean of the sample.
from matplotlib import pyplot as plt# Adjust the size of the plot output:
plt.rcParams["figure.figsize"] = (16,9)
# Sample from the distribution on [5,8) from the chapter Continuous distributions:
sample = np.random.choice(3, 10000, p=[4/7,2/7,1/7]) + 5 + np.random.random(10000)
print("sample[:10] =", sample[:10])
# Plot the histogram:
plt.hist(sample, bins=100)
plt.ylabel("frequency")
plt.xlabel(f"mean={np.mean(sample):.4f} (expected: {6+1/14:.4f}), standard deviation²={np.std(sample)**2:.4f}")
plt.show()
plt.hist(sample, bins=100, density=True)
plt.ylabel("density")
plt.xlabel(f"mean={np.mean(sample):.4f} (expected: {6+1/14:.4f}), standard deviation²={np.std(sample)**2:.4f}")
plt.show()
# Histogram with few bins:
plt.hist(sample, bins=3)
plt.ylabel("frequency")
plt.show()sample[:10] = [5.96301841 5.60942173 5.72908826 5.80068973 5.59819708 5.35377228
6.093922 6.36189002 5.42430139 5.87334923]



To plot a function we define it as a Python function and evaluate it at enough points; the plot is then interpolated between them:
# Plot the function x |-> 3+2x+x²-x³ on the interval [-80,90]:
def f(x):
return 3 + 2*x + x**2 - x**3
x = np.linspace(-80, 90, 100)
plt.plot(x, f(x), color='red')
plt.show()
With that, we turn to the most important continuous distributions:
Normal distribution¶
Parameters , notation
Density
Expected value
Variance
All odd central moments are 0 (the density is symmetric, so an odd moment is a sign of asymmetry)
Maximum entropy for a fixed expected value, a fixed variance and support all of
Central limit theorem: for iid with expected value and variance , the standardised sum is approximately -distributed for large , equivalently (chapter The normal distribution)
Example applications:
Measurement errors in physical systems
Manufacturing precision
Biological quantities (body height etc.)
# For the plots:
def plotX(x, X, name, params):
# First the density function:
plt.plot(x, X.pdf(x))
plt.ylabel('density')
plt.title(f"PDF of the {name} with parameters {params}")
plt.show()
# Then the cumulative distribution function:
plt.plot(x, X.cdf(x))
plt.ylabel('F(x) = P(X ≤ x)')
plt.title(f"CDF of the {name} with parameters {params}")
plt.show()
x = np.linspace(-3,3,100)
mu, sigma = 0, 1
X = st.norm(loc=mu, scale=sigma)
name = "normal distribution"
plotX(x, X, name, (mu, sigma))

X = st.norm(loc=3, scale=2)
Xsamples = X.rvs(size=10000)
plt.hist(Xsamples, bins=200, density=True)
plt.xlabel("10,000 samples from N(3, 2²), in 200 bins")
plt.ylabel("density")
plt.show()
Contaminated normal distribution¶
The contaminated normal distribution is a simple case of a mixture model, more precisely a Gaussian mixture.
Parameters , notation
Density
Expected value
Variance
Example applications: a two-stage process in which the outcome of a Bernoulli experiment decides which normal distribution is sampled from. One concrete experimental setup would be measuring the height of the participants in a computer science lecture. Gender is (approximately!) Bernoulli distributed (presumably not uniformly), and within each gender group height is then normally distributed (but with different means). The example also shows that every act of modelling has the potential to render invisible a part of reality that you may not want invisible at all. Fortunately, mixture models can also be set up with more than one parameter .
name = "0.6·N(0,1) + 0.4·N(2, 0.5²)"
def f(x, epsilon=0.6, mu1=0, sigma1=1, mu2=2, sigma2=0.5):
norm1 = st.norm(loc=mu1, scale=sigma1)
norm2 = st.norm(loc=mu2, scale=sigma2)
return epsilon*norm1.pdf(x) + (1-epsilon)*norm2.pdf(x)
x = np.linspace(-3,3,100)
plt.plot(x, f(x))
plt.ylabel('density')
plt.title(f"PDF of {name}")
plt.show()
Dirac distribution¶
Parameter , notation
No density, but
Expected value
Variance 0
Entropy 0 (read as a discrete distribution; it has no differential entropy)
Example applications:
Writing down ‘density functions’ in the continuous sense for discrete distributions
Photographs of the night sky (taken with Hubble, say) – every star is a smeared-out heap of pixels (normally distributed), but the ‘actual’ distribution of the stars behind it is a sum of Dirac measures, since every star sits at exactly one point (the star’s true extent is irrelevant at that distance). Some super-resolution techniques work with this model.
Strictly speaking the Dirac distribution cannot be plotted (there is no density, after all). Some people draw an arrow of length 1 at by way of illustration. The cumulative distribution function is a Heaviside step function:
name = "Dirac distribution at ξ=0"
plt.plot(x, np.heaviside(x, 1))
plt.ylabel('F(x) = P(X ≤ x)')
plt.title("CDF of the "+name)
plt.show()
Continuous uniform distribution¶
Parameters , notation
Density
Expected value
Variance
Maximum entropy among all distributions with support
Example applications:
If a bus arrives at your stop extremely punctually every hour but you do not know when the last one left, your waiting time at the stop, in minutes, is uniformly distributed on .
The temperature of your CPU from the third decimal place on,
(T * 100 - int(T * 100)), is approximately uniformly distributed.
a, b = -1, 2
X = st.uniform(loc=a, scale=b-a)
print(X.mean(), X.var())
name = "uniform distribution"
plotX(x, X, name, (a, b))0.5 0.75


Exponential distribution¶
Parameter , notation
Density for (and 0 for )
Expected value
Variance
Maximum entropy among all distributions with support and expected value
Example applications:
How long does it take until the next packet arrives at the server?
The exponential distribution is the continuous flip side of the Poisson distribution from the chapter The most important discrete distributions: count the events per unit of time and there are many of them; ask instead for the waiting time until the next event, and it is -distributed. In both cases is the same rate.
name = "exponential distribution"
lamda = 2 # 'lambda' is a keyword in Python (anonymous functions), hence the spelling
X = st.expon(scale = 1/lamda)
x = np.linspace(-1,5,100)
plotX(x,X,name,(lamda))

Log-normal distribution¶
Parameters , notation
Density
Expected value , but
Variance , but
Maximum entropy among all distributions with support and and
Example applications:
The length of a game of chess is log-normally distributed
Many (!) more examples are listed in Wikipedia on the log-normal distribution
name = "log-normal distribution"
mu, sigma = 1, 2
X = st.lognorm(s=sigma, scale = np.exp(mu))
x = np.linspace(0, 12, 1000)
plotX(x,X,name,(mu, sigma))

Weibull distribution¶
Parameters , notation
Density for
and forExpected value (where )
Variance
Maximum entropy among all distributions with support , fixed and fixed – so the shape parameter says which moment is held fixed
Example applications:
How long does a heat pump run before it breaks down?
What is the wind speed?
What is the largest amount of precipitation to fall on a single day in the year?
name = "Weibull distribution"
lamda, k = 1, 1.3 # lamda: see above
X = st.weibull_min(c=k, loc=0, scale = lamda)
x = np.linspace(0,2,1000)
plotX(x,X,name,(lamda, k))

Data to plot and fit¶
To get a little practice in plotting data and fitting distributions to it (and then visualising the fit in a plot), we need some toy data.
A very simple, and therefore very instructive, dataset is Fisher and Anderson’s ‘iris flower dataset’, which we can get from Scikit-Learn, for instance:
from sklearn import datasets
iris = datasets.load_iris()
print(iris["data"][:5])[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
[4.6 3.1 1.5 0.2]
[5. 3.6 1.4 0.2]]
first_feature = iris["data"][:,0]
plt.hist(first_feature, bins=30, density=True)
# Now the best-fitting normal distribution on top:
mu, sigma = st.norm.fit(first_feature)
X = st.norm(loc=mu, scale=sigma)
x = np.linspace(3,9,100)
plt.plot(x, X.pdf(x))
plt.xlabel("first feature of the Iris dataset")
plt.ylabel("density")
plt.show()
One source of reasonably well-maintained and much-discussed toy data is Kaggle.
To work with these data you usually have to turn CSV files into Numpy arrays. That is particularly painless with Pandas (which builds on Numpy), which we will come to; for now, Numpy’s built-in np.genfromtxt will do.
In its Climate Data Center (in German), the German weather service DWD keeps a large collection of well-documented weather and climate data, some of it updated daily. It holds spatially distributed variables and time series of all manner of physical quantities and statistical distributions. One interesting (and answerable) question, for instance, is whether heavy rainfall events in Düsseldorf have become more frequent over the last few decades.
Have a look at these (and other!) data, work out how the variables might be distributed, plot the distributions and fit them. Comparing the histogram with the density function of the fitted distribution is the best way to see how close we are.
Since a great deal of data fits none of the distributions listed here, we need more complex models as well. Mostly, though, these are assembled in stages from the simple models, as with the contaminated normal distribution. One of the most important will later be the mixture of multivariate normal distributions, which lets us look at some of the principles of machine learning.
If you want to dig further into the relationships between the distributions – and have noticed that several of them can perfectly well be parametrised in different ways (the normal distribution with , say, but also with ) – then ProbOnto may be just the thing. It is an ontology and knowledge base originally developed for pharmacology. The overview diagram alone is already a good memory aid:
