statistics --- 수학 통계 함수

버전 3.4에 추가.

소스 코드: Lib/statistics.py


이 모듈은 숫자 (Real 값) 데이터의 수학적 통계를 계산하는 함수를 제공합니다.

참고

달리 명시되지 않는 한, 이 함수는 int, float, decimal.Decimalfractions.Fraction를 지원합니다. 다른 형(숫자 계층에 있든 없든)에서의 동작은 현재 지원되지 않습니다. 혼합 형도 정의되지 않으며 구현에 따라 다릅니다. 입력 데이터가 혼합 형으로 구성되었으면, map()을 사용하여 일관된 결과를 보장 할 수 있습니다, 예를 들어 map(float, input_data).

평균과 중심 위치의 측정

이 함수는 모집단(population)이나 표본(sample)에서 평균이나 최빈값을 계산합니다.

flowdas

이 페이지의 원문에는 typical 이라는 단어가 몇 군데 등장합니다. typical value 라는 표현을 쓸 때 보통 평균값과 비슷한 의미로 사용하기도 하지만, 드물게는 mode, 즉 최빈값을 뜻하기도 합니다.

지금은 아래 표에서 mode가 등장하기 때문에 최빈값으로 번역했습니다만, 다른 곳에서는 최빈값을 뜻한다고 볼 수 없는 곳도 있습니다. 이 때문에 전체적으로 typical value 을 최빈값으로 번역하지 않는 것이 올바를 수도 있습니다. 하지만 번역된 문장이 잘못된 의미가 되지 않는다면 최빈값이라고 표현했습니다.

mean()

데이터의 산술 평균(arithmetic mean) ( "average").

fmean()

빠른, 부동 소수점 산술 평균.

geometric_mean()

데이터의 기하 평균(geometric mean).

harmonic_mean()

데이터의 조화 평균(harmonic mean).

median()

데이터의 중앙 값(median) (중간 값).

median_low()

데이터의 낮은 중앙 값(low median).

median_high()

데이터의 높은 중앙 값(high median).

median_grouped()

그룹화된 데이터의 중앙값, 또는 50번째 백분위 수(50th percentile)

mode()

이산(discrete) 또는 범주(nominal) 데이터의 단일 최빈값(mode) (가장 흔한 값)

multimode()

이산 또는 범주 데이터의 최빈값(mode) (가장 흔한 값) 리스트.

quantiles()

데이터를 같은 확률을 갖는 구간으로 나눕니다.

분산 측정

이 함수는 모집단이나 표본이 평균 값에서 벗어나는 정도를 측정합니다.

pstdev()

데이터의 모집단 표준 편차(population standard deviation).

pvariance()

데이터의 모집단 분산(population variance).

stdev()

데이터의 표본 표준 편차(sample standard deviation).

variance()

데이터의 표본 분산(sample variance).

함수 세부 사항

참고: 함수에 전달되는 데이터가 정렬될 필요는 없습니다. 하지만, 읽기 쉽도록 대부분 예제는 정렬된 시퀀스를 보여줍니다.

statistics.mean(data)

시퀀스나 이터레이터일 수 있는 data의 표본 산술 평균을 반환합니다.

산술 평균은 데이터의 합을 데이터 포인트 수로 나눈 값입니다. 흔히 "평균" 이라고 합니다만, 많은 수학적 평균중 하나일 뿐입니다. 데이터의 중심 위치에 대한 측정(measure)입니다.

data가 비어 있으면, StatisticsError가 발생합니다.

사용 예:

>>> mean([1, 2, 3, 4, 4])
2.8
>>> mean([-1.0, 2.5, 3.25, 5.75])
2.625

>>> from fractions import Fraction as F
>>> mean([F(3, 7), F(1, 21), F(5, 3), F(1, 3)])
Fraction(13, 21)

>>> from decimal import Decimal as D
>>> mean([D("0.5"), D("0.75"), D("0.625"), D("0.375")])
Decimal('0.5625')

