statistics — Funciones de estadística matemática

Nuevo en la versión 3.4.

Código fuente: Lib/statistics.py


Este módulo proporciona funciones para calcular estadísticas matemáticas de datos numéricos (de tipo Real).

Este módulo no pretende ser competidor o sustituto de bibliotecas de terceros como NumPy o SciPy, ni de paquetes completos de software propietario para profesionales como Minitab, SAS o Matlab. Este módulo se ubica a nivel de calculadoras científicas gráficas.

A menos que se indique explícitamente lo contrario, las funciones de este módulo manejan objetos int, float, Decimal y Fraction. No se garantiza un correcto funcionamiento con otros tipos (numéricos o no). El comportamiento de estas funciones con colecciones mixtas que contengan objetos de diferente tipo no está definido y depende de la implementación. Si tus datos de entrada consisten en una mezcla de varios tipos, puedes usar map() para asegurarte de que el resultado sea consistente, por ejemplo: map(float, input_data).

Promedios y medidas de tendencia central

Estas funciones calculan el promedio o el valor típico de una población o muestra.

mean()

Media aritmética («promedio») de los datos.

fmean()

Media aritmética usando coma flotante, más rápida.

geometric_mean()

Media geométrica de los datos.

harmonic_mean()

Media armónica de los datos.

median()

Mediana (valor central) de los datos.

median_low()

Mediana baja de los datos.

median_high()

Mediana alta de los datos.

median_grouped()

Mediana, o percentil 50, de los datos agrupados.

mode()

Moda única (valor más común) de datos discretos o nominales.

multimode()

Lista de modas (valores más comunes) de datos discretos o nominales.

quantiles()

Divide los datos en intervalos equiprobables.

Medidas de dispersión

Estas funciones calculan una medida de cuánto tiende a desviarse la población o muestra de los valores típicos o promedios.

pstdev()

Desviación típica poblacional de los datos.

pvariance()

Varianza poblacional de los datos.

stdev()

Desviación típica muestral de los datos.

variance()

Varianza muestral de los datos.

Detalles de las funciones

Nota: Las funciones no requieren que se ordenen los datos que se les proporcionan. Sin embargo, para facilitar la lectura, la mayoría de los ejemplos muestran secuencias ordenadas.

statistics.mean(data)

Retorna la media aritmética muestral de data, que puede ser una secuencia o un iterable.

La media aritmética es la suma de los valores dividida entre el número de observaciones. Es comúnmente denominada «promedio», aunque hay muchas formas de definir el promedio matemáticamente. Es una medida de tendencia central de los datos.

Se lanza una excepción StatisticsError si data está vacío.

Algunos ejemplos de uso:

>>> 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')

Nota

La media aritmética se ve fuertemente afectada por la presencia de valores atípicos en la muestra y no es un estimador robusto de tendencia central: la media no es necesariamente un ejemplo representativo de la muestra. Consulta median() y mode() para obtener medidas más robustas de tendencia central.

La media muestral proporciona una estimación no sesgada de la media real de la población. Por lo tanto, al calcular el promedio de todas las muestras posibles, mean(sample) converge con el promedio real de toda la población. Si data representa a una población completa, en lugar de a una muestra, entonces mean(data) equivale a calcular la media poblacional verdadera μ.

statistics.fmean(data)

Convierte los valores de data a flotantes y calcula la media aritmética.

Esta función se ejecuta más rápido que mean() y siempre retorna un float. data puede ser una secuencia o un iterable. Si el conjunto de datos de entrada está vacío, se lanza una excepción StatisticsError.

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

Nuevo en la versión 3.8.

statistics.geometric_mean(data)

Convierte los valores de data a flotantes y calcula la media geométrica.

La media geométrica indica la tendencia central o valor típico de data utilizando el producto de los valores (en oposición a la media aritmética, que utiliza su suma).

