statistics — Mathematical statistics functions

Added in version 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 estadistas profesionales como Minitab, SAS o Matlab. Este módulo se ubica a nivel de calculadoras científicas y 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).

Algunos conjuntos de datos utilizan valores NaN (no es un número) para representar los datos que faltan. Dado que los valores NaN tienen una semántica de comparación inusual, provocan comportamientos sorprendentes o indefinidos en las funciones estadísticas que ordenan los datos o que cuentan las ocurrencias. Las funciones afectadas son median(), median_low(), median_high(), median_grouped(), mode(), multimode(), y quantiles(). Los valores NaN deben eliminarse antes de llamar a estas funciones:

>>> from statistics import median
>>> from math import isnan
>>> from itertools import filterfalse

>>> data = [20.7, float('NaN'),19.2, 18.3, float('NaN'), 14.4]
>>> sorted(data)  # This has surprising behavior
[20.7, nan, 14.4, 18.3, 19.2, nan]
>>> median(data)  # This result is unexpected
16.35

>>> sum(map(isnan, data))    # Number of missing values
2
>>> clean = list(filterfalse(isnan, data))  # Strip NaN values
>>> clean
[20.7, 19.2, 18.3, 14.4]
>>> sorted(clean)  # Sorting now works as expected
[14.4, 18.3, 19.2, 20.7]
>>> median(clean)       # This result is now well defined
18.75

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

Fast, floating-point arithmetic mean, with optional weighting.

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

Median (50th percentile) of grouped data.

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.

Estadísticas para relaciones entre dos entradas

Estas funciones calculan estadísticas sobre las relaciones entre dos entradas.

covariance()

Covarianza muestral de dos variables.

correlation()

Coeficiente de correlación de Pearson y Spearman.

linear_regression()

Pendiente e intersección para regresión lineal simple.

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 se ve muy afectada por los outliers y no es necesariamente un ejemplo típico de los puntos de datos. Para una medida más robusta, aunque menos eficiente, de la tendencia central, véase median().

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, weights=None)

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

La ponderación es opcional. Por ejemplo, un profesor asigna una nota a un curso ponderando las pruebas en un 20%, los deberes en un 20%, un examen parcial en un 30% y un examen final en un 30%:

>>> grades = [85, 92, 83, 91]
>>> weights = [0.20, 0.20, 0.30, 0.30]
>>> fmean(grades, weights)
87.6

Si se proporciona weights, debe tener la misma longitud que los data o se producirá un ValueError.

Added in version 3.8.

Distinto en la versión 3.11: Soporte añadido a weights.

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

Added in version 3.8.

statistics.harmonic_mean(data, weights=None)

Return the harmonic mean of data, a sequence or iterable of real-valued numbers. If weights is omitted or None, then equal weighting is assumed.

La media armónica es recíproco de la mean() aritmética de los recíprocos de los datos. Por ejemplo, la media armónica de tres valores a, b and c es equivalente a 3/(1/a + 1/b + 1/c). Si alguno de los valores es cero, el resultado va a ser 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 un automóvil viaja 5 km a 40 km/h, y cuando el tráfico se despeja, acelera a 60 km/h durante los 30 km restantes del viaje. ¿Cuál es su velocidad media?

>>> harmonic_mean([40, 60], weights=[5, 30])
56.0

Una excepción StatisticsError es lanzada si data está vacío, algún elemento es menor que cero, o si la suma ponderada no es positiva.

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

Added in version 3.6.

Distinto en la versión 3.10: Soporte añadido a weights.

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

Estimates the median for numeric data that has been grouped or binned around the midpoints of consecutive, fixed-width intervals.

The data can be any iterable of numeric data with each value being exactly the midpoint of a bin. At least one value must be present.

The interval is the width of each bin.

For example, demographic information may have been summarized into consecutive ten-year age groups with each group being represented by the 5-year midpoints of the intervals:

>>> from collections import Counter
>>> demographics = Counter({
...    25: 172,   # 20 to 30 years old
...    35: 484,   # 30 to 40 years old
...    45: 387,   # 40 to 50 years old
...    55:  22,   # 50 to 60 years old
...    65:   6,   # 60 to 70 years old
... })
...

The 50th percentile (median) is the 536th person out of the 1071 member cohort. That person is in the 30 to 40 year old age group.

