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