Lanza una excepción StatisticsError si el conjunto de datos de entrada está vacío, o si contiene un cero o un valor negativo. data puede ser una secuencia o un iterable.

No se toman medidas especiales para garantizar que el resultado sea completamente preciso. (Sin embargo, esto puede cambiar en una versión futura.)

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

Nuevo en la versión 3.8.

statistics.harmonic_mean(data)

Retorna la media armónica de data, que debe ser una secuencia o un iterable de números que pertenezcan al conjunto de los números reales.

La media armónica es el inverso o recíproco de la media aritmética (mean()) de los inversos multiplicativos de los datos. Por ejemplo, la media armónica de tres valores a, b y c es 3/(1/a + 1/b + 1/c). El resultado es cero si uno de los valores es cero.

La media armónica es un tipo de promedio, una medida de la tendencia central de los datos. Generalmente es adecuada para calcular promedios de tasas o fracciones, por ejemplo, velocidades.

Supongamos que un automóvil viaja 10 km a 40 km/h, luego otros 10 km a 60 km/h. ¿Cuál es su velocidad media?

>>> harmonic_mean([40, 60])
48.0

Supongamos que un inversor compra la misma cantidad de acciones de tres empresas diferentes, con unos PER (ratio precio-beneficio) de 2.5, 3 y 10. ¿Cuál es el PER promedio para la cartera del inversor?

>>> harmonic_mean([2.5, 3, 10])  # For an equal investment portfolio.
3.6

Una excepción StatisticsError es lanzada si data está vacío o algún elemento es menor que cero.

El algoritmo actual tiene una salida anticipada cuando encuentra un cero en la entrada. Esto significa que no se comprueba la validez de las entradas posteriores al cero. (Este comportamiento puede cambiar en el futuro.)

Nuevo en la versión 3.6.

statistics.median(data)

Retorna la mediana (valor central) de los datos numéricos, utilizando el método clásico de «media de los dos del medio». Si data está vacío, se lanza una excepción StatisticsError. data puede ser una secuencia o un iterable.

La mediana es una medida de tendencia central robusta y es menos sensible a la presencia de valores atípicos que la media. Cuando el número de casos es impar, se retorna el valor central:

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

Cuando el número de observaciones es par, la mediana se interpola calculando el promedio de los dos valores centrales:

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

Este enfoque es adecuado para datos discretos, siempre que se acepte que la mediana no es necesariamente parte de las observaciones.

Si los datos son ordinales (se pueden ordenar) pero no numéricos (no se pueden sumar), considera usar median_low() o median_high() en su lugar.

statistics.median_low(data)

Retorna la mediana baja de los datos numéricos. Se lanza una excepción StatisticsError si data está vacío. data puede ser una secuencia o un iterable.

La mediana baja es siempre un valor presente en el conjunto de datos. Cuando el número de casos es impar, se retorna el valor central. Cuando el número de casos es par, se retorna el menor de los dos valores centrales.

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

Utiliza la mediana baja cuando tus datos sean discretos y prefieras que la mediana sea un valor representado en tus observaciones, en lugar de ser el resultado de una interpolación.

statistics.median_high(data)

Retorna la mediana alta de los datos. Lanza una excepción StatisticsError si data está vacío. data puede ser una secuencia o un iterable.

La mediana alta es siempre un valor presente en el conjunto de datos. Cuando el número de casos es impar, se retorna el valor central. Cuando el número de casos es par, se retorna el mayor de los dos valores centrales.

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

Utiliza la mediana alta cuando tus datos sean discretos y prefieras que la mediana sea un valor representado en tus observaciones, en lugar de ser el resultado de una interpolación.

statistics.median_grouped(data, interval=1)

Retorna la mediana de los datos continuos agrupados, calculada como el percentil 50, usando interpolación. Se lanza una excepción StatisticsError si data está vacío. data puede ser una secuencia o un iterable.

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

