02.03 - Aplicación para probar bondad de ajuste
Modelos de Simualción
Introducción
- Crear una aplicación que permita ajustar una distribución teórica a los datos de una muestra y realizar pruebas de bondad de ajuste.
- La aplicación debe permitir cargar los datos de muestra desde un archivo CSV o mediante un cuadro de texto.
- La aplicación debe permitir seleccionar la distribución teórica a ajustar (por ejemplo, normal, exponencial, uniforme, etc.).
Bibliotecas a utilizar
- NumPy: para el manejo de arreglos numéricos y operaciones matemáticas.
- SciPy: para trabajar con distribuciones de probabilidad y pruebas estadísticas.
- Matplotlib: para visualizar gráficamente los datos y resultados.
- Tkinter: para crear la interfaz gráfica.
Interfaz gráfica
- Utilizar Tkinter para crear una ventana con campos de entrada para cargar los datos (por ejemplo, mediante un cuadro de texto o la carga de un archivo CSV).
- Agregar una lista desplegable o radio buttons para seleccionar la distribución teórica a ajustar (por ejemplo, normal, exponencial, uniforme, etc.).
- Incluir un botón para realizar la prueba de bondad de ajuste y mostrar los resultados.
Funciones de generación de variables aleatorias
- Crear funciones que utilicen NumPy y SciPy para generar variables aleatorias según la distribución seleccionada.
Pruebas de bondad de ajuste
- Utilizar las funciones de SciPy para realizar pruebas de bondad de ajuste, como la prueba de Kolmogorov-Smirnov, la prueba de Anderson-Darling, o la prueba de Chi-cuadrado.
- Mostrar los resultados de la prueba en la interfaz gráfica, indicando si se acepta o rechaza la hipótesis nula (es decir, si los datos se ajustan a la distribución teórica seleccionada).
Visualización de resultados
- Utilizar Matplotlib para crear gráficos de los datos y la distribución teórica ajustada.
- Mostrar los gráficos en la interfaz gráfica.
Estructura general del programa
La aplicación tiene tres partes principales:
- Importación de bibliotecas y definición de funciones
- Creación de la interfaz gráfica de usuario (GUI) con Tkinter
- Funciones de ajuste y gráficos
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import ttk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
Definición de funciones
Función plot_distribution
Esta función crea un gráfico de la distribución ajustada junto con un histograma de los datos de muestra. La función plot_distribution toma tres argumentos:
- sample_data: Los datos de muestra
- dist: La distribución ajustada (un objeto de SciPy)
- params: Los parámetros ajustados de la distribución
La función realiza los siguientes pasos:
- Crea una figura y un eje para el gráfico utilizando plt.subplots().
- Genera un histograma de los datos de muestra utilizando el método ax.hist().
- La opción density=True normaliza el histograma de tal manera que el área bajo las barras del histograma sume 1. Esto permite comparar el histograma con la función de densidad de probabilidad (pdf) de la distribución ajustada.
- Crea un conjunto de puntos equiespaciados en el rango de los datos de muestra utilizando np.linspace(). Estos puntos se utilizarán para calcular y dibujar la pdf de la distribución ajustada.
- Calcula la pdf de la distribución ajustada utilizando el método dist.pdf(). Los parámetros ajustados de la distribución se pasan como argumentos utilizando el operador de desempaquetado *params.
- Dibuja la pdf de la distribución ajustada en el gráfico utilizando el método ax.plot().
- Añade una leyenda al gráfico utilizando el método ax.legend().
- Muestra el gráfico en la ventana de Tkinter utilizando FigureCanvasTkAgg.
La función plot_distribution crea un gráfico que compara el histograma de los datos de muestra con la función de densidad de probabilidad de la distribución ajustada. Esto permite visualizar la calidad del ajuste y evaluar si la distribución seleccionada es adecuada para los datos.
def plot_distribution(sample_data, dist, params):
# Crea una figura y un eje para el gráfico
fig, ax = plt.subplots(figsize=(6, 4))
# Genera un histograma a partir de los datos de la muestra
# La opción density=True hace que el histograma esté normalizado
ax.hist(sample_data, bins="auto", alpha=0.6, density=True, label="Datos")
# Genera puntos equiespaciados en el rango de los datos de la muestra
x = np.linspace(min(sample_data), max(sample_data), 1000)
# Calcula la función de densidad de probabilidad (pdf) de la distribución ajustada
pdf = dist.pdf(x, *params)
# Dibuja la pdf de la distribución ajustada en el gráfico
ax.plot(x, pdf, 'r-', lw=2, label=f"{dist.name.capitalize()} ajustada")
# Añade una leyenda al gráfico
ax.legend()
# Muestra el gráfico en la ventana de Tkinter
canvas = FigureCanvasTkAgg(fig, master=frame_plot)
canvas.draw()
canvas.get_tk_widget().pack()
Paso de parámetros
El operador * en Python se utiliza para desempaquetar los elementos de una secuencia (en este caso, una lista o una tupla) y pasarlos como argumentos posicionales a una función. En la línea de código:
pdf = dist.pdf(x, *params)
La variable params es una lista o tupla que contiene los parámetros estimados para la distribución seleccionada. Al usar el operador *
antes de params, se desempaquetan los elementos de params y se pasan como argumentos individuales a la función pdf() de la distribución.
Por ejemplo, supongamos que estamos ajustando una distribución normal a los datos de muestra. La función pdf() de la distribución normal requiere dos argumentos: la media (loc) y la desviación estándar (scale). Si params es una lista que contiene la media y la desviación estándar estimadas, como [mu, sigma], al usar *params
, estamos pasando efectivamente mu y sigma como argumentos individuales a pdf(). Sería equivalente a escribir:
pdf = dist.pdf(x, mu, sigma)
La ventaja de usar *params
es que permite tratar con distribuciones que tienen diferentes números de parámetros de manera genérica, sin tener que adaptar el código específicamente para cada distribución. El desempaquetado de argumentos con el operador *
hace que el código sea más flexible y fácil de mantener.
Función ajustar_distribucion
Esta función ajusta la distribución seleccionada a los datos de muestra y realiza pruebas de bondad de ajuste.
def ajustar_distribucion():
# Convierte los datos de la muestra a un array de NumPy
sample_data = np.array(data_entry.get().split(','), dtype=float)
# Obtiene la distribución seleccionada por el usuario
dist_name = selected_dist.get()
# Mapea el nombre de la distribución seleccionada al nombre aceptado por la función kstest
dist_map = {"normal": "norm", "exponencial": "expon", "uniforme": "uniform"}
ks_dist_name = dist_map[dist_name]
# Obtiene el objeto de la distribución de SciPy correspondiente al nombre seleccionado
if dist_name == "normal":
dist = stats.norm
elif dist_name == "exponencial":
dist = stats.expon
elif dist_name == "uniforme":
dist = stats.uniform
# Ajusta la distribución a los datos de la muestra
params = dist.fit(sample_data)
# Llama a la función plot_distribution para mostrar el gráfico de la distribución ajustada
plot_distribution(sample_data, dist, params)
# Prueba de Kolmogorov-Smirnov
D, ks_p_value = stats.kstest(sample_data, ks_dist_name, args=params)
# Prueba Chi-cuadrado
hist, bin_edges = np.histogram(sample_data, bins="auto", density=True)
bin_width = bin_edges[1] - bin_edges[0]
bin_centers = bin_edges[:-1] + bin_width / 2
expected_values = dist.pdf(bin_centers, *params) * bin_width * len(sample_data)
observed_values = hist * len(sample_data)
# Normaliza las frecuencias observadas y esperadas
observed_values_normalized = observed_values / np.sum(observed_values)
expected_values_normalized = expected_values / np.sum(expected_values)
chi2, chi2_p_value = stats.chisquare(observed_values_normalized, expected_values_normalized)
# Muestra los resultados de las pruebas de bondad de ajuste
ks_result_text.set(f"KS: D = {D:.4f}, p-valor = {ks_p_value:.4f}")
chi2_result_text.set(f"Chi2: Estadístico = {chi2:.4f}, p-valor = {chi2_p_value:.4f}")
# Evalúa si se acepta o rechaza la hipótesis nula para cada prueba
alpha = 0.05
if ks_p_value > alpha:
ks_hypothesis_text.set("Hipótesis nula aceptada")
else:
ks_hypothesis_text.set("Hipótesis nula rechazada")
if chi2_p_value > alpha:
chi2_hypothesis_text.set("Hipótesis nula aceptada")
else:
chi2_hypothesis_text.set("Hipótesis nula rechazada")
Funcion ajustar_distribucion
La función ajustar_distribucion realiza las siguientes acciones:
- Convierte los datos de entrada en una lista de números (sample_data).
- Obtiene el nombre de la distribución seleccionada en la GUI (dist_name).
- Mapea los nombres de las distribuciones de la GUI a los nombres aceptados por la función kstest de SciPy y asigna el resultado a ks_dist_name.
- Selecciona la distribución de SciPy correspondiente al nombre seleccionado en la GUI y la asigna a dist.
- Ajusta los parámetros de la distribución a los datos de muestra utilizando el método dist.fit() y guarda los parámetros ajustados en params.
Llama a la función plot_distribution para graficar los datos y la distribución ajustada.
Realiza la prueba de bondad de ajuste de Kolmogorov-Smirnov utilizando stats.kstest(). Esta prueba compara la función de distribución acumulativa empírica de los datos de muestra con la función de distribución acumulativa teórica de la distribución ajustada.
Guarda el estadístico de prueba ks_d_value y el p-valor en ks_p_value.
Realiza la prueba de bondad de ajuste Chi-cuadrado:
a. Crea un histograma de los datos de muestra utilizando np.histogram(), con la opción density=True para normalizar el histograma.
b. Calcula los valores esperados de la distribución ajustada en los centros de las barras del histograma utilizando dist.pdf(). Multiplica los valores por el ancho de las barras y la cantidad de datos para obtener las frecuencias esperadas.
c. Convierte las frecuencias observadas y esperadas a números enteros.
d. Normaliza las frecuencias observadas y esperadas dividiendo cada una por su suma total.
e. Aplica la prueba Chi-cuadrado utilizando stats.chisquare() y guarda el estadístico de prueba chi2 y el p-valor en chi2_p_value.
Muestra los resultados de las pruebas en la GUI utilizando las variables ks_result y chi2_result.
Evalúa si la hipótesis nula se acepta o se rechaza según los p-valores. Si el p-valor es mayor que el nivel de significancia alpha (por ejemplo, 0.05), se acepta la hipótesis nula, lo que indica que la distribución ajustada es adecuada para los datos. Si el p-valor es menor que alpha, se rechaza la hipótesis nula, lo que indica que la distribución ajustada no es adecuada para los datos. Muestra los resultados en la GUI utilizando las variables ks_result_text y chi2_result_text.
En resumen la función ajustar_distribucion ajusta una distribución teórica a los datos de muestra, grafica los resultados y realiza pruebas de bondad de ajuste para evaluar la calidad del ajuste.
Creación de la interfaz gráfica de usuario (GUI)
Se crea una ventana de Tkinter y se añaden elementos de entrada y salida.
window = tk.Tk()
window.title("Ajuste de distribuciones")
window.geometry("800x600")
# ... (código de la GUI)
window.mainloop()
Pruebas implementadas de bondad de ajuste
Prueba de Kolmogorov-Smirnov
Esta prueba compara la función de distribución acumulativa empírica de los datos de muestra con la función de distribución acumulativa teórica de la distribución ajustada.
D, ks_p_value = stats.kstest(sample_data, ks_dist_name, args=params)
Prueba Chi-cuadrado
Esta prueba compara las frecuencias observadas en un histograma de los datos de muestra con las frecuencias esperadas de la distribución ajustada.
chi2, chi2_p_value = stats.chisquare(observed_values_normalized, expected_values_normalized)
Resultados
La aplicación muestra los resultados de las pruebas de bondad de ajuste y acepta o rechaza la hipótesis nula según los p-valores de las pruebas.