참고

평균은 특이치(outlier)의 영향을 많이 받으며, 중심 위치에 대한 강인한(robust) 추정치가 아닙니다: 평균이 반드시 데이터 포인트의 전형적인 예는 아닙니다. 덜 효율적이기는 하지만, 더 강인한 중심 위치의 측정은, median()mode()를 참조하십시오. (이 경우, "효율"은 계산 효율성이 아닌 통계 효율성을 나타냅니다.)

flowdas

통계적 효율(statistical efficiency)이란 통계적인 값이나 절차의 품질에 대한 것입니다. 효율이 높다면 필요로 하는 품질의 결과를 얻는데 더 적은 표본이 필요하다는 뜻입니다. 따라서 중앙값이나 최빈값이 특이치의 영향을 덜 받기는 하지만, 더 많은 표본을 요구한다는 뜻입니다.

표본 평균은 실제 모집단 평균의 편향되지 않은(unbiased) 추정치를 제공합니다. 즉, 가능한 모든 표본에 대해 평균을 취하면, mean(sample)은 전체 모집단의 실제 평균에 수렴합니다. data가 표본이 아닌 전체 모집단을 나타낸다면, mean(data)는 실제 모집단 평균 μ를 계산하는 것과 동등합니다.

statistics.fmean(data)

data를 float로 변환하고 산술 평균을 계산합니다.

mean() 함수보다 빠르게 실행되며 항상 float를 반환합니다. 결과는 매우 정확하지만 mean()만큼 완벽하지는 않습니다. 입력 data가 비어 있으면 StatisticsError를 발생시킵니다.

>>> fmean([3.5, 4.0, 5.25])
4.25

버전 3.8에 추가.

statistics.geometric_mean(data)

data를 float로 변환하고 기하 평균(geometric mean)을 계산합니다.

입력 data가 비어 있거나, 0을 포함하거나, 음수 값을 포함하면 StatisticsError를 발생시킵니다.

정확한 결과를 얻기 위해 특별한 노력을 기울이지는 않습니다. (하지만, 향후 변경 될 수 있습니다.)

>>> round(geometric_mean([54, 24, 36]), 9)
36.0

버전 3.8에 추가.

statistics.harmonic_mean(data)

실수 값 숫자의 시퀀스나 이터레이터인 data의 조화 평균(harmonic mean)을 반환합니다.

때때로 subcontrary mean이라고도 하는 조화 평균은 데이터의 역수의 산술 mean()의 역수입니다. 예를 들어, a, bc 세 값의 조화 평균은 3/(1/a + 1/b + 1/c)와 동등합니다.

조화 평균은 데이터의 중심 위치의 측정인 평균의 한가지 유형입니다. 에를 들어 속도와 같은 률(rate)이나 비율(ratio)인 수량을 평균할 때 종종 적합합니다. 예를 들면:

투자자가 P/E (가격/수익) 비율이 2.5, 3 및 10 인 세 회사 각각에서 같은 비용으로 주식을 구입한다고 가정해 봅시다. 투자자 포트폴리오의 평균 P/E 비율은 무엇입니까?

>>> harmonic_mean([2.5, 3, 10])  # 균등 투자 포트폴리오.
3.6

산술 평균을 사용하면 평균이 약 5.167이 되어, 너무 높습니다.

data가 비어 있거나 0보다 작은 값이 있으면 StatisticsError가 발생합니다.

버전 3.6에 추가.

statistics.median(data)

일반적인 "중간 2개의 평균" 방법을 사용하여, 숫자 data의 중앙 값(중간 값)을 반환합니다. data가 비어 있으면, StatisticsError가 발생합니다. data는 시퀀스나 이터레이터일 수 있습니다.

중앙값은 중심 위치에 대한 강인한 측정이며, 데이터에 특이치가 있을 때 영향을 덜받습니다. 데이터 포인트 수가 홀수면, 가운데 데이터 포인트가 반환됩니다:

>>> median([1, 3, 5])
3

데이터 포인트 수가 짝수면, 중앙 값은 두 가운데 값의 평균을 취하여 보간됩니다:

>>> median([1, 3, 5, 7])
4.0

데이터가 이산(discrete)적이고, 중앙값이 실제 데이터 포인트가 아니라도 상관 없을 때 적합합니다.

데이터가 순서는 있지만 (대소 비교 지원) 숫자가 아니면 (덧셈을 지원하지 않음), median_low()median_high()를 대신 사용해야 합니다.

flowdas

데이터 포인트 수가 홀수일 때는 산술 연산이 필요하지 않기 때문에 대소 비교가 되기만 한다면 동작합니다. 하지만 이 연산이 공식 지원된다고 볼 수는 없습니다.

statistics.median_low(data)

숫자 데이터의 낮은 중앙값을 반환합니다. data가 비어 있으면 StatisticsError가 발생합니다. data는 시퀀스나 이터레이터일 수 있습니다.

flowdas

숫자 데이터만 지원하는 것처럼 설명되어 있지만. 대소 비교만 지원되는 숫자가 아닌 데이터도 지원합니다. median()의 설명에서 이를 지원한다고 언급하고 있기 때문에, 공식 지원된다고 보아야 합니다.

낮은 중앙값은 항상 데이터 세트의 멤버입니다. 데이터 포인트 수가 홀수이면 중간 값이 반환됩니다. 짝수이면, 두 중간 값 중 작은 값이 반환됩니다.

>>> median_low([1, 3, 5])
3
>>> median_low([1, 3, 5, 7])
3

데이터가 이산(discrete)적이고 보간된 값이 아닌 실제 데이터 포인트를 중앙값으로 선호 할 때 낮은 중앙값을 사용하십시오.

statistics.median_high(data)

데이터의 높은 중앙값을 반환합니다. data가 비어 있으면 StatisticsError가 발생합니다. data는 시퀀스나 이터레이터일 수 있습니다.

flowdas

대소 비교만 지원되는 숫자가 아닌 데이터도 지원합니다. median()의 설명에서 이를 지원한다고 언급하고 있기 때문에, 공식 지원된다고 보아야 합니다.

높은 중앙값은 항상 데이터 세트의 멤버입니다. 데이터 포인트 수가 홀수이면 중간 값이 반환됩니다. 짝수이면, 두 중간 값 중 큰 값이 반환됩니다.

>>> median_high([1, 3, 5])
3
>>> median_high([1, 3, 5, 7])
5

데이터가 이산(discrete)적이고 보간된 값이 아닌 실제 데이터 포인트를 중앙값으로 선호 할 때 높은 중앙값을 사용하십시오.

statistics.median_grouped(data, interval=1)

보간법을 사용하여, 50번째 백분위 수로 계산된, 연속 데이터의 그룹 중앙값을 반환합니다. data가 비어 있으면, StatisticsError가 발생합니다. data는 시퀀스나 이터레이터일 수 있습니다.

>>> median_grouped([52, 52, 53, 54])
52.5

다음 예에서, 각 값이 데이터 클래스의 중간 점을 나타내도록 데이터가 자리올림 됩니다. 예를 들어, 1은 클래스 0.5--1.5의 중간 점, 2는 1.5--2.5의 중간 점, 3은 2.5--3.5의 중간 점, 등입니다. 주어진 데이터에서 중간 값은 3.5--4.5 클래스 어딘가에 있고, 보간법을 사용하여 추정합니다:

>>> median_grouped([1, 2, 2, 3, 4, 4, 4, 4, 4, 5])
3.7

선택적 인자 interval는 클래스 간격을 나타내며, 기본값은 1입니다. 클래스 간격을 변경하면 자연스럽게 보간이 변경됩니다:

