Distribuciones de Probabilidad#

Fuentes de incertidumbre en ML#

  • datos: Incertidumbre proveniente del margen de error que tienen los instrumentos de medicion

  • atributos del modelo: Son variables que representan un subconjunto reducido de toda la realidad del problema. Ademas pueden posteriormente ser mas reducidas, sesgando mas el resultado

  • Arquitectura del modelo: Un modelo en mates es una representación simplificada de la realidad, lo cual hace que haya incertidumbre

Distribuciones de probabilidad discretas#

Distribucion de Bernoulli#

Esta distribucion permite describir ocurrencias con variables binarias.

Ej: Lanzamiento de una moneda

  • La probabilidad de ocurrencia del primer evento es P

  • La probabilidad de ocurrencia del segundo evento es 1 - P

drawing

Distribucion Binomial#

Cuando hay secuencias repetitivas de varios eventos binarios (o eventos de bernoulli) se genera otra distribucion. Conocida como la distribucion binomial.

Ejemplo:

Dados tres lanzamientos, cual es la probabilidad de obtener dos caras?

en la imagen abajo se observa la respuesta

drawing
  • Se observa que el espacio muestral arroja 8 posibles resultados.

  • Solo en 3 de esos 8 se obtienen dos caras.

Ahora, si se quiere la probabilidad de obtener k caras dados n lanzamientos, la formula general es:

\(P(k_{caras} | n_{lanzamientos} ) = P(k,n;p)\)

es igual a:

\(P(k,n;p) = \binom{n}{k}p^{k}(1-p)^{n-k}\)

Recordar la formula de la combinatoria:

\(\binom{n}{k} = \frac{n!}{k!(n-k)!}\)

Por lo cual la formula final es:

\(P(k,n;p) = \frac{n!}{k!(n-k)!}p^{k}(1-p)^{n-k}\)

donde k es el numero de caras, n es el numero de lanzamientos y p es la probabilidad del evento (1/2 para el caso de las monedas)

Entonces, retomando el mismo calculo que se hizo previamente:

Dados tres lanzamientos, cual es la probabilidad de obtener dos caras?

Solucion:

Recordemos que la formula general para la distribucion es:

\(P(k,n;p) = \binom{n}{k}p^{k}(1-p)^{n-k}\) = \(\frac{n!}{k!(n-k)!}p^{k}(1-p)^{n-k}\)

Al reemplazar los valores, se obtiene:

=> \(P(2;3,\frac{1}{2})\) = \(\frac{3!}{2!(3-2)!}\frac{1}{2}^{2}(1-\frac{1}{2})^{3-2}\) = \((\frac{6}{2})(\frac{1}{4})(\frac{1}{2})\)

= \(\frac{3}{8}\)#

Se observa que el resultado es el mismo

Distribucion Binomial en Python#

A continuacion se explican dos llibrerias que recrean una distribucion binomial,

  • Scipy, que calcula las probabilidades basado en la formula general de la distribucion (entrega datos teoricos): \(P(k,n;p) = \frac{n!}{k!(n-k)!}p^{k}(1-p)^{n-k}\)

  • Numpy, que recrea los experimentos con funciones aleatorias (entrega datos experimentales)

Pero antes de ir con las librerias, hagamoslo manual ;)

from math import factorial

Recordando la formula de la ecuacion:

\(P(k,n;p) = \frac{n!}{k!(n-k)!}p^{k}(1-p)^{n-k}\)

Hagamos una funcion que simule esta formula:

def my_binomial(k, n, p):
    return factorial(n)/(factorial(k)*factorial(n-k))*pow(p,k)*pow(1-p,n-k)

y verificamos con el ejercicio que se resolvio manualmente arriba:

my_binomial(2, 3, 0.5)
0.375

dado que \(3/8 = 0.375\), el resultado es correcto

Distribucion binomial de scipy#

Esta libreria permite construir la distribucion dado:

- un numero de intentos - la probabilidad p

Se recrea el ejercicio:

  • Al lanzar tres monedas.

  • Cual es la posibilidad de obtener dos caras?

from scipy.stats import binom

# Se define la distribucion, 3 experimentos, 0.5 probabilidad de exito
dist = binom(3, 0.5)

Para obtener la probabilidad de tener 2 exitos uso el atributo .pmf() (probability mass function)

especificando dos exitos

dist.pmf(2)
0.3750000000000001

Y se obtiene el mismo resultado \(1/8\).

