%matplotlib inline
import random
import numpy as np
import scipy as sc
import scipy.stats as st
import seaborn
import matplotlib.pyplot as plt
Una distribución de probabilidad es una descripción precisa de las probabilidades asociadas a los resultados de un experimento aleatorio.
Muchos sucesos aleatorios son bien conocidos y siguen una distribución de probabilidad que podemos encontrar en la wikipedia o en los libros de texto de probabilidad. Se les suele dar un nombre especial: (binomial, geométrica, gaussiana...).
Las distribuciones de probabilidad se clasifican según los valores que pueden tomar:
Es importante entender esta distinción para poder manejarlas, incluso a nivel intuitivo.
Si simulamos un experimento en base a una distribución de probabilidad, decimos que hemos hecho una extracción aleatoria de esa distribución.
Si simulamos un experimento N veces de forma independiente, el resultado se llama una muestra aleatoria independiente de tamaño N.
En esta clase vamos a centrarnos en distribuciones de probabilidad discretas, y lo primero que vamos a hacer es intentar entender la relación entre la ley de distribución de probabilidad, y las frecuencias con las que nos encontramos cada posible resultado.
Un experimento aleatorio sigue una distribución de Bernoulli si hay dos posibles resultados:
Cualquier experimento aleatorio con dos resultados se puede representar mediante una distribución de Bernouilli asignando a uno de los dos posibles resultados el valor 0 y al otro, el valor 1:
#La distribución depende de un parametro
p0 = 0.25
#El objeto B contiene los metodos asociados a una distribucion concreta de la familia binomial
Ber = st.bernoulli(p=p0)
Ber
La función de masa de una distribución de probabilidad indica la probabilidad de obtener cada posible resultado.
Ber.pmf(0)
es la probabilidad de obtener 0
y Ber.pmf(1)
es la probabilidad de obtener 1
.
#Usando Ber.pmf, podemos ver cuál es la probabilidad de
#obtener 0, o de obtener 1
print(Ber.pmf(0), Ber.pmf(1))
Una extracción aleatoria de la distribución nos devuelve un número aleatorio en el espacio muestral de la distribución, pero de tal forma que cuando hacemos muchas extracciones cada resultado aparece una proporción de veces parecida a la probabilidad que le asigna la funcion de masa.
Ber.rvs(N)
devuelve un array con N
extracciones aleatorias independientes de la distribución Ber
.
Ber.rvs(20)
# ATENCION: no hay que fijar la semilla aleatoria con random.seed,
#sino con np.random.seed(semilla)
random.seed(12)
#np.random.seed(12)
Ber.rvs(20)
#Usando Ber.pmf, podemos ver cuál es la probabilidad de
#obtener 0, o de obtener 1
plt.bar([0,1],[Ber.pmf(0), Ber.pmf(1)], fill=False)
plt.show()
Un Histograma es una representación gráfica de una serie de datos en forma de barras, donde la superficie de cada barra es proporcional a la frecuencia de los valores representados.
Comparamos la distribución teórica con el histograma de una muestra aleatoria de distintos tamaños.
Si extraemos una muestra pequeña, el histograma de la muestra no se parece demasiado a la distribución de probabilidad que hemos usado para obtener la muestra.
#Ejecutalo varias veces y veras como la forma cambia
#A veces se parece más, otras veces menos
plt.hist(Ber.rvs(10), bins=[-0.5,0.5,1.5], normed=1,alpha=0.8)
plt.bar([0,1],[Ber.pmf(0), Ber.pmf(1)],fill=False)
plt.show()
Pero si aumentamos el tamaño de la muestra aleatoria se parece mucho mas a la función de masa.
#Pero si aumentamos el tamanyo se parece mucho mas
plt.hist(Ber.rvs(1000), bins=[-0.5,0.5,1.5], normed=1,alpha=0.8)
plt.bar([0,1],[Ber.pmf(0), Ber.pmf(1)],fill=False)
plt.show()
Tenemos un experimento de Bernoulli con probabilidad p:
Repetimos el experimento n veces de forma independiente, y anotamos cuántas veces hemos ganado.
El espacio muestral son los números naturales entre 0 y n.
Depende de dos parámetros:
Puedes encontrar más información sobre la distribución binomial en la wikipedia
#La distribución depende de dos parametros
p0 = 0.2
n0 = 10
#El objeto B contiene los metodos asociados a una distribucion concreta de la familia binomial
B = st.binom(n=n0, p=p0)
B
La función de masa de la distribución binomial indica la probabilidad de ganar 0, 1, 2 ... hasta el máximo de n veces.
Si B
es la distribución, B.pmf(k)
es la probabilidad del valor k
.
#Usando B.pmf, podemos ver cuál es la probabilidad de ganar 0, 1, 2 ... hasta n0 veces
plt.bar(range(n0+1),[B.pmf(k) for k in range(n0+1)], fill=False)
plt.show()
B.rvs(n)
devuelve un array con n extracciones aleatorias independientes de la distribución B
.
B.rvs(20)
Comparemos el histograma de una muestra aleatoria de tamaño pequeño con las probabilidades de la función de masa...
#Ejecutalo varias veces y veras como la forma cambia
plt.hist(B.rvs(20), bins=[k+0.5 for k in range(-1,n0+1)], normed=1,alpha=0.8)
plt.bar(range(n0+1),[B.pmf(k) for k in range(n0+1)],fill=False)
plt.show()
Si aumentamos el tamaño de la muestra aleatoria se parece mucho mas a la función de masa.
#Pero si aumentamos el tamanyo se parece mucho mas
plt.hist(B.rvs(1000), bins=[k+0.5 for k in range(-1,n0+1)], normed=1,alpha=0.8)
plt.bar(range(n0+1),[B.pmf(k) for k in range(n0+1)],fill=False)
plt.show()
Vamos a dibujar en una misma gráfica los histogramas que corresponden a muestras de tamaños distintos.
def plot_samples_discrete(samples, rango, pmf):
N = len(samples)
samples_sizes = [k for k,_ in samples]
fig, axes = plt.subplots(nrows=1, ncols=N,figsize=(4*N, 6))
axs = axes.flatten()
for j,ax,(k,s) in zip(range(N),axs, samples):
ax.hist(
s,
bins=[k+0.5 for k in range(-1,rango+1)],
normed=1,
histtype='bar',
color=(0,j/N,1-j/N)
)
ax.bar(range(rango),[pmf(k) for k in range(rango)],
fill=False)
ax.set_title(u'Tamaño de la muestra: %.1e'%k)
sizes = [10**j for j in range(1,6)]
samples = [(size, B.rvs(size)) for size in sizes]
plot_samples_discrete(samples, n0+1, B.pmf)
En este caso sabemos generar nosotros la muestra mediante una simulación, usando únicamente el generador de números aleatorios de la CPU (random.random).
Primero escribimos una función que corresponde a una jugada:
random.random()
.Después simulamos el resultado de jugar n veces de forma independiente y sumando los resultados.
def jugada(p):
t = random.random()
return t < p
def extraccion_aleatoria_binomial(p, n):
resultados = [jugada(p) for j in range(n)]
return sum(resultados)
def muestra_aleatoria_binomial(p, n, K):
return [
extraccion_aleatoria_binomial(p, n)
for _ in range(K)]
muestra_aleatoria_binomial(p0, 10, 20)
#Repetimos la grafica anterior usando nuestra propia funcion
sizes = [10**j for j in range(1,6)]
samples = [(size, muestra_aleatoria_binomial(p0, n0, size)) for size in sizes]
plot_samples_discrete(samples, n0+1, B.pmf)
Repetimos un experimento aleatorio de Bernoulli que ofrece dos resultados:
tantas veces como sea necesario, de forma independiente, hasta obtener "1" por primera vez, y anotamos cuántas veces hemos realizado el experimento (contando la última vez, que hemos obtenido "1").
Los posibles valores son los números positivos mayores que 1 => discreta infinita.
Depende de un uńico parámetro:
Ejemplos:
p0 = 0.1
G = st.geom(p=p0)
G
#Muestras aleatorias
G.rvs(10)
Podemos comparar el histograma de una muestra aleatoria con la función de masa que dice la probabilidad de que tengamos que repetir 1,2,3 ... veces.
Pero para poder hacer el gráfico tenemos que elegir una cota K: consideramos que es casi imposible que obtengamos un número mayor.
SAMPLE_SIZE = 20
K = 50
plt.figure(figsize=(16,12))
plt.hist(G.rvs(SAMPLE_SIZE), normed=1, bins=[k+0.5 for k in range(-1,K+1)])
plt.bar(range(K),[G.pmf(k) for k in range(K)],fill=False)
plt.show()
De nuevo si la muestra es lo bastante grande el histograma se parece mucho a la función de masa.
sizes = [10**j for j in range(1,6)]
samples = [(size, G.rvs(size)) for size in sizes]
plot_samples_discrete(samples, int(5/p0), G.pmf)
También podemos simular este experimento, pero con un bucle while: repetimos hasta que un número aleatorio entre 0 y 1 sea menor que p (lo que ocurre una proporción p de las veces), y contamos el número total de extracciones.
def extraccion_aleatoria_geometrica(p):
k=1
while random.random() > p:
k += 1
return k
def geometrica_a_mano(p, K):
return [
extraccion_aleatoria_geometrica(p)
for _ in range(K)]
geometrica_a_mano(p0, 10)
SAMPLE_SIZE = 1000
K = 50
plt.figure(figsize=(16,12))
plt.hist(geometrica_a_mano(0.1, SAMPLE_SIZE), normed=1, bins=[k+0.5 for k in range(-1,K+1)])
plt.bar(range(K),[G.pmf(k) for k in range(K)],fill=False)
plt.show()