>>> median_grouped([1, 3, 3, 5, 7], interval=1)
3.25
>>> median_grouped([1, 3, 3, 5, 7], interval=2)
3.5

이 함수는 데이터 포인트가 적어도 interval 만큼 떨어져 있는지 확인하지 않습니다.

CPython implementation detail: 경우에 따라, median_grouped()는 데이터 포인트를 float로 강제 변환할 수 있습니다. 이 동작은 향후에 변경 될 수 있습니다.

더 보기

  • "Statistics for the Behavioral Sciences", Frederick J Gravetter 와 Larry B Wallnau (8판).

  • Gnome Gnumeric 스프레드시트의 SSMEDIAN 함수, 이 토론도 참조하세요

statistics.mode(data)

이산(discrete)적이거나 범주(nominal)적인 data에서 가장 흔한 단일 데이터 포인트를 반환합니다. 최빈값(mode)은 (존재하할 때) 가장 흔한 값이며 중심 위치의 측정으로 기능합니다.

flowdas

nominal은 흔히 사용되는 "명목적" 이나 "공칭적" 대신에, 데이터 사이언스에서 흔히 사용되고 이해하기도 쉬운 "범주적" 이라는 단어로 번역했습니다. categorical로 해석한 것인데, 뜻은 통하리라고 봅니다.

여러 최빈값이 있으면, data에서 처음 발견된 첫번 째 값을 반환합니다. data가 비어 있으면, StatisticsError가 발생합니다.

mode는 이산 데이터를 가정하고, 단일 값을 반환합니다. 이것이 학교에서 일반적으로 가르치는 최빈값의 표준적인 처리입니다:

>>> mode([1, 1, 2, 3, 3, 3, 3, 4])
3

최빈값은 범주(nominal)적 (숫자가 아닌) 데이터에도 적용되는 유일한 통계라는 점에서 특별합니다:

flowdas

숫자가 아닌 데이터에 적용될 수 있는 것은 median_low()median_high()가 제공하는 중앙값도 마찬가지입니다만, 대소 비교도 지원되지 않는 데이터에 적용할 수 있는 것은 최빈값뿐입니다.

>>> mode(["red", "blue", "blue", "red", "green", "red", "red"])
'red'

버전 3.8에서 변경: 이제 첫 번째 최빈값을 모드를 반환하여 다봉(multimodal) 데이터 세트를 처리합니다. 이전에는, 둘 이상의 최빈값이 발견되면 StatisticsError가 발생했습니다.

statistics.multimode(data)

data에서 먼저 발견되는 순서대로 가장 자주 등장하는 값의 리스트를 반환합니다. 여러 최빈값이 있으면 둘 이상의 결과를 반환하고, data가 비어 있으면 빈 리스트를 반환합니다:

>>> multimode('aabbbbccddddeeffffgg')
['b', 'd', 'f']
>>> multimode('')
[]

버전 3.8에 추가.

statistics.pstdev(data, mu=None)

Return the population standard deviation (the square root of the population variance). See pvariance() for arguments and other details.

>>> pstdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
0.986893273527251
statistics.pvariance(data, mu=None)

Return the population variance of data, a non-empty iterable of real-valued numbers. Variance, or second moment about the mean, is a measure of the variability (spread or dispersion) of data. A large variance indicates that the data is spread out; a small variance indicates it is clustered closely around the mean.

If the optional second argument mu is given, it should be the mean of data. If it is missing or None (the default), the mean is automatically calculated.

Use this function to calculate the variance from the entire population. To estimate the variance from a sample, the variance() function is usually a better choice.

Raises StatisticsError if data is empty.

Examples:

>>> data = [0.0, 0.25, 0.25, 1.25, 1.5, 1.75, 2.75, 3.25]
>>> pvariance(data)
1.25

If you have already calculated the mean of your data, you can pass it as the optional second argument mu to avoid recalculation:

>>> mu = mean(data)
>>> pvariance(data, mu)
1.25

This function does not attempt to verify that you have passed the actual mean as mu. Using arbitrary values for mu may lead to invalid or impossible results.

Decimals and Fractions are supported:

>>> from decimal import Decimal as D
>>> pvariance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('24.815')

>>> from fractions import Fraction as F
>>> pvariance([F(1, 4), F(5, 4), F(1, 2)])
Fraction(13, 72)

참고

When called with the entire population, this gives the population variance σ². When called on a sample instead, this is the biased sample variance s², also known as variance with N degrees of freedom.

If you somehow know the true population mean μ, you may use this function to calculate the variance of a sample, giving the known population mean as the second argument. Provided the data points are representative (e.g. independent and identically distributed), the result will be an unbiased estimate of the population variance.

statistics.stdev(data, xbar=None)

Return the sample standard deviation (the square root of the sample variance). See variance() for arguments and other details.

>>> stdev([1.5, 2.5, 2.5, 2.75, 3.25, 4.75])
1.0810874155219827
statistics.variance(data, xbar=None)

Return the sample variance of data, an iterable of at least two real-valued numbers. Variance, or second moment about the mean, is a measure of the variability (spread or dispersion) of data. A large variance indicates that the data is spread out; a small variance indicates it is clustered closely around the mean.

If the optional second argument xbar is given, it should be the mean of data. If it is missing or None (the default), the mean is automatically calculated.

Use this function when your data is a sample from a population. To calculate the variance from the entire population, see pvariance().

Raises StatisticsError if data has fewer than two values.

Examples:

>>> data = [2.75, 1.75, 1.25, 0.25, 0.5, 1.25, 3.5]
>>> variance(data)
1.3720238095238095

If you have already calculated the mean of your data, you can pass it as the optional second argument xbar to avoid recalculation:

>>> m = mean(data)
>>> variance(data, m)
1.3720238095238095

This function does not attempt to verify that you have passed the actual mean as xbar. Using arbitrary values for xbar can lead to invalid or impossible results.

Decimal and Fraction values are supported:

>>> from decimal import Decimal as D
>>> variance([D("27.5"), D("30.25"), D("30.25"), D("34.5"), D("41.75")])
Decimal('31.01875')

>>> from fractions import Fraction as F
>>> variance([F(1, 6), F(1, 2), F(5, 3)])
Fraction(67, 108)

참고

This is the sample variance s² with Bessel's correction, also known as variance with N-1 degrees of freedom. Provided that the data points are representative (e.g. independent and identically distributed), the result should be an unbiased estimate of the true population variance.

If you somehow know the actual population mean μ you should pass it to the pvariance() function as the mu parameter to get the variance of a sample.

statistics.quantiles(dist, *, n=4, method='exclusive')

Divide dist into n continuous intervals with equal probability. Returns a list of n - 1 cut points separating the intervals.

Set n to 4 for quartiles (the default). Set n to 10 for deciles. Set n to 100 for percentiles which gives the 99 cuts points that separate dist in to 100 equal sized groups. Raises StatisticsError if n is not least 1.

The dist can be any iterable containing sample data or it can be an instance of a class that defines an inv_cdf() method. For meaningful results, the number of data points in dist should be larger than n. Raises StatisticsError if there are not at least two data points.

For sample data, the cut points are linearly interpolated from the two nearest data points. For example, if a cut point falls one-third of the distance between two sample values, 100 and 112, the cut-point will evaluate to 104.

The method for computing quantiles can be varied depending on whether the data in dist includes or excludes the lowest and highest possible values from the population.

The default method is "exclusive" and is used for data sampled from a population that can have more extreme values than found in the samples. The portion of the population falling below the i-th of m sorted data points is computed as i / (m + 1). Given nine sample values, the method sorts them and assigns the following percentiles: 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%.