Si se quiere ver la probabilidad de cada caso, podemos hacer el calculo:

values = [0,1,2,3]
probabilities=[binom(3, 0.5).pmf(k) for k in values]
print("Para ",values[0], " de 3 eventos exitosos, la probabilidad de ocurrencia es ", probabilities[0] )
print("Para ",values[1], " de 3 eventos exitosos, la probabilidad de ocurrencia es ", probabilities[1] )
print("Para ",values[2], " de 3 eventos exitosos, la probabilidad de ocurrencia es ", probabilities[2] )
print("Para ",values[3], " de 3 eventos exitosos, la probabilidad de ocurrencia es ", probabilities[3] )
Para  0  de 3 eventos exitosos, la probabilidad de ocurrencia es  0.12499999999999997
Para  1  de 3 eventos exitosos, la probabilidad de ocurrencia es  0.3750000000000001
Para  2  de 3 eventos exitosos, la probabilidad de ocurrencia es  0.3750000000000001
Para  3  de 3 eventos exitosos, la probabilidad de ocurrencia es  0.125

Se recrea otro ejercicio:

  • Al lanzar tres monedas.

  • Cual es la posibilidad de obtener DOS CARAS O MENOS?

Ahora, para obtener la probabilidad de tener 2 exitos o menos uso el atributo .cdf() (cumulative density function).

  • A esto tambien se le llama Distribucion de probabilidad acumulada

Solucion:

\(P(k \leq 2,n = 3, p=1/2)\)

  • al hacerlo manualmente, el resultado es \(7/8\)

  • al hacerlo en python:

# Se define la distribucion
dist = binom(3, 0.5)

# Se usa la distribucion de probabilidad acumulada .cdf(cumulative density funcion)

dist.cdf(2)
0.875

el resultado tambien es \(7/8\)

Simulando la Distribucion Binomial Con Numpy#

A continuacion se hace una simulacion de secuencias con generadores aleatorios

Se usa la funcion binomial de numpy, la cual reproduce el experimento aleatoriamente

import numpy as np
from numpy.random import binomial   # Random number generator based on the binomial distribution
import matplotlib.pyplot as plt
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[8], line 3
      1 import numpy as np
      2 from numpy.random import binomial   # Random number generator based on the binomial distribution
----> 3 import matplotlib.pyplot as plt

ModuleNotFoundError: No module named 'matplotlib'

El generador aleatorio de numpy genera resultados basado en:

  • El numero de veces que se repite un experimento (n)

  • la probabilidad de exito (p)

Ejemplo:

Experimento aleatorio, con 3 intentos y 50% de probabilidad de exito en cada intento

p = 0.5
n = 3
x = binomial(n, p)

#Imprime el resultado
print(x)

# Explica el resultado
print("El resultado indica, de", n ,"intentos, obtuve", x, "exitos")
0
El resultado indica, de 3 intentos, obtuve 0 exitos

Ahora generamos una funcion que tiene que reproducir el experimento multiples veces

  • Analiza como se comporta a medida que el experimento se repite mas veces (n incrementa)

import seaborn as sns
import pandas as pd

def plot_binomial_seaborn(num_attempts):

    values = [0,1,2,3]
    attempts = []

    for i in range(0, num_attempts):
        attempts.append(binomial(n,p))

    simulacion = np.unique(attempts, return_counts=True)[1]/len(attempts)

    teorica = [binom(3, 0.5).pmf(k) for k in values]

    #Data into dataframe
    resultado = {"values":values*2,
                "probabilities":list(simulacion)+teorica,
                "categoria":["simulacion","simulacion","simulacion","simulacion",
                "valor teorico","valor teorico","valor teorico","valor teorico"]
                }
    Experimento = pd.DataFrame(resultado)

    sns.barplot(data=Experimento,x = "values", y="probabilities" ,hue="categoria")
    plt.title("{} experimentos".format(num_attempts))
    plt.show()

Nota: np.unique(attempts, return_counts=True) retorna una lista que contiene dos numpy arrays np.unique(attempts, return_counts=True)[0] es cada posible escenario np.unique(attempts, return_counts=True)[1] es la probabilidad de ese escenario

plot_binomial_seaborn(20)
plot_binomial_seaborn(200)
plot_binomial_seaborn(2000)
../_images/b200de608de7771393a5d2d3077390222c61f5c6eb910b2fe957fae18e9fb99b.png ../_images/fc0cef989f5f0338ac5563a62243484e119f5158d3f2ae08a7a434c0be30ac13.png ../_images/d777c082848da1698e45a57b8647d6bf3f92510c554d07c165e3db202c006091.png