En el siguiente ejemplo, los valores se redondean para que cada valor represente la mitad de un grupo. Por ejemplo, 1 es la mitad del grupo 0.5–1.5, 2 es la mitad del grupo 1.5–2.5, 3 es la mitad de 2.5–3.5, etc. En los datos proporcionados a continuación, el valor medio está en algún lugar del grupo que va de 3,5 a 4,5 y se estima mediante interpolación:

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

El argumento opcional interval representa el intervalo de clase y el valor predeterminado es 1. Cambiar el intervalo de clase cambiará la interpolación, como es natural:

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

Esta función no comprueba si los valores están separados por al menos un interval.

CPython implementation detail: Bajo algunas circunstancias, median_grouped() puede convertir algunos de los valores proporcionados en flotantes. Es probable que este comportamiento cambie en el futuro.

Ver también

  • «Statistics for the Behavioral Sciences», Frederick J Gravetter y Larry B Wallnau (8ª edición).

  • La función SSMEDIAN del programa de hojas de cálculo Gnumeric de Gnome, incluyendo esta discusión.

statistics.mode(data)

Retorna el valor más común del conjunto de datos discretos o nominales data.La moda (cuando existe) es el valor más representativo y sirve como medida de tendencia central.

Si hay varias modas con la misma frecuencia, retorna la primera encontrada en data. Si deseas la menor o la mayor de ellas, usa min(multimode(data)) o max(multimode(data)). Se lanza una excepción StatisticsError si la entrada data está vacía.

mode asume que los datos de entrada son discretos y retorna un solo valor. Esta es la definición habitual de la moda que se enseña en las escuelas:

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

La moda tiene la particularidad de ser la única estadística de este módulo que se puede calcular sobre datos nominales (no numéricos):

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

Distinto en la versión 3.8: Ahora maneja conjuntos de datos multimodales, retornando la primera moda encontrada. Anteriormente, se lanzaba una excepción StatisticsError cuando se daba esta situación.

statistics.multimode(data)

Retorna una lista de los valores más frecuentes en el orden en que aparecen en data. Retornará varios resultados en el caso de que existan varias modas, o una lista vacía si data está vacío:

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

Nuevo en la versión 3.8.

statistics.pstdev(data, mu=None)

Retorna la desviación típica poblacional (la raíz cuadrada de la varianza poblacional). Consultar pvariance() para los argumentos y otros detalles.

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

Retorna la varianza poblacional de data, que debe ser una secuencia no vacía o un iterable de números reales. La varianza, o momento de segundo orden respecto a la media, es una medida de la variabilidad (o dispersión) de los datos. Una alta varianza indica una amplia dispersión de valores; una varianza baja indica que los valores están agrupados alrededor de la media.

El segundo argumento opcional mu, que normalmente será la media de data, también se puede utilizar para calcular el momento de segundo orden alrededor de un punto que no es la media. Si no se proporciona o es None (el valor predeterminado), la media aritmética se calcula automáticamente.

Utiliza esta función para calcular la varianza de toda la población. Para estimar la varianza de una muestra, la función variance() suele ser una opción mejor.

Lanza una excepción StatisticsError si data está vacío.

Ejemplos:

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

Si ya has calculado la media de tus datos, puedes pasarla como segundo argumento opcional mu para evitar que se tenga que volver a calcular:

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

Se admiten decimales (Decimal) y fracciones (Fraction):

>>> 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)

Nota

Esta función retorna la varianza poblacional σ² cuando se aplica a toda la población. Si se aplica solo a una muestra, el resultado es la varianza muestral s², conocida también como varianza con N grados de libertad.

Si se conoce de antemano la verdadera media poblacional μ, se puede usar esta función para calcular la varianza muestral, pasando la media poblacional conocida como segundo argumento. Suponiendo que las observaciones provienen de una selección aleatoria uniforme de la población, el resultado será una estimación no sesgada de la varianza poblacional.

statistics.stdev(data, xbar=None)