Setting the method to "inclusive" is used for describing population data or for samples that are known to include the most extreme values from the population. The minimum value in dist is treated as the 0th percentile and the maximum value is treated as the 100th percentile. The portion of the population falling below the i-th of m sorted data points is computed as (i - 1) / (m - 1). Given 11 sample values, the method sorts them and assigns the following percentiles: 0%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%, 100%.

If dist is an instance of a class that defines an inv_cdf() method, setting method has no effect.

# Decile cut points for empirically sampled data
>>> data = [105, 129, 87, 86, 111, 111, 89, 81, 108, 92, 110,
...         100, 75, 105, 103, 109, 76, 119, 99, 91, 103, 129,
...         106, 101, 84, 111, 74, 87, 86, 103, 103, 106, 86,
...         111, 75, 87, 102, 121, 111, 88, 89, 101, 106, 95,
...         103, 107, 101, 81, 109, 104]
>>> [round(q, 1) for q in quantiles(data, n=10)]
[81.0, 86.2, 89.0, 99.4, 102.5, 103.6, 106.0, 109.8, 111.0]

>>> # Quartile cut points for the standard normal distibution
>>> Z = NormalDist()
>>> [round(q, 4) for q in quantiles(Z, n=4)]
[-0.6745, 0.0, 0.6745]

버전 3.8에 추가.

예외

A single exception is defined:

exception statistics.StatisticsError

Subclass of ValueError for statistics-related exceptions.

NormalDist 객체

NormalDist is a tool for creating and manipulating normal distributions of a random variable. It is a composite class that treats the mean and standard deviation of data measurements as a single entity.

Normal distributions arise from the Central Limit Theorem and have a wide range of applications in statistics.

class statistics.NormalDist(mu=0.0, sigma=1.0)

Returns a new NormalDist object where mu represents the arithmetic mean and sigma represents the standard deviation.

If sigma is negative, raises StatisticsError.

mean

A read-only property for the arithmetic mean of a normal distribution.

stdev

A read-only property for the standard deviation of a normal distribution.

variance

A read-only property for the variance of a normal distribution. Equal to the square of the standard deviation.

classmethod from_samples(data)

Makes a normal distribution instance computed from sample data. The data can be any iterable and should consist of values that can be converted to type float.

If data does not contain at least two elements, raises StatisticsError because it takes at least one point to estimate a central value and at least two points to estimate dispersion.

samples(n, *, seed=None)

Generates n random samples for a given mean and standard deviation. Returns a list of float values.

If seed is given, creates a new instance of the underlying random number generator. This is useful for creating reproducible results, even in a multi-threading context.

pdf(x)

Using a probability density function (pdf), compute the relative likelihood that a random variable X will be near the given value x. Mathematically, it is the ratio P(x <= X < x+dx) / dx.

The relative likelihood is computed as the probability of a sample occurring in a narrow range divided by the width of the range (hence the word "density"). Since the likelihood is relative to other points, its value can be greater than 1.0.

cdf(x)

Using a cumulative distribution function (cdf), compute the probability that a random variable X will be less than or equal to x. Mathematically, it is written P(X <= x).

inv_cdf(p)

Compute the inverse cumulative distribution function, also known as the quantile function or the percent-point function. Mathematically, it is written x : P(X <= x) = p.

Finds the value x of the random variable X such that the probability of the variable being less than or equal to that value equals the given probability p.

overlap(other)

Compute the overlapping coefficient (OVL) between two normal distributions, giving a measure of agreement. Returns a value between 0.0 and 1.0 giving the overlapping area for the two probability density functions.

Instances of NormalDist support addition, subtraction, multiplication and division by a constant. These operations are used for translation and scaling. For example:

>>> temperature_february = NormalDist(5, 2.5)             # Celsius
>>> temperature_february * (9/5) + 32                     # Fahrenheit
NormalDist(mu=41.0, sigma=4.5)

Dividing a constant by an instance of NormalDist is not supported because the result wouldn't be normally distributed.