Note que a medida que n incrementa, los resultados experimentales se aproximan mas a los resultados teoricos

Distribucion Multinomial#

Es una distribucion que sigue la formula:

Picture title

Distribucion de Poisson#

Para que X sea una variable aleatoria con distribucon de Poisson:

  • X representa el numero de veces que ocurre un evento durante un intervalo definido

La probabilidad de que un evento ocurra x veces en un invervalo de tiempo es:

\(P(X=x) = \frac{e^{-\mu}\mu^{x}}{x!}\)

con \( \mu \geq 0 \)

Ejemplo:

Un veterinario recibe en promedio \(\mu\) = 4 pacientes al dia, sabiendo que el numero de pacientes que llegan en un dia sigue una distribucion de poisson, calcula:

  • la probabilidad de que lleguen 3 pacientes en un dia

  • la probabilidad de que llegues 5 pacientes en un dia

Solucion:

  • Probabilidad de 3 pacientes:

\(P(X=3) = \frac{e^{-4}4^{3}}{3!} = 0.1954 = 19.54\)%

  • Probabilidad de 5 pacientes:

\(P(X=5) = \frac{e^{-4}4^{5}}{5!} = 0.1563 = 15.63\)%

Supuestos#

drawing

Distribucion de Poisson Scipy#

Esta libreria permite construir la distribucion dado:

- un valor \(\mu\) Que representa la ocurrencia del evento en el intervalo

Se recrea el ejercicio del veterinario:

  • en promedio llegan 4 perros en un dia, Cual es la posibilidad de que lleguen 3 perros en un dia?

Para obtener la probabilidad de tener 3 ocurrencias uso el atributo .pmf() (probability mass function)

from scipy.stats import poisson

# Se define la distribucion con lambda = 4
dist = poisson(4)

# Probabilidad de 3
dist.pmf(3)
0.1953668148131645

Se recrea otro ejercicio:

  • en promedio llegan 4 perros en un dia, cual es la probabilidad de que lleguen hasta 3 perros en un dia?

Ahora, para obtener la probabilidad de tener 3 eventos o menos, uso el atributo .cdf() (cumulative density function).

  • A esto tambien se le llama Distribucion de probabilidad acumulada

dist.cdf(3)
0.43347012036670896

Recuerda que la probabilidad de hasta 3 es la suma de las probabilidades de 0,1,2,3

dist.pmf(0)+dist.pmf(1)+dist.pmf(2)+dist.pmf(3)
0.43347012036670884

Simulando la Distribucion de Poisson con Numpy#

La funcion poisson de np.random genera experimentos aleatorios con distribucion de Poisson.

  • los parametros son el lambda y el tamano de la muestra

import numpy as np
from numpy.random import poisson   # Random number generator based on the poisson distribution
import matplotlib.pyplot as plt
# Parametros son el lambda y el tamano de la distribucion
def plot_poisson_seaborn(average, sample_size):
    
    poissondist = np.random.poisson(average, sample_size)

    xvalues = np.unique(poissondist, return_counts=True)[0]

    simulacion = np.unique(poissondist, return_counts=True)[1]/len(poissondist)

    teorica = [scipy.stats.poisson(average).pmf(k) for k in xvalues]

    fig, axes = plt.subplots(nrows=1,ncols=2, figsize=(9,6))
    fig.suptitle('Simulacion Dist. Poisson, n={}'.format(sample_size))
    fig.tight_layout()
    axes[0].bar(xvalues, simulacion)
    axes[0].set_title("Resultado Experimental, Lambda = {}".format(average))
    axes[0].set_xticks(xvalues)
    axes[1].bar(xvalues, teorica)
    axes[1].set_title("Resultado Teorico, Lambda = {}".format(average))
    axes[1].set_xticks(xvalues)
    plt.show()
plot_poisson_seaborn(4, 100)
../_images/6dc73c61efa21dddafaf86eda498ecbe41fb2c92f4a4e1cde4463957c31509aa.png
plot_poisson_seaborn(4, 1000)
../_images/89e8e237af2c8af857641cf43341d8ed53c64d62deb0751f420832bf12035b3e.png
plot_poisson_seaborn(4, 1000000)
../_images/0ecbbf6f0891f97d226dc7e78b4d083e326f01dab1160f52fa858e1f48fc57e2.png