The regular median() function would assume that everyone in the tricenarian age group was exactly 35 years old. A more tenable assumption is that the 484 members of that age group are evenly distributed between 30 and 40. For that, we use median_grouped():

>>> data = list(demographics.elements())
>>> median(data)
35
>>> round(median_grouped(data, interval=10), 1)
37.5

The caller is responsible for making sure the data points are separated by exact multiples of interval. This is essential for getting a correct result. The function does not check this precondition.

Inputs may be any numeric type that can be coerced to a float during the interpolation step.

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'

Only hashable inputs are supported. To handle type set, consider casting to frozenset. To handle type list, consider casting to tuple. For mixed or nested inputs, consider using this slower quadratic algorithm that only depends on equality tests: max(data, key=data.count).

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('')
[]

Added in version 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.

If the optional second argument mu is given, it should be the population mean of the data. It can also be used to compute the second moment around a point that is not the mean. If it is missing or None (the default), the arithmetic mean is automatically calculated.

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.

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

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

If you have already calculated the sample 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

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]

Added in version 3.8.

statistics.covariance(x, y, /)

Retorna la covarianza de muestra de dos entradas x y y. La covarianza es una medida de la variabilidad conjunta de dos entradas.

Ambas entradas deben ser del mismo largo (no menor a dos), de lo contrario se lanza StatisticsError.

Ejemplos:

>>> x = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> y = [1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> covariance(x, y)
0.75
>>> z = [9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> covariance(x, z)
-7.5
>>> covariance(z, x)
-7.5

Added in version 3.10.

statistics.correlation(x, y, /, *, method='linear')

Retorna el coeficiente de correlación de Pearson para dos entradas. El coeficiente de correlación de Pearson r toma valores entre -1 y +1. Mide la fuerza y dirección de la relación lineal.

Si method es «ranked», calcula El coeficiente de correlación de Spearman para dos entradas. Los datos se sustituyen por rangos. Los empates se promedian para que valores iguales reciban el mismo rango. El coeficiente resultante mide la fuerza de una relación monótona.

El coeficiente de correlación de Spearman es apropiado para datos ordinales o para datos continuos que no cumplen el requisito de proporción lineal para el coeficiente de correlación de Pearson.

Ambas entradas deben ser del mismo largo (no menor a dos), y no necesitan ser constantes, de lo contrario se lanza StatisticsError.

Ejemplo con “Leyes de Kepler sobre el movimiento planetario <https://es.wikipedia.org/wiki/Leyes_de_Kepler’_:

>>> # Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and  Neptune
>>> orbital_period = [88, 225, 365, 687, 4331, 10_756, 30_687, 60_190]    # days
>>> dist_from_sun = [58, 108, 150, 228, 778, 1_400, 2_900, 4_500] # million km

>>> # Show that a perfect monotonic relationship exists
>>> correlation(orbital_period, dist_from_sun, method='ranked')
1.0

>>> # Observe that a linear relationship is imperfect
>>> round(correlation(orbital_period, dist_from_sun), 4)
0.9882

>>> # Demonstrate Kepler's third law: There is a linear correlation
>>> # between the square of the orbital period and the cube of the
>>> # distance from the sun.
>>> period_squared = [p * p for p in orbital_period]
>>> dist_cubed = [d * d * d for d in dist_from_sun]
>>> round(correlation(period_squared, dist_cubed), 4)
1.0

Added in version 3.10.

Distinto en la versión 3.12: Soporte añadido para el coeficiente de correlación de Spearman.

statistics.linear_regression(x, y, /, *, proportional=False)

Retorna la pendiente e intersección de los parámetros de una regresión lineal simple estimados usando mínimos cuadrados ordinarios. La regresión lineal simple describe la relación entre la variable independiente x y la variable dependiente y en términos de esta función lineal:

y = slope * x + intercept + noise

donde slope e intercept son los parámetros de regresión que son estimados, y noise representa la variabilidad de los datos que no fue explicado por la regresión lineal (es igual a la diferencia entre los valores predichos y reales de la variable dependiente).

Ambas entradas deben ser del mismo largo (no menor a dos), y la variable independiente x no puede ser constante, de lo contrario se lanza StatisticsError.

Por ejemplo, podemos usar las fechas de lanzamiento de las películas de Monty Python para predecir el número acumulativo de películas de Monty Python que se habría producido en 2019 asumiendo que hubiesen mantenido el ritmo:

>>> year = [1971, 1975, 1979, 1982, 1983]
>>> films_total = [1, 2, 3, 4, 5]
>>> slope, intercept = linear_regression(year, films_total)
>>> round(slope * 2019 + intercept)
16

Si proportional es verdadero, se supone que la variable independiente x y la variable dependiente y son directamente proporcionales. Los datos se ajustan a una recta que pasa por el origen. Como la intercept siempre será 0,0, la función lineal subyacente se simplifica a:

y = slope * x + noise

Continuing the example from correlation(), we look to see how well a model based on major planets can predict the orbital distances for dwarf planets:

>>> model = linear_regression(period_squared, dist_cubed, proportional=True)
>>> slope = model.slope

>>> # Dwarf planets:   Pluto,  Eris,    Makemake, Haumea, Ceres
>>> orbital_periods = [90_560, 204_199, 111_845, 103_410, 1_680]  # days
>>> predicted_dist = [math.cbrt(slope * (p * p)) for p in orbital_periods]
>>> list(map(round, predicted_dist))
[5912, 10166, 6806, 6459, 414]

>>> [5_906, 10_152, 6_796, 6_450, 414]  # actual distance in million km
[5906, 10152, 6796, 6450, 414]

Added in version 3.10.

Distinto en la versión 3.11: Soporte añadido a proportional.

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)

Computa el Standard Score describiendo x en términos de los números de desviaciones estándar sobre o bajo la media de una distribución normal: (x - mean) / stdev.

Added in version 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

Added in version 3.8.

Examples and Recipes

Problemas de probabilidad clásicos

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]