Since normal distributions arise from additive effects of independent variables, it is possible to add and subtract two independent normally distributed random variables represented as instances of NormalDist. For example:

>>> birth_weights = NormalDist.from_samples([2.5, 3.1, 2.1, 2.4, 2.7, 3.5])
>>> drug_effects = NormalDist(0.4, 0.15)
>>> combined = birth_weights + drug_effects
>>> round(combined.mean, 1)
3.1
>>> round(combined.stdev, 1)
0.5

버전 3.8에 추가.

NormalDist 예제와 조리법

NormalDist readily solves classic probability problems.

For example, given historical data for SAT exams showing that scores are normally distributed with a mean of 1060 and a standard deviation of 192, determine the percentage of students with test scores between 1100 and 1200, after rounding to the nearest whole number:

>>> sat = NormalDist(1060, 195)
>>> fraction = sat.cdf(1200 + 0.5) - sat.cdf(1100 - 0.5)
>>> round(fraction * 100.0, 1)
18.4

Find the quartiles and deciles for the SAT scores:

>>> [round(sat.inv_cdf(p)) for p in (0.25, 0.50, 0.75)]
[928, 1060, 1192]
>>> [round(sat.inv_cdf(p / 10)) for p in range(1, 10)]
[810, 896, 958, 1011, 1060, 1109, 1162, 1224, 1310]

What percentage of men and women will have the same height in two normally distributed populations with known means and standard deviations?

>>> men = NormalDist(70, 4)
>>> women = NormalDist(65, 3.5)
>>> ovl = men.overlap(women)
>>> round(ovl * 100.0, 1)
50.3

To estimate the distribution for a model than isn't easy to solve analytically, NormalDist can generate input samples for a Monte Carlo simulation:

>>> def model(x, y, z):
...     return (3*x + 7*x*y - 5*y) / (11 * z)
...
>>> n = 100_000
>>> X = NormalDist(10, 2.5).samples(n)
>>> Y = NormalDist(15, 1.75).samples(n)
>>> Z = NormalDist(5, 1.25).samples(n)
>>> NormalDist.from_samples(map(model, X, Y, Z))     
NormalDist(mu=19.640137307085507, sigma=47.03273142191088)

Normal distributions commonly arise in machine learning problems.

Wikipedia has a nice example of a Naive Bayesian Classifier. The challenge is to predict a person's gender from measurements of normally distributed features including height, weight, and foot size.

We're given a training dataset with measurements for eight people. The measurements are assumed to be normally distributed, so we summarize the data with NormalDist:

>>> height_male = NormalDist.from_samples([6, 5.92, 5.58, 5.92])
>>> height_female = NormalDist.from_samples([5, 5.5, 5.42, 5.75])
>>> weight_male = NormalDist.from_samples([180, 190, 170, 165])
>>> weight_female = NormalDist.from_samples([100, 150, 130, 150])
>>> foot_size_male = NormalDist.from_samples([12, 11, 12, 10])
>>> foot_size_female = NormalDist.from_samples([6, 8, 7, 9])

Next, we encounter a new person whose feature measurements are known but whose gender is unknown:

>>> ht = 6.0        # height
>>> wt = 130        # weight
>>> fs = 8          # foot size

Starting with a 50% prior probability of being male or female, we compute the posterior as the prior times the product of likelihoods for the feature measurements given the gender:

>>> prior_male = 0.5
>>> prior_female = 0.5
>>> posterior_male = (prior_male * height_male.pdf(ht) *
...                   weight_male.pdf(wt) * foot_size_male.pdf(fs))

>>> posterior_female = (prior_female * height_female.pdf(ht) *
...                     weight_female.pdf(wt) * foot_size_female.pdf(fs))

The final prediction goes to the largest posterior. This is known as the maximum a posteriori or MAP:

>>> 'male' if posterior_male > posterior_female else 'female'
'female'