Distribucion Geometrica#

Distribucion Hipergeometrica#

Distribucion Binomial negativa#

Distribuciones de probabilidad continua#

Distribucion Normal (Gaussiana)#

drawing
import numpy as np
import matplotlib.pyplot as plt

def gaussian(x, mu, sigma):
    return 1/(sigma*np.sqrt(2*np.pi))*np.exp(-0.5*pow((x-mu)/sigma,2))
# Se define un eje X
x = np.arange(-4,4,0.1)

# Se define una distribucion normal con media=0 y desv=1
y = gaussian(x, 0.0, 1)

# Se grafica
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x7fe95160cca0>]
../_images/1d96dbf6d61209de2108daa691d2e98501151bc565be01c15f1483bf8b2df179.png

Distribucion normal de scipy#

from scipy.stats import norm
import numpy as np
import matplotlib.pyplot as plt
dist_norm = norm(0,1)
x = np.arange(-4,4,0.1)
y = [dist_norm.pdf(value) for value in x]
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x7fe93e071fa0>]
../_images/1d96dbf6d61209de2108daa691d2e98501151bc565be01c15f1483bf8b2df179.png
dist_norm = norm(0,1)
x = np.arange(-4,4,0.1)
y = [dist_norm.cdf(value) for value in x]
plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x7fe93dda29a0>]
../_images/30c6aa0357cf2f18d945b4f6b2421501e019ad9ae29afed5ded13edd03368601.png
import pandas as pd
df = pd.read_excel("s057.xls")

df
Normally Distributed Housefly Wing Lengths Unnamed: 1 Unnamed: 2 Unnamed: 3
0 Sokal, R.R., and P.E.Hunter. 1955. NaN NaN NaN
1 NaN NaN NaN NaN
2 length (x.1mm) NaN NaN NaN
3 36 NaN Bin Frequency
4 37 NaN 36-38 2
... ... ... ... ...
98 52 NaN NaN NaN
99 53 NaN NaN NaN
100 53 NaN NaN NaN
101 54 NaN NaN NaN
102 55 NaN NaN NaN

103 rows × 4 columns

La informacion relevate esta contenida en la columna “Normally Distributed Housefly Wing Lengths”

wingdata = df["Normally Distributed Housefly Wing Lengths"].values[4:]

Ahora, calculemos la probabilidad de cada uno de estos datos

values, dist = np.unique(wingdata, return_counts=True)
plt.bar(values, (dist/len(wingdata)))
<BarContainer object of 19 artists>
../_images/6a0ce8197087709a48b5e417508b32f51d5e8cfaa602d8e407640ed555425e3c.png

Estimacion de la distribucion

mu = wingdata.mean()
sigma = wingdata.std()
x = np.arange(30, 60, 0.1)
dist = norm (mu, sigma)
y = [dist.pdf(value) for value in x]
plt.plot(x, y)
values, dist = np.unique(wingdata, return_counts=True)
plt.bar(values, dist/len(wingdata))
<BarContainer object of 19 artists>
../_images/63bc082308b3ffa4f66b80e3b42bf86818256d1a608b802c272fd01c2feea0dc.png

Distribucion Exponencial#

Distribucion Paretto#

Distribucion Gamma#

Distribucion Beta#

Como estimar una distribucion de probabilidad?#

import numpy as np
import matplotlib.pyplot as plt
from numpy.random import normal
from scipy.stats import norm
sample = normal(size=10000)
plt.hist(sample, bins=30)
plt.show()
../_images/9c8c24844f44ccebfdfac1ecf481d479cd7884d173fb4d4b7db0140494bb927e.png

Estimacion parametrica#

Paso 1. Se genera una muestra aleatoria con distribucion normal

  • Esto se hace con numpy.random.normal

  • En este caso la distribucion tiene media = 50 y desviacion = 5

# Distrib normal con mu = 50, sigma = 5
sample = normal(loc=50, scale=5, size = 1000)
plt.hist(sample, bins=30)
plt.show()
../_images/baf7913689a14447405c670dd0916ae55d0b660b09f7987c41e30e552d0bbeed.png

Entonces supongamos que tenemos un conjunto de datos cuya distribucion y parametros no conocemos 😉😉😉

Paso 2. una vez se tienen los datos simulados, se procede a ajustar una funcion con sus parametros

# calcular parametros de la muestra
mu = sample.mean()
sigma= sample.std()