Entradas de Monte Carlo para simulaciones

To estimate the distribution for a model that 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, 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]

Aproximación de la distribución binomial

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

>>> # Exact 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, binomialvariate
>>> seed(8675309)
>>> mean(binomialvariate(n, p) <= k for i in range(10_000))
0.8406

Clasificador bayesiano ingenuo

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

Wikipedia detalla un buen ejemplo de un clasificador bayesiano ingenuo. El reto consiste en 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'

Estimación de la densidad del núcleo

It is possible to estimate a continuous probability distribution from a fixed number of discrete samples.

The basic idea is to smooth the data using a kernel function such as a normal distribution, triangular distribution, or uniform distribution. The degree of smoothing is controlled by a scaling parameter, h, which is called the bandwidth.

from random import choice, random

def kde_normal(data, h):
    "Create a continuous probability distribution from discrete samples."

    # Smooth the data with a normal distribution kernel scaled by h.
    K_h = NormalDist(0.0, h)

    def pdf(x):
        'Probability density function.  P(x <= X < x+dx) / dx'
        return sum(K_h.pdf(x - x_i) for x_i in data) / len(data)

    def cdf(x):
        'Cumulative distribution function.  P(X <= x)'
        return sum(K_h.cdf(x - x_i) for x_i in data) / len(data)

    def rand():
        'Random selection from the probability distribution.'
        return choice(data) + K_h.inv_cdf(random())

    return pdf, cdf, rand

“Wikipedia tiene un ejemplo <https://es.wikipedia.org/wiki/Estimaci%C3%B3n_de_Densidad_de_Kernel#Ejemplo>”_ donde podemos usar la fórmula kde_normal() para generar y trazar una función de densidad de probabilidad estimada a partir de una muestra pequeña:

>>> sample = [-2.1, -1.3, -0.4, 1.9, 5.1, 6.2]
>>> pdf, cdf, rand = kde_normal(sample, h=1.5)
>>> xarr = [i/100 for i in range(-750, 1100)]
>>> yarr = [pdf(x) for x in xarr]

Los puntos de xarr y yarr pueden utilizarse para hacer una gráfica de la función de densidad de probabilidad:

Diagrama de dispersión de la función de densidad de probabilidad estimada.

Resample the data to produce 100 new selections:

>>> new_selections = [rand() for i in range(100)]

Determine the probability of a new selection being below 2.0:

>>> round(cdf(2.0), 4)
0.5794

Add a new sample data point and find the new CDF at 2.0:

>>> sample.append(4.9)
>>> round(cdf(2.0), 4)
0.5005