Retorna la desviación típica muestral (la raíz cuadrada de la varianza muestral). Consultar variance() para los argumentos y otros detalles.

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

Retorna la varianza muestral de data, que debe ser un iterable de al menos dos números reales. La varianza, o momento de segundo orden respecto a la media, es una medida de la variabilidad (difusión o dispersión) de los datos. Una alta varianza indica que los datos están dispersos; una baja varianza indica que los datos están agrupados estrechamente alrededor de la media.

Si se proporciona el segundo argumento opcional xbar, este debe ser la media aritmética de data. Si no se proporciona o es None (el valor predeterminado), la media aritmética se calcula automáticamente.

Utiliza esta función cuando tus datos sean una muestra de una población. Para calcular la varianza de toda la población, consulta pvariance().

Lanza una excepción StatisticsError si data tiene menos de dos valores.

Ejemplos:

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

Si previamente se ha calculado la media de los datos, puede pasarse como segundo argumento opcional xbar para evitar que se tenga que volver a calcular:

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

Esta función no comprueba si el valor pasado al argumento xbar corresponde al promedio. El uso de valores arbitrarios para xbar produce resultados imposibles o incorrectos.

La función maneja decimales (Decimal) y fracciones (Fraction):

>>> 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)

Nota

Esta es la varianza muestral s² con la corrección de Bessel, también conocida como varianza con N-1 grados de libertad. Suponiendo que las observaciones son representativas de la población (es decir, independientes y distribuidas de forma idéntica), el resultado es una estimación no sesgada de la varianza.

Si conoces de antemano la verdadera media poblacional μ, debes pasarla a pvariance() mediante el parámetro mu para obtener la varianza muestral.

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

Divide data en n intervalos continuos equiprobables. Retorna una lista de n - 1 límites que delimitan los intervalos (cuantiles).

Establece n en 4 para obtener los cuartiles (el valor predeterminado), en 10 para obtener los deciles y en 100 para obtener los percentiles (lo que produce 99 valores que separan data en 100 grupos del mismo tamaño). Si n es menor que 1, se lanza una excepción StatisticsError.

data puede ser cualquier iterable que contenga los valores de la muestra. Para que los resultados sean significativos, el número de observaciones en la muestra data debe ser mayor que n. Si no hay al menos dos observaciones se lanza una excepción StatisticsError.

Los límites de los intervalos se interpolan linealmente a partir de los dos valores más cercanos de la muestra. Por ejemplo, si un límite es un tercio de la distancia entre los valores 100 y 112 de la muestra, el límite será 104.

El argumento method indica el método que se utilizará para calcular los cuantiles y se puede modificar para especificar si se deben incluir o excluir valores de data extremos, altos y bajos, de la población.

El valor predeterminado para method es «exclusive» y es aplicable a los datos muestreados de una población que puede tener valores más extremos que los encontrados en las muestras. La proporción de la población que se encuentra por debajo del i-ésimo valor de m valores ordenados se calcula mediante la fórmula i / (m + 1). Por ejemplo, asumiendo que hay 9 valores en la muestra, este método los ordena y los asocia con los siguientes percentiles: 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80%, 90%.

Si se usa «inclusive» como valor para el parámetro method, se asume que los datos corresponden a una población completa o que los valores extremos de la población están representados en la muestra. El valor mínimo de data se considera entonces como percentil 0 y el máximo como percentil 100. La proporción de la población que se encuentra por debajo del i-ésimo valor de m valores ordenados se calcula mediante la fórmula (i - 1) / (m - 1). Suponiendo que tenemos 11 valores en la muestra, este método los ordena y los asocia con los siguientes percentiles: 0%, 10%, 20%, 30%, 40%, 50%, 60%, 70%, 80 %, 90%, 100%.

# 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]

Nuevo en la versión 3.8.

Excepciones

Se define una sola excepción:

exception statistics.StatisticsError

