Migue1804 commited on
Commit
1fb0787
·
verified ·
1 Parent(s): a567a61

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +438 -438
app.py CHANGED
@@ -1,439 +1,439 @@
1
- import streamlit as st
2
- import pandas as pd
3
- import numpy as np
4
- import plotly.graph_objects as go
5
- from scipy.optimize import minimize, differential_evolution
6
- from sklearn.preprocessing import PolynomialFeatures
7
- from sklearn.linear_model import LinearRegression
8
- from sklearn.metrics import mean_squared_error, r2_score
9
- import matplotlib.pyplot as plt
10
- import seaborn as sns
11
- import io
12
- # Configuración inicial
13
- # Configuración de la aplicación
14
- st.set_page_config(page_title="Optimización Avanzada", page_icon="📊",layout="wide")
15
- # Display the image above the title
16
- st.image('cannabis.jpg')
17
- st.title("Optimización Avanzada con Diseño Experimental Box-Behnken")
18
- st.write("Aplicación con regresión cuadrática y estrategias de optimización mejoradas.")
19
-
20
- # Crear las pestañas
21
- tabs = st.selectbox("Selecciona una opción", ["Fundamento Teórico", "Aplicación Interactiva"])
22
-
23
- if tabs == "Fundamento Teórico":
24
- st.header("Fundamento Teórico")
25
-
26
- # Imagen del proceso
27
- st.subheader("Proceso de Extracción de CBD")
28
- st.image("CBD extraction process.png", caption="Proceso de Extracción de CBD con CO2 Supercrítico", use_column_width=True)
29
- st.write("""
30
- El proceso de extracción de CBD utiliza tecnología de CO2 supercrítico debido a su eficiencia y capacidad para producir extractos puros. Este método incluye etapas clave como molienda, extracción, separación y refinamiento, garantizando un producto de alta calidad para aplicaciones medicinales y comerciales.
31
- """)
32
-
33
- # Diseño Experimental Box-Behnken
34
- st.subheader("Diseño Experimental Box-Behnken")
35
- st.write("""
36
- El diseño experimental **Box-Behnken** se utiliza para modelar y optimizar procesos complejos. En este caso, se aplica para maximizar el rendimiento de CBD considerando variables clave como **Temperatura**, **Presión**, **Flujo de CO2** y **Tiempo**.
37
- Este enfoque reduce significativamente la cantidad de experimentos necesarios, permitiendo explorar interacciones no lineales de manera eficiente.
38
- """)
39
-
40
- # Proceso de Producción de CBD
41
- st.subheader("Proceso de Producción de CBD")
42
- st.write("""
43
- Según el documento *Optimization of Supercritical Carbon Dioxide Fluid Extraction of Medicinal Cannabis from Quebec*, el proceso de extracción con CO2 supercrítico es preferido por su alta selectividad y pureza. Las principales etapas incluyen:
44
- 1. **Preparación de la materia prima**: Molienda y acondicionamiento.
45
- 2. **Extracción supercrítica**:
46
- - El CO2 actúa como solvente bajo condiciones controladas de presión y temperatura.
47
- - Variables clave: presión (150-320 bar), temperatura (40-70°C), flujo de CO2 (5-15 g/min), tiempo (2-4 horas).
48
- 3. **Separación y recolección**: El CO2 se despresuriza para liberar los cannabinoides extraídos.
49
- 4. **Refinamiento posterior**: Remoción de ceras y otros compuestos no deseados.
50
- """)
51
-
52
- # Caso de Negocio
53
- st.subheader("Caso de Negocio: Optimización del Rendimiento")
54
- st.write("""
55
- Optimizar el rendimiento del proceso de extracción permite:
56
- - Maximizar la cantidad de CBD extraído por lote.
57
- - Reducir costos operativos (energía, solventes, tiempo).
58
- - Mejorar la calidad del producto final.
59
-
60
- Este enfoque es crucial en la industria del cannabis medicinal, donde la eficiencia del proceso impacta directamente en la rentabilidad y sostenibilidad del negocio.
61
- """)
62
-
63
- # Método Basado en CRISP-DM
64
- st.subheader("Metodología Basada en CRISP-DM")
65
- st.write("""
66
- La metodología **CRISP-DM** estructura el desarrollo del modelo en seis etapas:
67
- 1. **Comprensión del Negocio**: Definir objetivos y restricciones del proceso.
68
- 2. **Comprensión de los Datos**: Analizar datos experimentales y evaluar su calidad.
69
- 3. **Preparación de los Datos**: Limpiar y transformar datos para el modelado.
70
- 4. **Modelado**: Ajustar un modelo de regresión cuadrática para capturar relaciones no lineales.
71
- 5. **Evaluación**: Validar el modelo y analizar su desempeño.
72
- 6. **Despliegue**: Implementar el modelo en una aplicación interactiva para optimización en tiempo real.
73
- """)
74
-
75
- # Optimización
76
- st.subheader("Optimización")
77
- st.write("""
78
- Se emplean técnicas avanzadas para maximizar el rendimiento:
79
- - **L-BFGS-B**: Método de optimización local.
80
- - **Evolución Diferencial**: Optimización global para evitar óptimos locales.
81
- - **Múltiples inicios aleatorios**: Combina estrategias locales y globales para robustez.
82
- """)
83
-
84
- # Referencias
85
- st.subheader("Referencias")
86
- st.write("""
87
- - [Optimization of Supercritical Carbon Dioxide Fluid Extraction of Medicinal Cannabis from Quebec](https://www.mdpi.com/2227-9717/11/7/1953).
88
- - Herrero, M., Cifuentes, A., & Ibañez, E. (2006). Supercritical fluid extraction: Recent advances and applications. *Journal of Chromatography A*, 1131(1), 1–24.
89
- - Turner, C., Mathiasson, L., & Lewis, G. (2001). Supercritical fluid extraction and chromatography. *Journal of Biochemical Analysis*, 121(3), 35–58.
90
- """)
91
-
92
- elif tabs == "Aplicación Interactiva":
93
- st.header("Aplicación Interactiva")
94
- st.write("A continuación, puedes cargar tus datos, realizar predicciones y optimizar el rendimiento del proceso de extracción de cannabinoides.")
95
-
96
- # Opciones de selección de datos de ejemplo
97
- st.subheader("Datos de Ejemplo")
98
- usar_datos_exp = st.checkbox("Usar datos de ejemplo: datos_exp.csv")
99
- usar_datos_process = st.checkbox("Usar datos de ejemplo: datos_process.csv")
100
-
101
- # Inicializar variable de datos
102
- data = None
103
-
104
- # Verificar qué checkbox está seleccionado y cargar el archivo correspondiente
105
- if usar_datos_exp and not usar_datos_process:
106
- data = pd.read_csv("data_exp.csv")
107
- st.success("Datos de ejemplo (datos_exp.csv) cargados exitosamente.")
108
- st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
109
- elif usar_datos_process and not usar_datos_exp:
110
- data = pd.read_csv("data_process.csv")
111
- st.success("Datos de ejemplo (datos_process.csv) cargados exitosamente.")
112
- st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
113
- elif usar_datos_exp and usar_datos_process:
114
- st.error("Por favor, selecciona solo un conjunto de datos de ejemplo a la vez.")
115
-
116
- # Opción para cargar datos personalizados
117
- st.subheader("Carga tus Datos")
118
- uploaded_file = st.file_uploader("Carga un archivo CSV con los datos experimentales:", type="csv")
119
-
120
- if uploaded_file is not None:
121
- # Leer datos cargados
122
- data = pd.read_csv(uploaded_file)
123
- st.success("Datos cargados exitosamente desde el archivo proporcionado.")
124
- st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
125
-
126
- # Validación para asegurarse de que se cargaron datos
127
- if data is None:
128
- st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
129
- else:
130
- st.write("### Datos listos para su análisis.")
131
-
132
- # Definir el orden fijo de variables
133
- variable_columns = ['Temperatura', 'Presión', 'Flujo_CO2', 'Tiempo']
134
-
135
- # Extraer variables independientes y dependiente en el orden correcto
136
- X = data[variable_columns]
137
- y = data['Rendimiento']
138
-
139
- # Generar términos cuadráticos (regresión polinómica de segundo grado)
140
- poly = PolynomialFeatures(degree=2, include_bias=False)
141
- X_poly = poly.fit_transform(X)
142
- columnas_poly = poly.get_feature_names_out(X.columns)
143
-
144
- # Ajustar modelo cuadrático
145
- modelo = LinearRegression()
146
- modelo.fit(X_poly, y)
147
-
148
- # Predicciones del modelo
149
- y_pred = modelo.predict(X_poly)
150
-
151
- # Evaluación del modelo
152
- st.subheader("Evaluación del Modelo Cuadrático")
153
- st.write(f"**Error Cuadrático Medio (MSE):** {mean_squared_error(y, y_pred):.4f}")
154
- st.write(f"**R² (Coeficiente de Determinación):** {r2_score(y, y_pred):.4f}")
155
-
156
- # Resumen del modelo: coeficientes
157
-
158
- # Visualización paralela usando columnas
159
- col1, col2 = st.columns(2)
160
-
161
- # En la columna 1, mostrar el DataFrame con términos y coeficientes, adaptado al ancho de la columna
162
- with col1:
163
- st.markdown("#### Términos y Coeficientes del Modelo")
164
-
165
- # Crear un DataFrame con los coeficientes
166
- coeficientes = pd.DataFrame({
167
- 'Término': columnas_poly,
168
- 'Coeficiente': modelo.coef_
169
- })
170
-
171
- coeficientes = coeficientes.sort_values(by='Coeficiente', ascending=False)
172
-
173
- # Función para aplicar colores a los coeficientes
174
- def color_coef(val):
175
- color = 'red' if val > 0 else 'blue' # Los coeficientes positivos serán rojos, negativos azules
176
- return f'background-color: {color}; color: white;'
177
-
178
- # Aplicar estilo a la columna 'Coeficiente' para colorear los valores
179
- styled_coef = coeficientes.style.applymap(color_coef, subset=['Coeficiente'])
180
-
181
- # Mostrar el DataFrame estilizado y ajustado al ancho de la columna
182
- st.dataframe(styled_coef, use_container_width=True)
183
-
184
- # En la columna 2, mostrar el gráfico de importancia de las variables, adaptado al ancho de la columna
185
- with col2:
186
- st.markdown("#### Importancia de las Variables (Feature Importance)")
187
-
188
- # Calcular la importancia de las variables (valor absoluto de los coeficientes)
189
- coef_abs = np.abs(modelo.coef_) # Valor absoluto de los coeficientes
190
- feature_importance = pd.DataFrame({
191
- 'Variable': columnas_poly,
192
- 'Importancia': coef_abs
193
- }).sort_values(by='Importancia', ascending=False) # De mayor a menor
194
-
195
- # Gráfico de barras horizontal con el eje Y invertido
196
- fig_importance = go.Figure(go.Bar(
197
- y=feature_importance['Variable'],
198
- x=feature_importance['Importancia'],
199
- orientation='h',
200
- marker=dict(color='teal')
201
- ))
202
- fig_importance.update_layout(
203
- title="Importancia de las Variables en el Modelo Cuadrático",
204
- xaxis_title="Importancia",
205
- yaxis_title="Variables",
206
- yaxis=dict(autorange="reversed"), # Invertir el eje Y
207
- margin=dict(l=0, r=0, t=30, b=30) # Ajustar márgenes
208
- )
209
- st.plotly_chart(fig_importance, use_container_width=True)
210
-
211
- # Superficies de respuesta dinámicas
212
- st.subheader("Superficies de Respuesta")
213
-
214
- # Selector de variables
215
- eje_x = st.selectbox("Selecciona la variable para el eje X:", variable_columns, index=0)
216
- eje_z = st.selectbox("Selecciona la variable para el eje Z:", variable_columns, index=1)
217
-
218
- # Rango para generar puntos
219
- x_range = np.linspace(X[eje_x].min(), X[eje_x].max(), 50)
220
- z_range = np.linspace(X[eje_z].min(), X[eje_z].max(), 50)
221
- X_grid, Z_grid = np.meshgrid(x_range, z_range)
222
-
223
- # Preparar valores para las otras dos variables
224
- otras_variables = [col for col in variable_columns if col not in [eje_x, eje_z]]
225
-
226
- # Crear grilla de predicción con orden de columnas fijo
227
- grid_data = []
228
- for x_val, z_val in zip(X_grid.ravel(), Z_grid.ravel()):
229
- # Crear un diccionario con todas las variables en el orden correcto
230
- row_data = dict(zip(variable_columns, [
231
- x_val if eje_x == 'Temperatura' else X['Temperatura'].mean(),
232
- x_val if eje_x == 'Presión' else (z_val if eje_z == 'Presión' else X['Presión'].mean()),
233
- x_val if eje_x == 'Flujo_CO2' else (z_val if eje_z == 'Flujo_CO2' else X['Flujo_CO2'].mean()),
234
- x_val if eje_x == 'Tiempo' else (z_val if eje_z == 'Tiempo' else X['Tiempo'].mean())
235
- ]))
236
- grid_data.append(row_data)
237
-
238
- # Convertir a DataFrame con orden de columnas fijo
239
- grid_df = pd.DataFrame(grid_data)[variable_columns]
240
-
241
- # Transformar datos para predicciones
242
- grid_poly = poly.transform(grid_df)
243
-
244
- # Predecir valores
245
- Y_grid = modelo.predict(grid_poly).reshape(X_grid.shape)
246
-
247
- # Gráfico dinámico con Plotly
248
- fig = go.Figure(data=[go.Surface(z=Y_grid, x=X_grid, y=Z_grid, colorscale='Viridis')])
249
- fig.update_layout(
250
- title=f"Superficie de Respuesta: {eje_x} vs {eje_z} vs Rendimiento",
251
- scene=dict(
252
- xaxis_title=eje_x,
253
- yaxis_title=eje_z,
254
- zaxis_title="Rendimiento (%)"
255
- )
256
- )
257
- st.plotly_chart(fig, use_container_width=True)
258
-
259
- # Análisis de sensibilidad
260
- st.subheader("Análisis de Sensibilidad")
261
- temp = st.slider("Temperatura (°C)", int(X['Temperatura'].min()), int(X['Temperatura'].max()), int(X['Temperatura'].mean()))
262
- pres = st.slider("Presión (Bar)", int(X['Presión'].min()), int(X['Presión'].max()), int(X['Presión'].mean()))
263
- flujo = st.slider("Flujo CO2 (g/min)", int(X['Flujo_CO2'].min()), int(X['Flujo_CO2'].max()), int(X['Flujo_CO2'].mean()))
264
- tiempo = st.slider("Tiempo (h)", int(X['Tiempo'].min()), int(X['Tiempo'].max()), int(X['Tiempo'].mean()))
265
-
266
- # Predicción para los valores seleccionados
267
- entrada_sensibilidad = pd.DataFrame({'Temperatura': [temp], 'Presión': [pres], 'Flujo_CO2': [flujo], 'Tiempo': [tiempo]})
268
- entrada_poly = poly.transform(entrada_sensibilidad)
269
- prediccion = modelo.predict(entrada_poly)
270
- st.write(f"**Rendimiento Predicho:** {prediccion[0]:.2f}%")
271
-
272
- # Optimización de puntos mejorada
273
- st.subheader("Determinación de Puntos Óptimos")
274
- def objetivo(params):
275
- """Función objetivo para optimización."""
276
- # Transformar parámetros a DataFrame
277
- entrada = pd.DataFrame([params], columns=variable_columns)
278
- entrada_poly = poly.transform(entrada)
279
- return -modelo.predict(entrada_poly)[0] # Negativo para maximizar
280
-
281
- # Métodos de Optimización
282
- st.write("#### Comparación de Métodos de Optimización")
283
-
284
- # Límites de las variables
285
- limites = [(X[col].min(), X[col].max()) for col in variable_columns]
286
-
287
- # 1. Optimización por L-BFGS-B (Método Local)
288
- #st.write("##### Método L-BFGS-B (Optimización Local)")
289
- x0 = [X[col].mean() for col in variable_columns]
290
- resultado_lbfgs = minimize(
291
- objetivo,
292
- x0=x0,
293
- bounds=limites,
294
- method='L-BFGS-B'
295
- )
296
-
297
- # 2. Evolución Diferencial (Método Global)
298
- #st.write("##### Evolución Diferencial (Optimización Global)")
299
- resultado_de = differential_evolution(
300
- objetivo,
301
- bounds=limites,
302
- strategy='best1bin',
303
- popsize=15,
304
- maxiter=100
305
- )
306
-
307
- # 3. Múltiples Inicios Aleatorios
308
- #st.write("##### Múltiples Inicios Aleatorios")
309
- def multi_start_optimize(num_starts=10):
310
- resultados = []
311
- for _ in range(num_starts):
312
- # Punto inicial aleatorio
313
- x0 = [np.random.uniform(low, high) for low, high in limites]
314
-
315
- resultado = minimize(
316
- objetivo,
317
- x0=x0,
318
- bounds=limites,
319
- method='L-BFGS-B'
320
- )
321
- resultados.append((resultado, -resultado.fun))
322
-
323
- # Encontrar el mejor resultado
324
- return max(resultados, key=lambda x: x[1])
325
-
326
- resultado_multi = multi_start_optimize()
327
-
328
- # Mostrar resultados de optimización
329
- metodos = [
330
- ("L-BFGS-B", resultado_lbfgs, -resultado_lbfgs.fun),
331
- ("Evolución Diferencial", resultado_de, -resultado_de.fun),
332
- ("Múltiples Inicios", resultado_multi[0], resultado_multi[1])
333
- ]
334
-
335
- # Tabla comparativa de resultados
336
- resultados_df = pd.DataFrame(columns=variable_columns + ['Rendimiento Predicho'])
337
- for nombre, resultado, rendimiento in metodos:
338
- if resultado.success:
339
- fila = pd.DataFrame([list(resultado.x) + [rendimiento]],
340
- columns=variable_columns + ['Rendimiento Predicho'])
341
- fila.insert(0, 'Método', nombre)
342
- resultados_df = pd.concat([resultados_df, fila], ignore_index=True)
343
-
344
- # Mostrar tabla de resultados
345
- #st.write("### Comparación de Resultados de Optimización")
346
- st.dataframe(resultados_df)
347
-
348
- # Seleccionar el mejor resultado
349
- mejor_resultado = resultados_df.loc[resultados_df['Rendimiento Predicho'].idxmax()]
350
- st.write("### Punto Óptimo Recomendado")
351
- st.write(f"**Método:** {mejor_resultado['Método']}")
352
-
353
- # Mostrar detalles del mejor punto
354
- detalles_optimos = mejor_resultado[variable_columns].to_dict()
355
- detalles_str = ", ".join([f"{col}: {val:.2f}" for col, val in detalles_optimos.items()])
356
- st.write(f"**Punto Óptimo:** {detalles_str}")
357
- st.write(f"**Rendimiento Máximo Predicho:** {mejor_resultado['Rendimiento Predicho']:.2f}%")
358
-
359
- # Análisis de Incertidumbre
360
- st.subheader("Análisis de Incertidumbre")
361
-
362
- # Input para número de bootstraps
363
- num_bootstraps = st.number_input(
364
- "Número de Bootstraps",
365
- min_value=10,
366
- max_value=1000,
367
- value=100,
368
- step=10,
369
- help="Número de remuestreos para el análisis de incertidumbre"
370
- )
371
-
372
- # Botón para realizar análisis de incertidumbre
373
- if st.button("Realizar Análisis de Incertidumbre"):
374
- with st.spinner('Realizando análisis de bootstrapping...'):
375
- # Bootstrap para estimar intervalos de confianza
376
- def bootstrap_optimize(num_bootstraps=num_bootstraps):
377
- resultados_bootstrap = []
378
-
379
- for _ in range(num_bootstraps):
380
- # Muestreo con reemplazo
381
- indices = np.random.randint(0, len(X), len(X))
382
- X_boot = X.iloc[indices]
383
- y_boot = y.iloc[indices]
384
-
385
- # Ajustar modelo
386
- poly_boot = PolynomialFeatures(degree=2, include_bias=False)
387
- X_poly_boot = poly_boot.fit_transform(X_boot)
388
- modelo_boot = LinearRegression()
389
- modelo_boot.fit(X_poly_boot, y_boot)
390
-
391
- # Definir nueva función objetivo
392
- def objetivo_boot(params):
393
- entrada = pd.DataFrame([params], columns=variable_columns)
394
- entrada_poly = poly_boot.transform(entrada)
395
- return -modelo_boot.predict(entrada_poly)[0]
396
-
397
- # Optimizar
398
- resultado = differential_evolution(
399
- objetivo_boot,
400
- bounds=limites,
401
- strategy='best1bin',
402
- popsize=15,
403
- maxiter=100
404
- )
405
-
406
- resultados_bootstrap.append({
407
- 'Punto': resultado.x,
408
- 'Rendimiento': -resultado.fun
409
- })
410
-
411
- return resultados_bootstrap
412
-
413
- # Realizar bootstrap
414
- resultados_bootstrap = bootstrap_optimize()
415
-
416
- # Convertir a DataFrame
417
- bootstrap_df = pd.DataFrame(resultados_bootstrap)
418
-
419
- # Calcular intervalos de confianza
420
- intervalos_confianza = {}
421
- for i, col in enumerate(variable_columns):
422
- intervalos_confianza[col] = (
423
- np.percentile(bootstrap_df['Punto'].apply(lambda x: x[i]), 2.5),
424
- np.percentile(bootstrap_df['Punto'].apply(lambda x: x[i]), 97.5)
425
- )
426
-
427
- # Mostrar intervalos de confianza
428
- st.write("### Intervalos de Confianza (95%)")
429
- for col, (min_val, max_val) in intervalos_confianza.items():
430
- st.write(f"**{col}:** [{min_val:.2f}, {max_val:.2f}]")
431
-
432
- # Distribución de rendimientos
433
- st.write("### Distribución de Rendimientos en Bootstrap")
434
- fig_bootstrap = plt.figure(figsize=(10, 6))
435
- plt.hist(bootstrap_df['Rendimiento'], bins=30, edgecolor='black')
436
- plt.title(f'Distribución de Rendimientos Predichos (Bootstrap: {num_bootstraps})')
437
- plt.xlabel('Rendimiento (%)')
438
- plt.ylabel('Frecuencia')
439
  st.pyplot(fig_bootstrap)
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.graph_objects as go
5
+ from scipy.optimize import minimize, differential_evolution
6
+ from sklearn.preprocessing import PolynomialFeatures
7
+ from sklearn.linear_model import LinearRegression
8
+ from sklearn.metrics import mean_squared_error, r2_score
9
+ import matplotlib.pyplot as plt
10
+ import seaborn as sns
11
+ import io
12
+ # Configuración inicial
13
+ # Configuración de la aplicación
14
+ st.set_page_config(page_title="Optimización Avanzada", page_icon="📊",layout="wide")
15
+ # Display the image above the title
16
+ st.image('cannabis.jpg')
17
+ st.title("Optimización Avanzada con Diseño Experimental Box-Behnken")
18
+ st.write("Aplicación con regresión cuadrática y estrategias de optimización mejoradas.")
19
+
20
+ # Crear las pestañas
21
+ tabs = st.selectbox("Selecciona una opción", ["Fundamento Teórico", "Aplicación Interactiva"])
22
+
23
+ if tabs == "Fundamento Teórico":
24
+ st.header("Fundamento Teórico")
25
+
26
+ # Imagen del proceso
27
+ st.subheader("Proceso de Extracción de CBD")
28
+ st.image("CBD extraction process.png", caption="Proceso de Extracción de CBD con CO2 Supercrítico", use_container_width=True)
29
+ st.write("""
30
+ El proceso de extracción de CBD utiliza tecnología de CO2 supercrítico debido a su eficiencia y capacidad para producir extractos puros. Este método incluye etapas clave como molienda, extracción, separación y refinamiento, garantizando un producto de alta calidad para aplicaciones medicinales y comerciales.
31
+ """)
32
+
33
+ # Diseño Experimental Box-Behnken
34
+ st.subheader("Diseño Experimental Box-Behnken")
35
+ st.write("""
36
+ El diseño experimental **Box-Behnken** se utiliza para modelar y optimizar procesos complejos. En este caso, se aplica para maximizar el rendimiento de CBD considerando variables clave como **Temperatura**, **Presión**, **Flujo de CO2** y **Tiempo**.
37
+ Este enfoque reduce significativamente la cantidad de experimentos necesarios, permitiendo explorar interacciones no lineales de manera eficiente.
38
+ """)
39
+
40
+ # Proceso de Producción de CBD
41
+ st.subheader("Proceso de Producción de CBD")
42
+ st.write("""
43
+ Según el documento *Optimization of Supercritical Carbon Dioxide Fluid Extraction of Medicinal Cannabis from Quebec*, el proceso de extracción con CO2 supercrítico es preferido por su alta selectividad y pureza. Las principales etapas incluyen:
44
+ 1. **Preparación de la materia prima**: Molienda y acondicionamiento.
45
+ 2. **Extracción supercrítica**:
46
+ - El CO2 actúa como solvente bajo condiciones controladas de presión y temperatura.
47
+ - Variables clave: presión (150-320 bar), temperatura (40-70°C), flujo de CO2 (5-15 g/min), tiempo (2-4 horas).
48
+ 3. **Separación y recolección**: El CO2 se despresuriza para liberar los cannabinoides extraídos.
49
+ 4. **Refinamiento posterior**: Remoción de ceras y otros compuestos no deseados.
50
+ """)
51
+
52
+ # Caso de Negocio
53
+ st.subheader("Caso de Negocio: Optimización del Rendimiento")
54
+ st.write("""
55
+ Optimizar el rendimiento del proceso de extracción permite:
56
+ - Maximizar la cantidad de CBD extraído por lote.
57
+ - Reducir costos operativos (energía, solventes, tiempo).
58
+ - Mejorar la calidad del producto final.
59
+
60
+ Este enfoque es crucial en la industria del cannabis medicinal, donde la eficiencia del proceso impacta directamente en la rentabilidad y sostenibilidad del negocio.
61
+ """)
62
+
63
+ # Método Basado en CRISP-DM
64
+ st.subheader("Metodología Basada en CRISP-DM")
65
+ st.write("""
66
+ La metodología **CRISP-DM** estructura el desarrollo del modelo en seis etapas:
67
+ 1. **Comprensión del Negocio**: Definir objetivos y restricciones del proceso.
68
+ 2. **Comprensión de los Datos**: Analizar datos experimentales y evaluar su calidad.
69
+ 3. **Preparación de los Datos**: Limpiar y transformar datos para el modelado.
70
+ 4. **Modelado**: Ajustar un modelo de regresión cuadrática para capturar relaciones no lineales.
71
+ 5. **Evaluación**: Validar el modelo y analizar su desempeño.
72
+ 6. **Despliegue**: Implementar el modelo en una aplicación interactiva para optimización en tiempo real.
73
+ """)
74
+
75
+ # Optimización
76
+ st.subheader("Optimización")
77
+ st.write("""
78
+ Se emplean técnicas avanzadas para maximizar el rendimiento:
79
+ - **L-BFGS-B**: Método de optimización local.
80
+ - **Evolución Diferencial**: Optimización global para evitar óptimos locales.
81
+ - **Múltiples inicios aleatorios**: Combina estrategias locales y globales para robustez.
82
+ """)
83
+
84
+ # Referencias
85
+ st.subheader("Referencias")
86
+ st.write("""
87
+ - [Optimization of Supercritical Carbon Dioxide Fluid Extraction of Medicinal Cannabis from Quebec](https://www.mdpi.com/2227-9717/11/7/1953).
88
+ - Herrero, M., Cifuentes, A., & Ibañez, E. (2006). Supercritical fluid extraction: Recent advances and applications. *Journal of Chromatography A*, 1131(1), 1–24.
89
+ - Turner, C., Mathiasson, L., & Lewis, G. (2001). Supercritical fluid extraction and chromatography. *Journal of Biochemical Analysis*, 121(3), 35–58.
90
+ """)
91
+
92
+ elif tabs == "Aplicación Interactiva":
93
+ st.header("Aplicación Interactiva")
94
+ st.write("A continuación, puedes cargar tus datos, realizar predicciones y optimizar el rendimiento del proceso de extracción de cannabinoides.")
95
+
96
+ # Opciones de selección de datos de ejemplo
97
+ st.subheader("Datos de Ejemplo")
98
+ usar_datos_exp = st.checkbox("Usar datos de ejemplo: datos_exp.csv")
99
+ usar_datos_process = st.checkbox("Usar datos de ejemplo: datos_process.csv")
100
+
101
+ # Inicializar variable de datos
102
+ data = None
103
+
104
+ # Verificar qué checkbox está seleccionado y cargar el archivo correspondiente
105
+ if usar_datos_exp and not usar_datos_process:
106
+ data = pd.read_csv("data_exp.csv")
107
+ st.success("Datos de ejemplo (datos_exp.csv) cargados exitosamente.")
108
+ st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
109
+ elif usar_datos_process and not usar_datos_exp:
110
+ data = pd.read_csv("data_process.csv")
111
+ st.success("Datos de ejemplo (datos_process.csv) cargados exitosamente.")
112
+ st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
113
+ elif usar_datos_exp and usar_datos_process:
114
+ st.error("Por favor, selecciona solo un conjunto de datos de ejemplo a la vez.")
115
+
116
+ # Opción para cargar datos personalizados
117
+ st.subheader("Carga tus Datos")
118
+ uploaded_file = st.file_uploader("Carga un archivo CSV con los datos experimentales:", type="csv")
119
+
120
+ if uploaded_file is not None:
121
+ # Leer datos cargados
122
+ data = pd.read_csv(uploaded_file)
123
+ st.success("Datos cargados exitosamente desde el archivo proporcionado.")
124
+ st.dataframe(data, use_container_width=True) # Ajustar al ancho de la app
125
+
126
+ # Validación para asegurarse de que se cargaron datos
127
+ if data is None:
128
+ st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
129
+ else:
130
+ st.write("### Datos listos para su análisis.")
131
+
132
+ # Definir el orden fijo de variables
133
+ variable_columns = ['Temperatura', 'Presión', 'Flujo_CO2', 'Tiempo']
134
+
135
+ # Extraer variables independientes y dependiente en el orden correcto
136
+ X = data[variable_columns]
137
+ y = data['Rendimiento']
138
+
139
+ # Generar términos cuadráticos (regresión polinómica de segundo grado)
140
+ poly = PolynomialFeatures(degree=2, include_bias=False)
141
+ X_poly = poly.fit_transform(X)
142
+ columnas_poly = poly.get_feature_names_out(X.columns)
143
+
144
+ # Ajustar modelo cuadrático
145
+ modelo = LinearRegression()
146
+ modelo.fit(X_poly, y)
147
+
148
+ # Predicciones del modelo
149
+ y_pred = modelo.predict(X_poly)
150
+
151
+ # Evaluación del modelo
152
+ st.subheader("Evaluación del Modelo Cuadrático")
153
+ st.write(f"**Error Cuadrático Medio (MSE):** {mean_squared_error(y, y_pred):.4f}")
154
+ st.write(f"**R² (Coeficiente de Determinación):** {r2_score(y, y_pred):.4f}")
155
+
156
+ # Resumen del modelo: coeficientes
157
+
158
+ # Visualización paralela usando columnas
159
+ col1, col2 = st.columns(2)
160
+
161
+ # En la columna 1, mostrar el DataFrame con términos y coeficientes, adaptado al ancho de la columna
162
+ with col1:
163
+ st.markdown("#### Términos y Coeficientes del Modelo")
164
+
165
+ # Crear un DataFrame con los coeficientes
166
+ coeficientes = pd.DataFrame({
167
+ 'Término': columnas_poly,
168
+ 'Coeficiente': modelo.coef_
169
+ })
170
+
171
+ coeficientes = coeficientes.sort_values(by='Coeficiente', ascending=False)
172
+
173
+ # Función para aplicar colores a los coeficientes
174
+ def color_coef(val):
175
+ color = 'red' if val > 0 else 'blue' # Los coeficientes positivos serán rojos, negativos azules
176
+ return f'background-color: {color}; color: white;'
177
+
178
+ # Aplicar estilo a la columna 'Coeficiente' para colorear los valores
179
+ styled_coef = coeficientes.style.applymap(color_coef, subset=['Coeficiente'])
180
+
181
+ # Mostrar el DataFrame estilizado y ajustado al ancho de la columna
182
+ st.dataframe(styled_coef, use_container_width=True)
183
+
184
+ # En la columna 2, mostrar el gráfico de importancia de las variables, adaptado al ancho de la columna
185
+ with col2:
186
+ st.markdown("#### Importancia de las Variables (Feature Importance)")
187
+
188
+ # Calcular la importancia de las variables (valor absoluto de los coeficientes)
189
+ coef_abs = np.abs(modelo.coef_) # Valor absoluto de los coeficientes
190
+ feature_importance = pd.DataFrame({
191
+ 'Variable': columnas_poly,
192
+ 'Importancia': coef_abs
193
+ }).sort_values(by='Importancia', ascending=False) # De mayor a menor
194
+
195
+ # Gráfico de barras horizontal con el eje Y invertido
196
+ fig_importance = go.Figure(go.Bar(
197
+ y=feature_importance['Variable'],
198
+ x=feature_importance['Importancia'],
199
+ orientation='h',
200
+ marker=dict(color='teal')
201
+ ))
202
+ fig_importance.update_layout(
203
+ title="Importancia de las Variables en el Modelo Cuadrático",
204
+ xaxis_title="Importancia",
205
+ yaxis_title="Variables",
206
+ yaxis=dict(autorange="reversed"), # Invertir el eje Y
207
+ margin=dict(l=0, r=0, t=30, b=30) # Ajustar márgenes
208
+ )
209
+ st.plotly_chart(fig_importance, use_container_width=True)
210
+
211
+ # Superficies de respuesta dinámicas
212
+ st.subheader("Superficies de Respuesta")
213
+
214
+ # Selector de variables
215
+ eje_x = st.selectbox("Selecciona la variable para el eje X:", variable_columns, index=0)
216
+ eje_z = st.selectbox("Selecciona la variable para el eje Z:", variable_columns, index=1)
217
+
218
+ # Rango para generar puntos
219
+ x_range = np.linspace(X[eje_x].min(), X[eje_x].max(), 50)
220
+ z_range = np.linspace(X[eje_z].min(), X[eje_z].max(), 50)
221
+ X_grid, Z_grid = np.meshgrid(x_range, z_range)
222
+
223
+ # Preparar valores para las otras dos variables
224
+ otras_variables = [col for col in variable_columns if col not in [eje_x, eje_z]]
225
+
226
+ # Crear grilla de predicción con orden de columnas fijo
227
+ grid_data = []
228
+ for x_val, z_val in zip(X_grid.ravel(), Z_grid.ravel()):
229
+ # Crear un diccionario con todas las variables en el orden correcto
230
+ row_data = dict(zip(variable_columns, [
231
+ x_val if eje_x == 'Temperatura' else X['Temperatura'].mean(),
232
+ x_val if eje_x == 'Presión' else (z_val if eje_z == 'Presión' else X['Presión'].mean()),
233
+ x_val if eje_x == 'Flujo_CO2' else (z_val if eje_z == 'Flujo_CO2' else X['Flujo_CO2'].mean()),
234
+ x_val if eje_x == 'Tiempo' else (z_val if eje_z == 'Tiempo' else X['Tiempo'].mean())
235
+ ]))
236
+ grid_data.append(row_data)
237
+
238
+ # Convertir a DataFrame con orden de columnas fijo
239
+ grid_df = pd.DataFrame(grid_data)[variable_columns]
240
+
241
+ # Transformar datos para predicciones
242
+ grid_poly = poly.transform(grid_df)
243
+
244
+ # Predecir valores
245
+ Y_grid = modelo.predict(grid_poly).reshape(X_grid.shape)
246
+
247
+ # Gráfico dinámico con Plotly
248
+ fig = go.Figure(data=[go.Surface(z=Y_grid, x=X_grid, y=Z_grid, colorscale='Viridis')])
249
+ fig.update_layout(
250
+ title=f"Superficie de Respuesta: {eje_x} vs {eje_z} vs Rendimiento",
251
+ scene=dict(
252
+ xaxis_title=eje_x,
253
+ yaxis_title=eje_z,
254
+ zaxis_title="Rendimiento (%)"
255
+ )
256
+ )
257
+ st.plotly_chart(fig, use_container_width=True)
258
+
259
+ # Análisis de sensibilidad
260
+ st.subheader("Análisis de Sensibilidad")
261
+ temp = st.slider("Temperatura (°C)", int(X['Temperatura'].min()), int(X['Temperatura'].max()), int(X['Temperatura'].mean()))
262
+ pres = st.slider("Presión (Bar)", int(X['Presión'].min()), int(X['Presión'].max()), int(X['Presión'].mean()))
263
+ flujo = st.slider("Flujo CO2 (g/min)", int(X['Flujo_CO2'].min()), int(X['Flujo_CO2'].max()), int(X['Flujo_CO2'].mean()))
264
+ tiempo = st.slider("Tiempo (h)", int(X['Tiempo'].min()), int(X['Tiempo'].max()), int(X['Tiempo'].mean()))
265
+
266
+ # Predicción para los valores seleccionados
267
+ entrada_sensibilidad = pd.DataFrame({'Temperatura': [temp], 'Presión': [pres], 'Flujo_CO2': [flujo], 'Tiempo': [tiempo]})
268
+ entrada_poly = poly.transform(entrada_sensibilidad)
269
+ prediccion = modelo.predict(entrada_poly)
270
+ st.write(f"**Rendimiento Predicho:** {prediccion[0]:.2f}%")
271
+
272
+ # Optimización de puntos mejorada
273
+ st.subheader("Determinación de Puntos Óptimos")
274
+ def objetivo(params):
275
+ """Función objetivo para optimización."""
276
+ # Transformar parámetros a DataFrame
277
+ entrada = pd.DataFrame([params], columns=variable_columns)
278
+ entrada_poly = poly.transform(entrada)
279
+ return -modelo.predict(entrada_poly)[0] # Negativo para maximizar
280
+
281
+ # Métodos de Optimización
282
+ st.write("#### Comparación de Métodos de Optimización")
283
+
284
+ # Límites de las variables
285
+ limites = [(X[col].min(), X[col].max()) for col in variable_columns]
286
+
287
+ # 1. Optimización por L-BFGS-B (Método Local)
288
+ #st.write("##### Método L-BFGS-B (Optimización Local)")
289
+ x0 = [X[col].mean() for col in variable_columns]
290
+ resultado_lbfgs = minimize(
291
+ objetivo,
292
+ x0=x0,
293
+ bounds=limites,
294
+ method='L-BFGS-B'
295
+ )
296
+
297
+ # 2. Evolución Diferencial (Método Global)
298
+ #st.write("##### Evolución Diferencial (Optimización Global)")
299
+ resultado_de = differential_evolution(
300
+ objetivo,
301
+ bounds=limites,
302
+ strategy='best1bin',
303
+ popsize=15,
304
+ maxiter=100
305
+ )
306
+
307
+ # 3. Múltiples Inicios Aleatorios
308
+ #st.write("##### Múltiples Inicios Aleatorios")
309
+ def multi_start_optimize(num_starts=10):
310
+ resultados = []
311
+ for _ in range(num_starts):
312
+ # Punto inicial aleatorio
313
+ x0 = [np.random.uniform(low, high) for low, high in limites]
314
+
315
+ resultado = minimize(
316
+ objetivo,
317
+ x0=x0,
318
+ bounds=limites,
319
+ method='L-BFGS-B'
320
+ )
321
+ resultados.append((resultado, -resultado.fun))
322
+
323
+ # Encontrar el mejor resultado
324
+ return max(resultados, key=lambda x: x[1])
325
+
326
+ resultado_multi = multi_start_optimize()
327
+
328
+ # Mostrar resultados de optimización
329
+ metodos = [
330
+ ("L-BFGS-B", resultado_lbfgs, -resultado_lbfgs.fun),
331
+ ("Evolución Diferencial", resultado_de, -resultado_de.fun),
332
+ ("Múltiples Inicios", resultado_multi[0], resultado_multi[1])
333
+ ]
334
+
335
+ # Tabla comparativa de resultados
336
+ resultados_df = pd.DataFrame(columns=variable_columns + ['Rendimiento Predicho'])
337
+ for nombre, resultado, rendimiento in metodos:
338
+ if resultado.success:
339
+ fila = pd.DataFrame([list(resultado.x) + [rendimiento]],
340
+ columns=variable_columns + ['Rendimiento Predicho'])
341
+ fila.insert(0, 'Método', nombre)
342
+ resultados_df = pd.concat([resultados_df, fila], ignore_index=True)
343
+
344
+ # Mostrar tabla de resultados
345
+ #st.write("### Comparación de Resultados de Optimización")
346
+ st.dataframe(resultados_df)
347
+
348
+ # Seleccionar el mejor resultado
349
+ mejor_resultado = resultados_df.loc[resultados_df['Rendimiento Predicho'].idxmax()]
350
+ st.write("### Punto Óptimo Recomendado")
351
+ st.write(f"**Método:** {mejor_resultado['Método']}")
352
+
353
+ # Mostrar detalles del mejor punto
354
+ detalles_optimos = mejor_resultado[variable_columns].to_dict()
355
+ detalles_str = ", ".join([f"{col}: {val:.2f}" for col, val in detalles_optimos.items()])
356
+ st.write(f"**Punto Óptimo:** {detalles_str}")
357
+ st.write(f"**Rendimiento Máximo Predicho:** {mejor_resultado['Rendimiento Predicho']:.2f}%")
358
+
359
+ # Análisis de Incertidumbre
360
+ st.subheader("Análisis de Incertidumbre")
361
+
362
+ # Input para número de bootstraps
363
+ num_bootstraps = st.number_input(
364
+ "Número de Bootstraps",
365
+ min_value=10,
366
+ max_value=1000,
367
+ value=100,
368
+ step=10,
369
+ help="Número de remuestreos para el análisis de incertidumbre"
370
+ )
371
+
372
+ # Botón para realizar análisis de incertidumbre
373
+ if st.button("Realizar Análisis de Incertidumbre"):
374
+ with st.spinner('Realizando análisis de bootstrapping...'):
375
+ # Bootstrap para estimar intervalos de confianza
376
+ def bootstrap_optimize(num_bootstraps=num_bootstraps):
377
+ resultados_bootstrap = []
378
+
379
+ for _ in range(num_bootstraps):
380
+ # Muestreo con reemplazo
381
+ indices = np.random.randint(0, len(X), len(X))
382
+ X_boot = X.iloc[indices]
383
+ y_boot = y.iloc[indices]
384
+
385
+ # Ajustar modelo
386
+ poly_boot = PolynomialFeatures(degree=2, include_bias=False)
387
+ X_poly_boot = poly_boot.fit_transform(X_boot)
388
+ modelo_boot = LinearRegression()
389
+ modelo_boot.fit(X_poly_boot, y_boot)
390
+
391
+ # Definir nueva función objetivo
392
+ def objetivo_boot(params):
393
+ entrada = pd.DataFrame([params], columns=variable_columns)
394
+ entrada_poly = poly_boot.transform(entrada)
395
+ return -modelo_boot.predict(entrada_poly)[0]
396
+
397
+ # Optimizar
398
+ resultado = differential_evolution(
399
+ objetivo_boot,
400
+ bounds=limites,
401
+ strategy='best1bin',
402
+ popsize=15,
403
+ maxiter=100
404
+ )
405
+
406
+ resultados_bootstrap.append({
407
+ 'Punto': resultado.x,
408
+ 'Rendimiento': -resultado.fun
409
+ })
410
+
411
+ return resultados_bootstrap
412
+
413
+ # Realizar bootstrap
414
+ resultados_bootstrap = bootstrap_optimize()
415
+
416
+ # Convertir a DataFrame
417
+ bootstrap_df = pd.DataFrame(resultados_bootstrap)
418
+
419
+ # Calcular intervalos de confianza
420
+ intervalos_confianza = {}
421
+ for i, col in enumerate(variable_columns):
422
+ intervalos_confianza[col] = (
423
+ np.percentile(bootstrap_df['Punto'].apply(lambda x: x[i]), 2.5),
424
+ np.percentile(bootstrap_df['Punto'].apply(lambda x: x[i]), 97.5)
425
+ )
426
+
427
+ # Mostrar intervalos de confianza
428
+ st.write("### Intervalos de Confianza (95%)")
429
+ for col, (min_val, max_val) in intervalos_confianza.items():
430
+ st.write(f"**{col}:** [{min_val:.2f}, {max_val:.2f}]")
431
+
432
+ # Distribución de rendimientos
433
+ st.write("### Distribución de Rendimientos en Bootstrap")
434
+ fig_bootstrap = plt.figure(figsize=(10, 6))
435
+ plt.hist(bootstrap_df['Rendimiento'], bins=30, edgecolor='black')
436
+ plt.title(f'Distribución de Rendimientos Predichos (Bootstrap: {num_bootstraps})')
437
+ plt.xlabel('Rendimiento (%)')
438
+ plt.ylabel('Frecuencia')
439
  st.pyplot(fig_bootstrap)