#generar una distribucion normal con los parametros calculados
dist = norm(mu, sigma)

# Y se obtienen las probabilidades de cada dato
values = np.arange(30,70,1)
probabilidades = [dist.pdf(i) for i in values]

Por ultimo se grafican los datos originales y la distribucion calculada para ver si se ajusta correctamente

# Density = True hace que el grafico muestre probabilidades en lugar de frecuencias
plt.hist(sample, bins = 30, density = True)
plt.plot(values, probabilidades)
plt.show()
../_images/fa0d5d05b0b3fe5edcdb1ab33470c6d8c36f466e97a3e5298a72f1078ef3af53.png

Estimacion no parametrica#

Este proceso se aplica cuando los datos no se ajustan a una distribución conocida, Por lo cual no se pueden forzar a los datos a ajustarse a una unica distribucion

Se genera aleatoriamente un dataset con distribucion bimodal

import numpy as np

sample1 = normal(loc=20, scale=5, size=300)
sample2 = normal(loc=40, scale=5, size=700)
sample = np.hstack((sample1, sample2))
plt.hist(sample, bins = 30, density = True)
plt.show()
../_images/c381ed492185fada695e11b291ea0f4cec2a859f9e38373077a5f83fd473226f.png

Se aplica el metodo de Kernel Density Estimation(KDE)

*una vez se tienen los datos simulados, comienza el proceso de estimación:

  • 1. Se crea un objeto modelo, con parámetros bandwidth= (parámetro de suavizado) y kernel= (funciones de distribución que se usan para la estimación).

  • 2. Una vez creado el objeto, se ajustan los datos a las necesidades del objeto, para esto se utiliza el método reshape, para ordenar en una matriz de 1000 filas y 1 columna y luego se ajusta el modelo a estos datos:

from sklearn.neighbors import KernelDensity

model = KernelDensity(bandwidth=2, kernel="gaussian")
sample = sample.reshape((len(sample),1))
model.fit(sample)
KernelDensity(bandwidth=2)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.

Ahora se ajusta el modelo (o la distribucion que queremos predecir) a los datos, como si se estuviese calculando la media y la desviacion estandar de una distribución normal, pero nos faltan calcular los promedios de cada Kernel para darle forma a la función teórica. Esto es lo que hacemos a continuación:

  • se crea un eje x en el rango obetenido, luego calculamos las probabilidades.

  • Aclaración: la probabilidad sale en forma logarítimica(por cuestiones de eficiencia), Luego a través de función exponencial se revierte este efecto.

values = np.arange(1,60,1)
values = values.reshape((len(values), 1))
probabilities = model.score_samples(values) # probabilidad logaritmica (computacionalmente mas barato)
probabilities = np.exp(probabilities) # se regresa a las probabilidades originales

# Datos originales
plt.hist(sample, bins=50, density=True)
#Curva ajustada
plt.plot(values, probabilities)
plt.show()
../_images/7c4c7f3b40a13cbd55cecf3f135f8e22d03ab296964c9244f1ae691acb2a8b60.png

Maximum Likelihood Estimation (MLE)#

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import pandas as pd
  1. Escoger una distribucion

  2. Escoger los parametros de la distribucion

def likelihood(y, yp):
    return yp*y+(1-yp)*(1-y)
fig = plt.figure()
#ax = fig.gca(projection="3d")
ax = fig.add_subplot(projection='3d')

Y = np.arange(0,1,0.01)
YP = np.arange(0,1,0.01)
Y, YP = np.meshgrid(Y, YP)
Z = likelihood(Y, YP)

surf = ax.plot_surface(Y, YP,Z, cmap = cm.coolwarm)
fig.colorbar(surf, shrink = 0.5, aspect = 5)
plt.show()
../_images/92dc96e5bd39cf11c97534be013c1da7b59e842402bd16fde1db69d9d55d3a3e.png
from sklearn.datasets import load_iris
from sklearn.linear_model import LogisticRegression
atrib_names = ["sepal length", "sepal width", "petal length", "petal width"]
X, y = load_iris(return_X_y=True)
X[:2]
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2]])
y
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
clf = LogisticRegression(random_state=10, solver="liblinear").fit(X[:100], y[:100])

Revisamos los parametros resultantes \(\beta_{1}\)

clf.coef_
array([[-0.40247392, -1.46382925,  2.23785648,  1.00009294]])
Created in deepnote.com Created in Deepnote