Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -268,35 +268,37 @@ class RSM_BoxBehnken:
|
|
| 268 |
|
| 269 |
def pareto_chart(self, model, title):
|
| 270 |
"""
|
| 271 |
-
Genera un diagrama de Pareto para los efectos
|
| 272 |
incluyendo la línea de significancia.
|
| 273 |
"""
|
| 274 |
-
# Calcular los
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
| 280 |
|
| 281 |
-
# Calcular el valor crítico de
|
| 282 |
alpha = 0.05 # Nivel de significancia
|
| 283 |
-
|
| 284 |
-
|
|
|
|
| 285 |
|
| 286 |
# Crear el diagrama de Pareto
|
| 287 |
fig = px.bar(
|
| 288 |
-
x=
|
| 289 |
y=sorted_names,
|
| 290 |
orientation='h',
|
| 291 |
-
labels={'x': '
|
| 292 |
title=title
|
| 293 |
)
|
| 294 |
fig.update_yaxes(autorange="reversed")
|
| 295 |
|
| 296 |
# Agregar la línea de significancia
|
| 297 |
-
fig.add_vline(x=
|
| 298 |
-
|
| 299 |
-
|
| 300 |
|
| 301 |
return fig
|
| 302 |
|
|
@@ -327,7 +329,7 @@ class RSM_BoxBehnken:
|
|
| 327 |
equation += f" + {coef:.3f}*{self.x3_name}^2"
|
| 328 |
|
| 329 |
return equation
|
| 330 |
-
|
| 331 |
def generate_prediction_table(self):
|
| 332 |
"""
|
| 333 |
Genera una tabla con los valores actuales, predichos y residuales.
|
|
@@ -343,7 +345,7 @@ class RSM_BoxBehnken:
|
|
| 343 |
|
| 344 |
def calculate_contribution_percentage(self):
|
| 345 |
"""
|
| 346 |
-
Calcula el porcentaje de contribución de cada factor
|
| 347 |
"""
|
| 348 |
if self.model_simplified is None:
|
| 349 |
print("Error: Ajusta el modelo simplificado primero.")
|
|
@@ -351,18 +353,21 @@ class RSM_BoxBehnken:
|
|
| 351 |
|
| 352 |
# ANOVA del modelo simplificado
|
| 353 |
anova_table = sm.stats.anova_lm(self.model_simplified, typ=2)
|
| 354 |
-
|
| 355 |
-
#
|
| 356 |
ss_total = anova_table['sum_sq'].sum()
|
| 357 |
-
|
| 358 |
# Crear tabla de contribución
|
| 359 |
contribution_table = pd.DataFrame({
|
| 360 |
'Factor': [],
|
| 361 |
'Suma de Cuadrados': [],
|
|
|
|
| 362 |
'% Contribución': []
|
| 363 |
})
|
| 364 |
|
| 365 |
-
# Calcular porcentaje de contribución para cada factor
|
|
|
|
|
|
|
| 366 |
for index, row in anova_table.iterrows():
|
| 367 |
if index != 'Residual':
|
| 368 |
factor_name = index
|
|
@@ -374,11 +379,13 @@ class RSM_BoxBehnken:
|
|
| 374 |
factor_name = f'{self.x3_name}^2'
|
| 375 |
|
| 376 |
ss_factor = row['sum_sq']
|
|
|
|
| 377 |
contribution_percentage = (ss_factor / ss_total) * 100
|
| 378 |
|
| 379 |
contribution_table = pd.concat([contribution_table, pd.DataFrame({
|
| 380 |
'Factor': [factor_name],
|
| 381 |
'Suma de Cuadrados': [ss_factor],
|
|
|
|
| 382 |
'% Contribución': [contribution_percentage]
|
| 383 |
})], ignore_index=True)
|
| 384 |
|
|
@@ -395,10 +402,10 @@ class RSM_BoxBehnken:
|
|
| 395 |
# --- ANOVA detallada ---
|
| 396 |
# 1. Ajustar un modelo solo con los términos de primer orden y cuadráticos
|
| 397 |
formula_reduced = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
|
| 398 |
-
|
| 399 |
model_reduced = smf.ols(formula_reduced, data=self.data).fit()
|
| 400 |
|
| 401 |
-
# 2. ANOVA del modelo reducido
|
| 402 |
anova_reduced = sm.stats.anova_lm(model_reduced, typ=2)
|
| 403 |
|
| 404 |
# 3. Suma de cuadrados total
|
|
@@ -436,7 +443,10 @@ class RSM_BoxBehnken:
|
|
| 436 |
ms_lack_of_fit = ss_lack_of_fit / df_lack_of_fit if not np.isnan(ss_lack_of_fit) else np.nan
|
| 437 |
ms_pure_error = ss_pure_error / df_pure_error if not np.isnan(ss_pure_error) else np.nan
|
| 438 |
|
| 439 |
-
# 11.
|
|
|
|
|
|
|
|
|
|
| 440 |
f_lack_of_fit = ms_lack_of_fit / ms_pure_error if not np.isnan(ms_lack_of_fit) else np.nan
|
| 441 |
p_lack_of_fit = 1 - f.cdf(f_lack_of_fit, df_lack_of_fit, df_pure_error) if not np.isnan(f_lack_of_fit) else np.nan
|
| 442 |
|
|
@@ -446,22 +456,31 @@ class RSM_BoxBehnken:
|
|
| 446 |
'Suma de Cuadrados': [ss_regression, ss_residual, ss_lack_of_fit, ss_pure_error, ss_total],
|
| 447 |
'Grados de Libertad': [df_regression, df_residual, df_lack_of_fit, df_pure_error, df_total],
|
| 448 |
'Cuadrado Medio': [ms_regression, ms_residual, ms_lack_of_fit, ms_pure_error, np.nan],
|
| 449 |
-
'F': [
|
| 450 |
-
'Valor p': [
|
| 451 |
})
|
| 452 |
|
| 453 |
-
# Calcular la suma de cuadrados y
|
| 454 |
-
ss_curvature = anova_reduced['sum_sq'][f'I({self.x1_name} ** 2)'] +
|
|
|
|
|
|
|
| 455 |
df_curvature = 3
|
|
|
|
|
|
|
|
|
|
| 456 |
|
| 457 |
# Añadir la fila de curvatura a la tabla ANOVA
|
| 458 |
-
detailed_anova_table.loc[len(detailed_anova_table)] = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
|
| 460 |
-
# Reorganizar las filas
|
| 461 |
-
detailed_anova_table = detailed_anova_table.reindex([0, 5, 1, 2, 3, 4])
|
| 462 |
-
|
| 463 |
-
# Resetear el índice para que sea consecutivo
|
| 464 |
-
detailed_anova_table = detailed_anova_table.reset_index(drop=True)
|
| 465 |
|
| 466 |
return detailed_anova_table.round(3)
|
| 467 |
|
|
@@ -918,4 +937,4 @@ def main():
|
|
| 918 |
interface.launch(share=True)
|
| 919 |
|
| 920 |
if __name__ == "__main__":
|
| 921 |
-
main()
|
|
|
|
| 268 |
|
| 269 |
def pareto_chart(self, model, title):
|
| 270 |
"""
|
| 271 |
+
Genera un diagrama de Pareto para los efectos usando estadísticos F,
|
| 272 |
incluyendo la línea de significancia.
|
| 273 |
"""
|
| 274 |
+
# Calcular los estadísticos F para cada término
|
| 275 |
+
# F = (coef/std_err)^2 = t^2
|
| 276 |
+
fvalues = model.tvalues[1:]**2 # Excluir la Intercept y convertir t a F
|
| 277 |
+
abs_fvalues = np.abs(fvalues)
|
| 278 |
+
sorted_idx = np.argsort(abs_fvalues)[::-1]
|
| 279 |
+
sorted_fvalues = abs_fvalues[sorted_idx]
|
| 280 |
+
sorted_names = fvalues.index[sorted_idx]
|
| 281 |
|
| 282 |
+
# Calcular el valor crítico de F para la línea de significancia
|
| 283 |
alpha = 0.05 # Nivel de significancia
|
| 284 |
+
dof_num = 1 # Grados de libertad del numerador (cada término)
|
| 285 |
+
dof_den = model.df_resid # Grados de libertad residuales
|
| 286 |
+
f_critical = f.ppf(1 - alpha, dof_num, dof_den)
|
| 287 |
|
| 288 |
# Crear el diagrama de Pareto
|
| 289 |
fig = px.bar(
|
| 290 |
+
x=sorted_fvalues.round(3),
|
| 291 |
y=sorted_names,
|
| 292 |
orientation='h',
|
| 293 |
+
labels={'x': 'Estadístico F', 'y': 'Término'},
|
| 294 |
title=title
|
| 295 |
)
|
| 296 |
fig.update_yaxes(autorange="reversed")
|
| 297 |
|
| 298 |
# Agregar la línea de significancia
|
| 299 |
+
fig.add_vline(x=f_critical, line_dash="dot",
|
| 300 |
+
annotation_text=f"F crítico = {f_critical:.3f}",
|
| 301 |
+
annotation_position="bottom right")
|
| 302 |
|
| 303 |
return fig
|
| 304 |
|
|
|
|
| 329 |
equation += f" + {coef:.3f}*{self.x3_name}^2"
|
| 330 |
|
| 331 |
return equation
|
| 332 |
+
|
| 333 |
def generate_prediction_table(self):
|
| 334 |
"""
|
| 335 |
Genera una tabla con los valores actuales, predichos y residuales.
|
|
|
|
| 345 |
|
| 346 |
def calculate_contribution_percentage(self):
|
| 347 |
"""
|
| 348 |
+
Calcula el porcentaje de contribución de cada factor usando estadísticos F.
|
| 349 |
"""
|
| 350 |
if self.model_simplified is None:
|
| 351 |
print("Error: Ajusta el modelo simplificado primero.")
|
|
|
|
| 353 |
|
| 354 |
# ANOVA del modelo simplificado
|
| 355 |
anova_table = sm.stats.anova_lm(self.model_simplified, typ=2)
|
| 356 |
+
|
| 357 |
+
# Calcular las sumas de cuadrados ajustadas
|
| 358 |
ss_total = anova_table['sum_sq'].sum()
|
| 359 |
+
|
| 360 |
# Crear tabla de contribución
|
| 361 |
contribution_table = pd.DataFrame({
|
| 362 |
'Factor': [],
|
| 363 |
'Suma de Cuadrados': [],
|
| 364 |
+
'Estadístico F': [],
|
| 365 |
'% Contribución': []
|
| 366 |
})
|
| 367 |
|
| 368 |
+
# Calcular estadísticos F y porcentaje de contribución para cada factor
|
| 369 |
+
ms_error = anova_table.loc['Residual', 'sum_sq'] / anova_table.loc['Residual', 'df']
|
| 370 |
+
|
| 371 |
for index, row in anova_table.iterrows():
|
| 372 |
if index != 'Residual':
|
| 373 |
factor_name = index
|
|
|
|
| 379 |
factor_name = f'{self.x3_name}^2'
|
| 380 |
|
| 381 |
ss_factor = row['sum_sq']
|
| 382 |
+
f_stat = (ss_factor / row['df']) / ms_error
|
| 383 |
contribution_percentage = (ss_factor / ss_total) * 100
|
| 384 |
|
| 385 |
contribution_table = pd.concat([contribution_table, pd.DataFrame({
|
| 386 |
'Factor': [factor_name],
|
| 387 |
'Suma de Cuadrados': [ss_factor],
|
| 388 |
+
'Estadístico F': [f_stat],
|
| 389 |
'% Contribución': [contribution_percentage]
|
| 390 |
})], ignore_index=True)
|
| 391 |
|
|
|
|
| 402 |
# --- ANOVA detallada ---
|
| 403 |
# 1. Ajustar un modelo solo con los términos de primer orden y cuadráticos
|
| 404 |
formula_reduced = f'{self.y_name} ~ {self.x1_name} + {self.x2_name} + {self.x3_name} + ' \
|
| 405 |
+
f'I({self.x1_name}**2) + I({self.x2_name}**2) + I({self.x3_name}**2)'
|
| 406 |
model_reduced = smf.ols(formula_reduced, data=self.data).fit()
|
| 407 |
|
| 408 |
+
# 2. ANOVA del modelo reducido
|
| 409 |
anova_reduced = sm.stats.anova_lm(model_reduced, typ=2)
|
| 410 |
|
| 411 |
# 3. Suma de cuadrados total
|
|
|
|
| 443 |
ms_lack_of_fit = ss_lack_of_fit / df_lack_of_fit if not np.isnan(ss_lack_of_fit) else np.nan
|
| 444 |
ms_pure_error = ss_pure_error / df_pure_error if not np.isnan(ss_pure_error) else np.nan
|
| 445 |
|
| 446 |
+
# 11. Estadísticos F y valores p
|
| 447 |
+
f_regression = ms_regression / ms_residual
|
| 448 |
+
p_regression = 1 - f.cdf(f_regression, df_regression, df_residual)
|
| 449 |
+
|
| 450 |
f_lack_of_fit = ms_lack_of_fit / ms_pure_error if not np.isnan(ms_lack_of_fit) else np.nan
|
| 451 |
p_lack_of_fit = 1 - f.cdf(f_lack_of_fit, df_lack_of_fit, df_pure_error) if not np.isnan(f_lack_of_fit) else np.nan
|
| 452 |
|
|
|
|
| 456 |
'Suma de Cuadrados': [ss_regression, ss_residual, ss_lack_of_fit, ss_pure_error, ss_total],
|
| 457 |
'Grados de Libertad': [df_regression, df_residual, df_lack_of_fit, df_pure_error, df_total],
|
| 458 |
'Cuadrado Medio': [ms_regression, ms_residual, ms_lack_of_fit, ms_pure_error, np.nan],
|
| 459 |
+
'F': [f_regression, np.nan, f_lack_of_fit, np.nan, np.nan],
|
| 460 |
+
'Valor p': [p_regression, np.nan, p_lack_of_fit, np.nan, np.nan]
|
| 461 |
})
|
| 462 |
|
| 463 |
+
# Calcular la suma de cuadrados y estadísticos F para la curvatura
|
| 464 |
+
ss_curvature = anova_reduced['sum_sq'][f'I({self.x1_name} ** 2)'] + \
|
| 465 |
+
anova_reduced['sum_sq'][f'I({self.x2_name} ** 2)'] + \
|
| 466 |
+
anova_reduced['sum_sq'][f'I({self.x3_name} ** 2)']
|
| 467 |
df_curvature = 3
|
| 468 |
+
ms_curvature = ss_curvature / df_curvature
|
| 469 |
+
f_curvature = ms_curvature / ms_residual
|
| 470 |
+
p_curvature = 1 - f.cdf(f_curvature, df_curvature, df_residual)
|
| 471 |
|
| 472 |
# Añadir la fila de curvatura a la tabla ANOVA
|
| 473 |
+
detailed_anova_table.loc[len(detailed_anova_table)] = [
|
| 474 |
+
'Curvatura',
|
| 475 |
+
ss_curvature,
|
| 476 |
+
df_curvature,
|
| 477 |
+
ms_curvature,
|
| 478 |
+
f_curvature,
|
| 479 |
+
p_curvature
|
| 480 |
+
]
|
| 481 |
|
| 482 |
+
# Reorganizar las filas y resetear el índice
|
| 483 |
+
detailed_anova_table = detailed_anova_table.reindex([0, 5, 1, 2, 3, 4]).reset_index(drop=True)
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
return detailed_anova_table.round(3)
|
| 486 |
|
|
|
|
| 937 |
interface.launch(share=True)
|
| 938 |
|
| 939 |
if __name__ == "__main__":
|
| 940 |
+
main()
|