Subclase de ValueError para excepciones relacionadas con la estadística.

Objetos NormalDist

NormalDist es una herramienta para crear y manipular distribuciones normales de una variable aleatoria. Esta clase gestiona la desviación típica y la media de un conjunto de observaciones como una sola entidad.

Las distribuciones normales surgen del Teorema del límite central y tienen una amplia gama de aplicaciones en estadística.

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

Retorna un nuevo objeto NormalDist donde mu representa la media aritmética y sigma representa la desviación típica.

Se lanza una excepción StatisticsError si sigma es negativo.

mean

Una propiedad de solo lectura para la media aritmética de una distribución normal.

median

Una propiedad de solo lectura para la mediana de una distribución normal.

mode

Una propiedad de solo lectura para la moda de una distribución normal.

stdev

Una propiedad de solo lectura para la desviación típica de una distribución normal.

variance

Una propiedad de solo lectura para la varianza de una distribución normal. Es igual al cuadrado de la desviación típica.

classmethod from_samples(data)

Crea una instancia de distribución normal con los parámetros mu y sigma estimados a partir de data usando fmean() y stdev().

data puede ser cualquier iterable de valores que se puedan convertir al tipo float. Se lanza una excepción StatisticsError si data no contiene al menos dos elementos, esto se debe a que se necesita al menos un punto para estimar un valor central y al menos dos puntos para estimar la dispersión.

samples(n, *, seed=None)

Genera n muestras aleatorias para una media y una desviación típica proporcionadas. Retorna un objeto list de valores float.

Si se proporciona seed, su valor se usa para inicializar una nueva instancia del generador de números aleatorios subyacente. Esto permite producir resultados reproducibles incluso en un contexto de paralelismo con múltiples hilos.

pdf(x)

Haciendo uso de una función de densidad de probabilidad (FPD o PDF en inglés), calcula la verosimilitud relativa de que una variable aleatoria X caiga en una región cercana al valor x proporcionado. Matemáticamente, esto corresponde al límite de la razón P(x <= X < x+dx) / dx cuando dx tiende a cero.

La verosimilitud relativa se calcula como la probabilidad de que una observación pertenezca a un intervalo estrecho dividida entre el ancho del intervalo (de ahí el término «densidad»). Como la verosimilitud es relativa a los otros puntos, su valor puede ser mayor que 1.0.

cdf(x)

Usando una función de distribución acumulada (FDA, CDF en inglés), calcula la probabilidad de que una variable aleatoria X sea menor o igual que x. Matemáticamente, se escribe P(X <= x).

inv_cdf(p)

Calcula la función de distribución acumulada inversa, también conocida como función cuantil o función punto porcentual. Matemáticamente, se escribe x : P(X <= x) = p.

Calcula el valor x de la variable aleatoria X tal que la probabilidad de que la variable sea menor o igual a este valor es igual a la probabilidad p dada.

overlap(other)

Mide la concordancia entre dos distribuciones de probabilidad normales. Retorna un valor entre 0.0 y 1.0 que indica el área de superposición de dos funciones de densidad de probabilidad.

quantiles(n=4)

Divide la distribución normal en n intervalos continuos equiprobables. Retorna una lista de (n - 1) cuantiles que separan los intervalos.

Establece n en 4 para obtener los cuartiles (el valor predeterminado), en 10 para obtener los deciles y en 100 para obtener los percentiles (lo que produce 99 límites que separan los datos en 100 grupos del mismo tamaño).

zscore(x)

Compute the Standard Score describing x in terms of the number of standard deviations above or below the mean of the normal distribution: (x - mean) / stdev.

Nuevo en la versión 3.9.

Las instancias de la clase NormalDist soportan la suma, resta, multiplicación y división por una constante. Estas operaciones se pueden utilizar para traducir o escalar, por ejemplo:

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

No se admite la división de una constante entre una instancia de NormalDist debido a que el resultado no sería una distribución normal.

Dado que las distribuciones normales se derivan de las propiedades aditivas de variables independientes, es posible sumar o restar dos variables independientes con distribución normal representadas por instancias de NormalDist. Por ejemplo :

>>> 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

Nuevo en la versión 3.8.

Ejemplos de uso de NormalDist

NormalDist permite resolver fácilmente problemas probabilísticos clásicos.

Por ejemplo, sabiendo que los datos históricos de los exámenes SAT siguen una distribución normal con una media de 1060 y una desviación típica de 195, determinar el porcentaje de estudiantes con puntuaciones entre 1100 y 1200, redondeado al número entero más cercano:

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

Determinar los cuartiles y deciles de las puntuaciones del SAT:

>>> list(map(round, sat.quantiles()))
[928, 1060, 1192]
>>> list(map(round, sat.quantiles(n=10)))
[810, 896, 958, 1011, 1060, 1109, 1162, 1224, 1310]

Con la finalidad de estimar la distribución de un modelo que es difícil de resolver analíticamente, NormalDist puede generar muestras de entrada para una simulación utilizando el método Montecarlo:

>>> 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, seed=3652260728)
>>> Y = NormalDist(15, 1.75).samples(n, seed=4582495471)
>>> Z = NormalDist(50, 1.25).samples(n, seed=6582483453)
>>> quantiles(map(model, X, Y, Z))       
[1.4591308524824727, 1.8035946855390597, 2.175091447274739]

Las distribuciones normales se pueden utilizar para aproximar distribuciones binomiales cuando el tamaño de la muestra es grande y la probabilidad de un ensayo exitoso es cercana al 50%.

Por ejemplo, 750 personas asisten a una conferencia sobre código abierto y se dispone de dos salas con capacidad para 500 personas cada una. En la primera sala hay una charla sobre Python, en la otra una sobre Ruby. En conferencias pasadas, el 65% de las personas prefirieron escuchar las charlas sobre Python. Suponiendo que las preferencias de la población no hayan cambiado, ¿cuál es la probabilidad de que la sala de Python permanezca por debajo de su capacidad máxima?

>>> n = 750             # Sample size
>>> p = 0.65            # Preference for Python
>>> q = 1.0 - p         # Preference for Ruby
>>> k = 500             # Room capacity

>>> # Approximation using the cumulative normal distribution
>>> from math import sqrt
>>> round(NormalDist(mu=n*p, sigma=sqrt(n*p*q)).cdf(k + 0.5), 4)
0.8402

>>> # Solution using the cumulative binomial distribution
>>> from math import comb, fsum
>>> round(fsum(comb(n, r) * p**r * q**(n-r) for r in range(k+1)), 4)
0.8402

>>> # Approximation using a simulation
>>> from random import seed, choices
>>> seed(8675309)
>>> def trial():
...     return choices(('Python', 'Ruby'), (p, q), k=n).count('Python')
>>> mean(trial() <= k for i in range(10_000))
0.8398

Las distribuciones normales a menudo están involucradas en el aprendizaje automático.

Wikipedia detalla un buen ejemplo de un clasificador bayesiano ingenuo. El objetivo es predecir el género de una persona a partir de características físicas que siguen una distribución normal, como la altura, el peso y el tamaño del pie.

Disponemos de un conjunto de datos de entrenamiento que contiene las medidas de ocho personas. Se supone que estas medidas siguen una distribución normal, por lo que podemos sintetizar los datos usando 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])

A continuación, nos encontramos con un nuevo individuo del que conocemos las medidas de sus características pero no su género:

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

Partiendo de una probabilidad a priori del 50% de ser hombre o mujer, calculamos la probabilidad a posteriori como el producto de la probabilidad a priori y la verosimilitud de las diferentes medidas dado el género:

>>> 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))

La predicción final es la que tiene mayor probabilidad a posteriori. Este enfoque se denomina máximo a posteriori o MAP:

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