Migue1804 commited on
Commit
1d96d9b
·
verified ·
1 Parent(s): 777d265

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +723 -752
app.py CHANGED
@@ -1,752 +1,723 @@
1
- import streamlit as st
2
- import networkx as nx
3
- from pyvis.network import Network
4
- import streamlit.components.v1 as components
5
- from PIL import Image
6
- import io
7
- import base64
8
- import pandas as pd
9
- import matplotlib.pyplot as plt
10
- import plotly.express as px
11
- import osmnx as ox
12
- from geopy.distance import geodesic
13
- import streamlit.components.v1 as components
14
- import matplotlib.colors as mcolors
15
- import plotly.graph_objects as go
16
- import graphviz
17
-
18
- # Configuración de la aplicación
19
- st.set_page_config(page_title="Visualización de Marcos SCVID", layout="wide")
20
- # Display the image above the title
21
- st.image('Napkin App.png', use_column_width=True)
22
-
23
- def add_background_local(image_path):
24
- with open(image_path, "rb") as image_file:
25
- encoded = base64.b64encode(image_file.read()).decode()
26
- st.markdown(
27
- f"""
28
- <style>
29
- .stApp {{
30
- background-image: url("data:image/png;base64,{encoded}");
31
- background-size: cover;
32
- background-position: center;
33
- background-repeat: no-repeat;
34
- background-attachment: fixed;
35
- }}
36
- .block-container {{
37
- background-color: rgba(255, 255, 255, 0.95);
38
- padding: 20px;
39
- border-radius: 10px;
40
- }}
41
- </style>
42
- """,
43
- unsafe_allow_html=True
44
- )
45
-
46
- # Ruta de la imagen local
47
- image_path = "Servilleta App.jpg" # Reemplaza con la ruta a tu imagen local
48
-
49
- # Aplicar la imagen de fondo
50
- add_background_local(image_path)
51
-
52
- # Agregar una nueva pestaña para la reseña del libro
53
- tabs = st.tabs([ "Reseña del Libro", "Quién/Qué", "Cuánto", "Dónde", "Cuándo", "Cómo", "Por qué","Acerca de mí"])
54
-
55
- # Pestaña: Reseña del libro
56
- with tabs[0]:
57
-
58
- resumen = """
59
- ## Resumen del libro: "The Back of the Napkin de Dan Roam"
60
-
61
- Este resumen se centra en el marco de 6 preguntas, el SQVID y las estrategias visuales recomendadas en el libro
62
- "The Back of the Napkin" de Dan Roam para resolver problemas y vender ideas.
63
-
64
- El libro argumenta que el pensamiento visual, utilizando dibujos sencillos, es una herramienta poderosa para la
65
- resolución de problemas y la comunicación efectiva. Roam explica que cualquier problema se puede aclarar con una
66
- imagen, y cualquier imagen se puede crear utilizando un conjunto simple de herramientas y reglas.
67
-
68
- ### El Marco de las 6 Preguntas (6W)
69
-
70
- Roam propone un marco de seis preguntas fundamentales, conocidas como las 6W, para analizar y abordar cualquier problema:
71
-
72
- * **Quién/Qué (Who/What):** Define los actores y objetos involucrados.
73
- * **Cuánto (How Much):** Analiza las cantidades, medidas y datos.
74
- * **Dónde (Where):** Ubica el problema en un contexto espacial.
75
- * **Cuándo (When):** Establece un marco temporal para el problema.
76
- * **Cómo (How):** Describe los procesos y relaciones de causa y efecto.
77
- * **Por qué (Why):** Explora las razones, motivaciones y causas subyacentes.
78
-
79
- Estas preguntas no solo ayudan a comprender el problema, sino que también guían la elección del gráfico o estrategia
80
- visual más efectiva para comunicarlo.
81
-
82
- ### El SQVID como Herramienta de Imaginación
83
-
84
- El SQVID es un acrónimo que representa cinco preguntas para activar la imaginación visual y explorar diferentes
85
- perspectivas de una idea:
86
-
87
- * **Simple vs. Elaborado:** ¿Se necesita una representación simple o detallada?
88
- * **Cualitativo vs. Cuantitativo:** ¿Se enfatizan las características o los datos numéricos?
89
- * **Visión vs. Ejecución:** ¿Se busca inspirar o mostrar los pasos concretos?
90
- * **Individual vs. Comparación:** ¿Se presenta una sola idea o se compara con otras?
91
- * **Cambio vs. Status Quo:** ¿Se propone una transformación o se describe la situación actual?
92
-
93
- Al responder estas preguntas, se pueden generar múltiples representaciones visuales de una idea y elegir la más
94
- efectiva para la audiencia y el objetivo.
95
-
96
- ### Gráficos y Estrategias Visuales Recomendadas
97
-
98
- El libro presenta seis marcos visuales principales, cada uno correspondiente a una de las 6W:
99
-
100
- * **Retrato:** Para mostrar **quién/qué** (ej. un organigrama, un diagrama de personajes).
101
- * **Gráfico:** Para visualizar **cuánto** (ej. gráfico de barras, gráfico circular).
102
- * **Mapa:** Para representar **dónde** (ej. mapa mental, diagrama de flujo de procesos).
103
- * **Línea de Tiempo:** Para ilustrar **cuándo** (ej. cronograma, diagrama de Gantt).
104
- * **Diagrama de Flujo:** Para explicar **cómo** (ej. diagrama de flujo de trabajo, algoritmo).
105
- * **Gráfico de Múltiples Variables:** Para analizar **por qué** (ej. gráfico de dispersión, mapa de calor).
106
-
107
- En resumen, "The Back of the Napkin" ofrece un enfoque práctico para utilizar el pensamiento visual como herramienta
108
- para la resolución de problemas y la comunicación efectiva.
109
- """
110
-
111
- st.markdown(resumen)
112
-
113
- st.subheader("Razones de la escogencia de los gráficos")
114
-
115
- # Ejemplo de razones
116
- explicacion_graficos = """
117
- Los gráficos utilizados en esta aplicación se alinean con el marco visual propuesto por Dan Roam en "The Back of the Napkin".
118
-
119
- 1. **Quién/Qué (Gráfico de Nodos):** Este gráfico permite mostrar las conexiones entre personas, estudios y habilidades, siguiendo la recomendación de usar retratos o gráficos de red para visualizar relaciones entre actores y objetos.
120
-
121
- 2. **Cuánto (Gráfico de Pareto):** Este gráfico es ideal para visualizar cantidades y datos. En particular, resalta la importancia de unos pocos elementos clave sobre la mayoría, una estrategia visual efectiva para mostrar patrones cuantitativos.
122
-
123
- 3. **Cómo (Diagrama de Flujo):** El uso de diagramas de flujo para mostrar procesos es una herramienta clara y directa para entender relaciones de causa y efecto.
124
-
125
- 4. **Cuándo (Línea de Tiempo):** Para mostrar secuencias temporales, la línea de tiempo es el gráfico recomendado para organizar eventos de manera clara y ordenada.
126
-
127
- 5. **Por qué (Gráfico de Burbujas):** El gráfico de burbujas permite analizar múltiples variables simultáneamente, destacando cómo las categorías se comparan en función de varias dimensiones (eje X, eje Y, tamaño y color de las burbujas). Esta visualización es útil para explorar las razones subyacentes y patrones complejos en los datos, facilitando la comprensión de cómo diferentes variables se relacionan entre sí.
128
- """
129
- st.markdown(explicacion_graficos)
130
-
131
- # Explicación en audio precargada
132
- st.subheader("AI Podcast del libro (EN)")
133
-
134
- # Ruta del archivo de audio precargado
135
- audio_path = "Napkin_podcast.mp3" # Cambia esta ruta al archivo correcto
136
- audio_file = open(audio_path, "rb").read() # Cargar el archivo de audio
137
-
138
- # Reproducir el archivo de audio
139
- st.audio(audio_file, format="audio/mp3")
140
-
141
- # Sidebar
142
- st.sidebar.title("Datos de Entrada")
143
- # Función para cargar imagen y convertirla en base64
144
- def get_image_base64(image):
145
- if isinstance(image, str): # Si la imagen es una ruta
146
- with open(image, "rb") as image_file:
147
- return "data:image/png;base64," + base64.b64encode(image_file.read()).decode("utf-8")
148
- elif isinstance(image, Image.Image): # Si la imagen es un objeto PIL
149
- buffered = io.BytesIO()
150
- image = image.resize((150, 150)) # Ajustar tamaño de la imagen a 150x150 píxeles
151
- image.save(buffered, format="PNG") # Convertir imagen a bytes
152
- return "data:image/png;base64," + base64.b64encode(buffered.getvalue()).decode("utf-8")
153
- return None
154
-
155
-
156
- def crear_grafico_quien_que(nombre, categorias, imagen):
157
- # Crear un network graph
158
- G = nx.Graph()
159
-
160
- # Agregar nodo principal (la persona o entidad)
161
- G.add_node(nombre, type='central', shape='circularImage', image=get_image_base64(imagen) if imagen else None)
162
-
163
- # Agregar nodos para las categorías y atributos
164
- for categoria, atributos in categorias.items():
165
- for atributo in atributos:
166
- G.add_node(atributo, type=categoria)
167
- G.add_edge(nombre, atributo) # Conectar atributo con el nodo central
168
-
169
- # Conectar atributos entre sí si están en la misma categoría
170
- for otro_atributo in atributos:
171
- if atributo != otro_atributo:
172
- G.add_edge(atributo, otro_atributo)
173
-
174
- # Crear visualización con pyvis
175
- person_net = Network(
176
- height='600px',
177
- width='100%',
178
- bgcolor='#222222',
179
- font_color='white'
180
- )
181
-
182
- # Definir colores para las categorías
183
- categoria_colores = {
184
- 'Categoría 1': '#1f77b4', # Azul oscuro
185
- 'Categoría 2': '#2ca02c', # Verde oscuro
186
- 'Categoría 3': '#9467bd', # Púrpura oscuro
187
- 'Categoría 4': '#bcbd22', # Amarillo oliva oscuro
188
- }
189
-
190
- # Configurar los nodos con imágenes, colores y tamaños
191
- for node in G.nodes(data=True):
192
- node_categoria = node[1].get('type', 'central')
193
- color = categoria_colores.get(node_categoria, 'gray') # Color por defecto si no se encuentra la categoría
194
-
195
- # Determinar si el nodo es el central o uno de los otros
196
- if node_categoria == 'central':
197
- node_options = {
198
- "label": node[0],
199
- "shape": "circularImage" if node[1].get('image', '') else "circle",
200
- "image": node[1].get('image', ''), # Convertir a base64 si es una imagen
201
- "color": color,
202
- "size": 80, # Tamaño más grande para el nodo central
203
- "fixed": {"x": False, "y": False} # Mantener el nodo fijo en tamaño, no en posición
204
- }
205
- else:
206
- node_options = {
207
- "label": node[0],
208
- "shape": "circle",
209
- "color": color,
210
- "size": 10, # Tamaño más pequeño para los nodos secundarios
211
- "fixed": {"x": False, "y": False} # Mantener el nodo fijo en tamaño, no en posición
212
- }
213
- person_net.add_node(node[0], **node_options)
214
-
215
- # Agregar edges
216
- for edge in G.edges():
217
- person_net.add_edge(edge[0], edge[1])
218
-
219
- # Configurar layout del grafo con centrado
220
- person_net.repulsion(
221
- node_distance=200,
222
- central_gravity=0.33,
223
- spring_length=100,
224
- spring_strength=0.10,
225
- damping=0.95
226
- )
227
-
228
- # Agregar opciones para mantener el gráfico centrado
229
- person_net.set_options("""
230
- var options = {
231
- "physics": {
232
- "stabilization": {
233
- "enabled": true,
234
- "iterations": 1000
235
- },
236
- "minVelocity": 0.75
237
- },
238
- "interaction": {
239
- "dragNodes": true,
240
- "zoomView": true
241
- }
242
- }
243
- """)
244
-
245
- # Guardar y mostrar grafo en HTML
246
- path = '/tmp'
247
- person_net.save_graph(f'{path}/pyvis_graph.html')
248
-
249
- with open(f'{path}/pyvis_graph.html', 'r', encoding='utf-8') as HtmlFile:
250
- graph_html = HtmlFile.read()
251
-
252
- # Mostrar grafo en la app con Streamlit Components con ancho responsivo
253
- components.html(graph_html, height=600, width=800)
254
-
255
- # Pestaña: Quién/Qué
256
- with tabs[1]:
257
- st.header("¿Quién/Qué?")
258
- st.sidebar.subheader("Ingresos de datos del ¿Quién/Qué?:")
259
-
260
- # Entrada de texto para el nombre
261
- nombre = st.sidebar.text_input("Ingrese el nombre:", "Ai-ngineering")
262
-
263
- # Imagen predeterminada si no se carga ninguna
264
- imagen_predeterminada = "perfil.jpg"
265
-
266
- # Cargar una imagen
267
- imagen_subida = st.sidebar.file_uploader("Cargue una foto", type=["png", "jpg", "jpeg"])
268
- imagen = Image.open(imagen_subida) if imagen_subida else imagen_predeterminada
269
-
270
- # Ingreso de categorías y atributos
271
- st.sidebar.write("Ingrese las diferentes categorías y atributos:")
272
-
273
- # Crear DataFrame editable para las categorías
274
- example_data = {
275
- "Categoría 1": ["Ingeniero", "Big Data", "MBA"],
276
- "Categoría 2": ["Procesos", "Razonamiento", "Cálculo", "Datos", "Storytelling", "Programación"],
277
- "Categoría 3": ["Esposo", "Padre", "Hijo", "Músico"],
278
- "Categoría 4": ["Venezuela", "Colombia", "Ecuador", "México", "Brasil"]
279
- }
280
- df_categorias = pd.DataFrame.from_dict(example_data, orient='index').transpose()
281
-
282
- # Data editor en el sidebar
283
- df_categorias = st.sidebar.data_editor(df_categorias, num_rows="dynamic", key="df_quienque")
284
-
285
- # Convertir el DataFrame a un diccionario de listas para categorías
286
- categorias = {col: df_categorias[col].dropna().tolist() for col in df_categorias.columns}
287
-
288
- # Mostrar el gráfico solo si se ha ingresado un nombre
289
- if nombre:
290
- crear_grafico_quien_que(nombre, categorias, imagen)
291
- # Agregar un enlace al final de la pestaña
292
- st.markdown("Para más información consulta en: [Interactive network visualizations](https://pyvis.readthedocs.io/en/latest/)")
293
-
294
- # Función para crear gráfico de Pareto
295
- def crear_grafico_pareto(datos):
296
- # Ordenar los datos de mayor a menor
297
- datos = datos.sort_values(by='Valor', ascending=False).reset_index(drop=True)
298
-
299
- # Calcular el valor acumulado y su porcentaje
300
- datos['Acumulado'] = datos['Valor'].cumsum()
301
- total = datos['Valor'].sum()
302
- datos['% Valor'] = datos['Valor'] / total * 100
303
- datos['% Acumulado'] = datos['Acumulado'] / total * 100
304
-
305
- # Crear gráfico de Pareto
306
- fig, ax1 = plt.subplots(figsize=(10, 6))
307
-
308
- # Crear colormap para las barras
309
- norm = mcolors.Normalize(vmin=datos['Valor'].min(), vmax=datos['Valor'].max())
310
- cmap = plt.get_cmap('inferno') # Puedes cambiar 'inferno' por otro colormap
311
-
312
- # Ajustar el ancho de las barras para que no haya separación
313
- ancho_barras = 1.0
314
-
315
- # Gráfico de barras con colores de heatmap
316
- bars = ax1.bar(datos['Categoría'], datos['% Valor'], color=cmap(norm(datos['Valor'])), width=ancho_barras)
317
- ax1.set_ylabel('Porcentaje del Valor')
318
- ax1.set_xlabel('Categoría')
319
- ax1.tick_params(axis='x', rotation=45)
320
- ax1.set_ylim(0, 100) # Configurar el rango del eje y
321
-
322
- # Crear un segundo eje para la línea acumulada
323
- ax2 = ax1.twinx()
324
- ax2.plot(datos['Categoría'], datos['% Acumulado'], color='red', marker='D', linestyle='-', label='Porcentaje Acumulado')
325
- ax2.axhline(80, color='gray', linestyle='--') # Línea que marca el 80%
326
- ax2.set_ylabel('Porcentaje Acumulado')
327
- ax2.set_ylim(0, 100) # Configurar el rango del eje y
328
-
329
- # Añadir leyenda y etiquetas
330
- plt.title('Gráfico de Pareto')
331
- ax2.legend(loc='best')
332
-
333
- # Añadir barra de color
334
- sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
335
- sm.set_array([])
336
- cbar = plt.colorbar(sm, ax=ax1, orientation='vertical')
337
- cbar.set_label('Valor')
338
-
339
- st.pyplot(fig)
340
-
341
- # Pestaña: Cuánto (modificada)
342
- with tabs[2]:
343
- st.header("¿Cuánto?")
344
- # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
345
- st.sidebar.subheader("Ingresos de datos del ¿Cuánto?:")
346
-
347
- example_data = {
348
- "Categoría": ["A", "B", "C", "D", "E", "F"],
349
- "Valor": [10, 40, 20, 15, 5, 2]
350
- }
351
- example_df = pd.DataFrame(example_data)
352
-
353
- # Mostrar ejemplo de datos en el sidebar
354
- st.sidebar.write("Ingrese las categorías y valores en el siguiente formato:")
355
-
356
- # Permitir al usuario editar el DataFrame
357
- df_cuanto = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_cuanto")
358
-
359
- # Verificar si el DataFrame tiene datos antes de generar el gráfico
360
- if not df_cuanto.empty:
361
- # Llamada a la función para crear el gráfico de Pareto
362
- crear_grafico_pareto(df_cuanto)
363
- st.markdown("Para más información consulta en: [Pareto chart](https://asq.org/quality-resources/pareto)")
364
-
365
- # Función para obtener las coordenadas de un lugar utilizando OSMNX
366
- def obtener_coordenadas_lugar(lugar):
367
- return ox.geocode(lugar)
368
-
369
- # Función para calcular la distancia entre dos puntos (coordenadas)
370
- def calcular_distancia(origen, destino):
371
- return geodesic(origen, destino).kilometers
372
-
373
- # Función para graficar las ubicaciones en un mapa y mostrar la distancia, conectando los puntos con una línea
374
- def crear_grafico_lugares(origen, destino, coordenadas_origen, coordenadas_destino):
375
- # Calcular la distancia entre las dos ubicaciones
376
- distancia = calcular_distancia(coordenadas_origen, coordenadas_destino)
377
-
378
- # Crear un DataFrame para los dos puntos
379
- df = pd.DataFrame({
380
- "Lugar": [origen, destino],
381
- "Latitud": [coordenadas_origen[0], coordenadas_destino[0]],
382
- "Longitud": [coordenadas_origen[1], coordenadas_destino[1]]
383
- })
384
-
385
- # Crear el mapa con los dos puntos
386
- fig = px.scatter_mapbox(
387
- df, lat="Latitud", lon="Longitud", zoom=5, height=600, text="Lugar"
388
- )
389
-
390
- # Añadir la línea que conecta los dos puntos
391
- fig.add_scattermapbox(
392
- lat=[coordenadas_origen[0], coordenadas_destino[0]], # Latitudes de origen y destino
393
- lon=[coordenadas_origen[1], coordenadas_destino[1]], # Longitudes de origen y destino
394
- mode="lines",
395
- line=dict(width=2, color="blue"),
396
- name="Línea de conexión"
397
- )
398
-
399
- fig.update_layout(mapbox_style="open-street-map")
400
-
401
- # Mostrar el gráfico en la aplicación
402
- st.plotly_chart(fig)
403
-
404
- # Mostrar la distancia entre los dos lugares
405
- st.write(f"La distancia entre {origen} y {destino} es de aproximadamente {distancia:.2f} km.")
406
-
407
- # Pestaña: Dónde (modificada)
408
- with tabs[3]:
409
- st.header("¿Dónde?")
410
- # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
411
- st.sidebar.subheader("Ingresos de datos del ¿Dónde?:")
412
- # Ingreso de las ubicaciones de origen y destino
413
- origen = st.sidebar.text_input("Ingrese el lugar de origen:", "Valencia, Venezuela")
414
- destino = st.sidebar.text_input("Ingrese el lugar de destino:", "Medellín, Colombia")
415
-
416
- # Mostrar el gráfico de los puntos y la distancia si se ingresan ambas ubicaciones
417
- if origen and destino:
418
- try:
419
- coordenadas_origen = obtener_coordenadas_lugar(origen)
420
- coordenadas_destino = obtener_coordenadas_lugar(destino)
421
-
422
- crear_grafico_lugares(origen, destino, coordenadas_origen, coordenadas_destino)
423
- except Exception as e:
424
- st.error(f"Error al obtener las coordenadas: {e}")
425
-
426
- # Función para crear gráfico de Gantt usando Plotly Timeline
427
- def crear_grafico_gantt(eventos):
428
- # Crear el gráfico de Gantt con Plotly
429
- fig = px.timeline(
430
- eventos,
431
- x_start="Fecha de Inicio",
432
- x_end="Fecha de Fin",
433
- y="Evento",
434
- color='Categoría', # Categorías para colorear las tareas
435
- title='Gráfico de Gantt de Eventos'
436
- )
437
-
438
- # Añadir el porcentaje de realización como etiquetas en el gráfico
439
- for i, row in eventos.iterrows():
440
- fig.add_annotation(
441
- x=row['Fecha de Fin'],
442
- y=row['Evento'],
443
- text=f"{row['Porcentaje de Realización']}% completado",
444
- showarrow=False,
445
- font=dict(size=12, color='white'),
446
- bgcolor="green" if row['Porcentaje de Realización'] == 100 else "blue",
447
- bordercolor="black",
448
- xanchor='left'
449
- )
450
-
451
- fig.update_yaxes(autorange="reversed") # Invertir el eje Y
452
- fig.update_layout(height=600, width=900) # Ajustar tamaño del gráfico
453
- st.plotly_chart(fig)
454
-
455
- with tabs[4]:
456
- st.header("¿Cuándo?")
457
-
458
- # Ejemplo de datos ampliados en DataFrame
459
- st.sidebar.subheader("Ingresos de datos del ¿Cuándo?:")
460
- example_data = {
461
- "Evento": ["Proyecto Inicio", "Análisis de Requisitos", "Desarrollo", "Pruebas", "Implementación", "Cierre"],
462
- "Fecha de Inicio": ["2023-01-01", "2023-02-01", "2023-06-01", "2023-08-01", "2023-10-01", "2023-11-01"],
463
- "Fecha de Fin": ["2023-02-01", "2023-06-01", "2023-08-01", "2023-10-01", "2023-11-01", "2023-12-01"],
464
- "Categoría": ["Planificación", "Análisis", "Desarrollo", "Pruebas", "Implementación", "Cierre"],
465
- "Porcentaje de Realización": [100, 100, 80, 50, 20, 0] # Porcentajes de realización
466
- }
467
- example_df = pd.DataFrame(example_data)
468
-
469
- # Ingreso de datos de tareas mediante un DataFrame editable
470
- st.sidebar.write("Ingrese los datos de las tareas en el siguiente formato:")
471
- df_tareas = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_gantt")
472
-
473
- # Verificar que el DataFrame tiene datos para generar el gráfico
474
- if not df_tareas.empty:
475
- # Convertir las columnas de fechas a formato de fecha
476
- df_tareas['Fecha de Inicio'] = pd.to_datetime(df_tareas['Fecha de Inicio'])
477
- df_tareas['Fecha de Fin'] = pd.to_datetime(df_tareas['Fecha de Fin'])
478
-
479
- # Crear gráfico Gantt a partir del DataFrame ingresado
480
- crear_grafico_gantt(df_tareas)
481
-
482
- st.markdown("Para más información consulta en: [Gantt chart](https://www.apm.org.uk/resources/find-a-resource/gantt-chart/)")
483
-
484
-
485
- # # Función para crear gráfico de Gantt usando Plotly Timeline
486
- # def crear_grafico_gantt(eventos):
487
- # fig = px.timeline(
488
- # eventos,
489
- # x_start="Fecha de Inicio",
490
- # x_end="Fecha de Fin",
491
- # y="Evento",
492
- # color='Categoría', # Esto es opcional si deseas agregar categorías para colorear las tareas
493
- # title='Gráfico de Gantt de Eventos'
494
- # )
495
-
496
- # fig.update_yaxes(autorange="reversed") # Invertir el eje y para que las tareas se vean de arriba hacia abajo
497
- # fig.update_layout(height=600, width=900) # Ajustar el tamaño del gráfico
498
- # st.plotly_chart(fig)
499
-
500
- # with tabs[4]:
501
- # st.header("¿Cuándo?")
502
-
503
- # # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
504
- # st.sidebar.subheader("Ingresos de datos del ¿Cuándo?:")
505
- # example_data = {
506
- # "Evento": ["Proyecto Inicio", "Desarrollo", "Fin"],
507
- # "Fecha de Inicio": ["2023-01-01", "2023-06-01", "2023-09-01"],
508
- # "Fecha de Fin": ["2023-06-01", "2023-09-01", "2023-12-01"],
509
- # "Categoría": ["Planificación", "Ejecución", "Cierre"]
510
- # }
511
- # example_df = pd.DataFrame(example_data)
512
- # #st.sidebar.write(example_df)
513
-
514
- # # Ingreso de datos de tareas mediante un DataFrame editable
515
- # st.sidebar.write("Ingrese los datos de las tareas en el siguiente formato:")
516
- # df_tareas = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_gantt")
517
-
518
- # # Verificar que el DataFrame tiene datos para generar el gráfico
519
- # if not df_tareas.empty:
520
- # # Convertir las columnas de fechas a formato de fecha
521
- # df_tareas['Fecha de Inicio'] = pd.to_datetime(df_tareas['Fecha de Inicio'])
522
- # df_tareas['Fecha de Fin'] = pd.to_datetime(df_tareas['Fecha de Fin'])
523
-
524
- # # Crear gráfico Gantt a partir del DataFrame ingresado
525
- # crear_grafico_gantt(df_tareas)
526
- # st.markdown("Para más información consulta en: [Gantt chart](https://www.apm.org.uk/resources/find-a-resource/gantt-chart/)")
527
-
528
- # Pestaña: Cómo (modificada con el diagrama de flujo con simbología ANSI)
529
- with tabs[5]:
530
- st.header("¿Cómo?")
531
- # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
532
- st.sidebar.subheader("Ingresos de datos del ¿Cómo?:")
533
- # Ingreso de datos mediante un DataFrame editable
534
- st.sidebar.write("Modifique los datos de acuerdo a su necesidad para el diagrama de flujo:")
535
-
536
- # Función para generar el diagrama de flujo con colores llamativos, texto blanco y proporciones ajustadas
537
- def generate_flowchart(steps):
538
- dot = graphviz.Digraph()
539
-
540
- # Colores por tipo de actividad, más llamativos
541
- color_map = {
542
- "Inicio / Fin": "#32CD32", # LimeGreen
543
- "Operación / Actividad": "#FFD700", # Gold
544
- "Documento": "#1E90FF", # DodgerBlue
545
- "Datos": "#FF6347", # Tomato
546
- "Almacenamiento / Archivo": "#8A2BE2", # BlueViolet
547
- "Decisión": "#FF4500" # OrangeRed
548
- }
549
-
550
- # Agregar nodos de inicio y fin fijos con colores llamativos y texto blanco
551
- dot.node("Inicio", label="Inicio", shape="oval", style="filled", fillcolor=color_map["Inicio / Fin"], fontcolor="white")
552
- dot.node("Fin", label="Fin", shape="oval", style="filled", fillcolor=color_map["Inicio / Fin"], fontcolor="white")
553
-
554
- # Agregar los pasos del diagrama de flujo
555
- for step in steps:
556
- fillcolor = color_map.get(step['tipo'], 'white')
557
- dot.node(step['id'], label=step['etiqueta'], shape=step['forma'], style="filled", fillcolor=fillcolor, fontcolor="white")
558
-
559
- # Conectar los pasos
560
- dot.edge("Inicio", steps[0]['id']) # Conexión desde el inicio al primer paso
561
- for step in steps:
562
- if 'siguiente' in step:
563
- for next_step_id in step['siguiente']:
564
- dot.edge(step['id'], next_step_id)
565
- dot.edge(steps[-1]['id'], "Fin") # Conexión del último paso al fin
566
-
567
- # Ajustar proporciones de ancho y alto (más alto que ancho)
568
- dot.attr(size="6,8", ratio="fill")
569
-
570
- return dot
571
-
572
- # Ejemplo precargado utilizando todos los símbolos y colores llamativos
573
- def cargar_proceso_ejemplo():
574
- return [
575
- {'id': 'Paso_1', 'etiqueta': 'Iniciar proceso', 'tipo': 'Operación / Actividad', 'forma': 'rectangle', 'siguiente': ['Paso_2']},
576
- {'id': 'Paso_2', 'etiqueta': 'Recibir documento', 'tipo': 'Documento', 'forma': 'parallelogram', 'siguiente': ['Paso_3']},
577
- {'id': 'Paso_3', 'etiqueta': 'Tomar decisión', 'tipo': 'Decisión', 'forma': 'diamond', 'siguiente': ['Paso_4', 'Paso_5']},
578
- {'id': 'Paso_4', 'etiqueta': 'Almacenar documento', 'tipo': 'Almacenamiento / Archivo', 'forma': 'invtriangle', 'siguiente': ['Paso_6']},
579
- {'id': 'Paso_5', 'etiqueta': 'Procesar datos', 'tipo': 'Datos', 'forma': 'parallelogram', 'siguiente': ['Paso_6']},
580
- {'id': 'Paso_6', 'etiqueta': 'Finalizar proceso', 'tipo': 'Operación / Actividad', 'forma': 'rectangle', 'siguiente': []},
581
- ]
582
-
583
- # Cargar o editar un proceso de ejemplo por defecto
584
- usar_ejemplo = st.sidebar.checkbox('Usar proceso de ejemplo', value=True)
585
-
586
- if usar_ejemplo:
587
- pasos = cargar_proceso_ejemplo()
588
- else:
589
- pasos = []
590
-
591
- # Seleccionar número de pasos (si no se usa el proceso de ejemplo)
592
- num_pasos = st.sidebar.number_input('Número de pasos', min_value=1, max_value=20, value=len(pasos) if usar_ejemplo else 1)
593
-
594
- # IDs automáticos para los pasos
595
- ids_disponibles = [f'Paso_{i + 1}' for i in range(num_pasos)]
596
-
597
- for i in range(num_pasos):
598
- st.sidebar.subheader(f'Paso {i + 1}')
599
-
600
- # Datos para los pasos existentes o nuevos
601
- if i < len(pasos):
602
- paso = pasos[i]
603
- etiqueta = st.sidebar.text_input(f'Etiqueta para el Paso {i + 1}', value=paso['etiqueta'])
604
- tipo = st.sidebar.selectbox(f'Tipo de actividad del Paso {i + 1}',
605
- ['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'],
606
- index=['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'].index(paso['tipo']))
607
- siguientes_pasos = st.sidebar.multiselect(f'Pasos siguientes desde el Paso {i + 1}', ids_disponibles, default=paso['siguiente'])
608
- else:
609
- etiqueta = st.sidebar.text_input(f'Etiqueta para el Paso {i + 1}', value=f'Paso {i + 1}')
610
- tipo = st.sidebar.selectbox(f'Tipo de actividad del Paso {i + 1}', ['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'])
611
- siguientes_pasos = st.sidebar.multiselect(f'Pasos siguientes desde el Paso {i + 1}', ids_disponibles)
612
-
613
- # Definir la forma según el tipo de actividad
614
- forma_map = {
615
- 'Inicio / Fin': 'oval',
616
- 'Operación / Actividad': 'rectangle',
617
- 'Documento': 'parallelogram',
618
- 'Datos': 'parallelogram',
619
- 'Almacenamiento / Archivo': 'invtriangle',
620
- 'Decisión': 'diamond'
621
- }
622
- forma = forma_map.get(tipo, 'rectangle')
623
-
624
- # Actualizar o agregar el paso
625
- if i < len(pasos):
626
- pasos[i] = {
627
- 'id': f'Paso_{i + 1}',
628
- 'etiqueta': etiqueta,
629
- 'tipo': tipo,
630
- 'forma': forma,
631
- 'siguiente': siguientes_pasos
632
- }
633
- else:
634
- pasos.append({
635
- 'id': f'Paso_{i + 1}',
636
- 'etiqueta': etiqueta,
637
- 'tipo': tipo,
638
- 'forma': forma,
639
- 'siguiente': siguientes_pasos
640
- })
641
-
642
- # Mostrar el diagrama de flujo generado
643
- st.graphviz_chart(generate_flowchart(pasos))
644
- st.markdown("Para más información consulta en: [Flowchart](https://asq.org/quality-resources/flowchart)")
645
-
646
- # Pestaña: Por qué
647
- with tabs[6]:
648
- st.header("¿Por qué?")
649
- st.sidebar.subheader("Ingresos de datos del ¿Por qué?:")
650
-
651
- # Ejemplo de datos en DataFrame
652
- example_data = {
653
- "Categoría": ["Idea A", "Idea B", "Idea C", "Idea D", "Idea E", "Idea F"],
654
- "Variable x": [10, 15, 5, 30, 2, 60],
655
- "Variable y": [3, 5, 2, 10, 1, 15],
656
- "Variable z": [1000, 1500, 100, 500, 0, 250]
657
- }
658
- example_df = pd.DataFrame(example_data)
659
-
660
- # Ingreso de datos mediante un DataFrame editable
661
- st.sidebar.write("Modifique los datos de acuerdo a su necesidad:")
662
- df_porque = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_porque")
663
-
664
- # Selección de columnas para el gráfico
665
- columnas = df_porque.columns.tolist()
666
-
667
- x_col = st.sidebar.selectbox("Seleccione la variable para el eje X:", columnas, index=1)
668
- y_col = st.sidebar.selectbox("Seleccione la variable para el eje Y:", columnas, index=2)
669
- size_col = st.sidebar.selectbox("Seleccione la variable para el tamaño de las burbujas:", columnas, index=3)
670
- color_col = st.sidebar.selectbox("Seleccione la variable para el color de las burbujas:", columnas, index=3)
671
-
672
- # Verificar si los nombres ingresados son válidos en el DataFrame
673
- if not df_porque.empty:
674
- if all(col in df_porque.columns for col in [x_col, y_col, size_col, color_col]):
675
- # Crear gráfico de burbujas con Plotly si los nombres de las columnas son válidos
676
- fig = px.scatter(
677
- df_porque,
678
- x=x_col, # Variable en el eje X
679
- y=y_col, # Variable en el eje Y
680
- size=size_col, # Tamaño de las burbujas
681
- text="Categoría", # Etiquetas para las burbujas
682
- color=color_col, # Color basado en la variable seleccionada
683
- title="Gráfico de Burbujas Personalizado",
684
- size_max=10, # Ajuste del tamaño máximo de las burbujas
685
- color_continuous_scale=px.colors.sequential.Plasma # Escala de colores
686
- )
687
-
688
- # Personalizar diseño
689
- fig.update_traces(marker=dict(sizemode='diameter', opacity=0.8, line=dict(width=2, color='DarkSlateGrey')),
690
- selector=dict(mode='markers+text'))
691
- fig.update_layout(height=600, width=900) # Ajustar tamaño del gráfico
692
-
693
- # Mostrar gráfico en la aplicación
694
- st.plotly_chart(fig)
695
- else:
696
- st.error("Algunas de las columnas ingresadas no existen en el DataFrame. Verifique los nombres.")
697
-
698
- st.markdown("Para más información consulta en: [Bubble chart](https://plotly.com/python/bubble-charts/)")
699
-
700
- # Pestaña: Acerca de mí
701
- with tabs[7]:
702
- #st.header("Acerca de mí")
703
-
704
- st.subheader("José Miguel Aguilar Torrealba")
705
- st.write("**Chemical Engineer | MBA Digital Business Administration | Innovation & CI Facilitator | Coaching | Citizen Data Scientist | Lean Six Sigma | Integrated Management Systems Auditor**")
706
-
707
- st.subheader("Resumen profesional")
708
- st.write("""
709
- 20 años de experiencia en los sectores químico, educativo y de alimentos, me especializo en modelos de mejora continua, excelencia operacional y proyectos de tecnología e infraestructura. He trabajado en la industria y en el ámbito académico, facilitando y auditoriando sistemas de gestión.
710
- """)
711
-
712
- st.subheader("Experiencia Profesional")
713
- st.write("""
714
- - **Continuous Improvement Manager** en AkzoNobel (abr. 2022 - actualidad): Implementación de modelos globales de mejora continua, gestión de iniciativas de ahorro y transferencia de buenas prácticas.
715
-
716
- - **SR Continuous Improvement Coordinator** en Andercol SAS (feb. 2016 - abr. 2022): Diseño de estrategias de mejora, Industria 4.0 y Big Data, y gestión de la excelencia operacional regional.
717
-
718
- - **Senior Process & Project Coordinator** en C.A. Venezolana de Pinturas (feb. 2014 - ene. 2016): Control de productos y procesos, reducción del impacto ambiental y optimización de la producción.
719
-
720
- - **Process Engineer** en C.A. QUIMICA INTEGRADA -INTEQUIM- (may. 2010 - feb. 2014): Coordinación de mejoras en procesos y equipos para optimizar la productividad.
721
-
722
- - **Seminary Professor** en Universidad de Carabobo (sept. 2009 - sept. 2010): Facilitación de nuevas tendencias en ingeniería química y mejoramiento continuo.
723
-
724
- - **CI & Quality Advisor** en Palma Products International C.A. (nov. 2009 - abr. 2010): Reingeniería del SGC y mejora continua.
725
-
726
- - **Continuous Improvement Engineer** en Ajegroup (ene. 2005 - oct. 2008): Implementación de metodologías Lean Manufacturing, Kaizen, y TPM.
727
-
728
- - **Process Engineer** en Ajegroup (ago. 2006 - nov. 2006): Mejora de sistemas hidráulicos y procesos de producción.
729
-
730
- - **Quality Auditor** en Ajegroup (nov. 2005 - jul. 2006): Control y auditoría de calidad.
731
-
732
- - **Continuous Improvement Intern** en Ajegroup (ago. 2004 - nov. 2005): Participación en control estadístico de procesos y Lean Six Sigma.
733
-
734
- - **Analytical Chemistry Trainer** en Universidad de Carabobo (mar. 2004 - nov. 2005): Entrenador en química analítica y asistente del profesor.
735
- """)
736
-
737
- st.subheader("Formación")
738
- st.write("""
739
- - **Grand Master MBA Dirección de Negocio Digital**, TECH Universidad (ene. 2021 - feb. 2023)
740
-
741
- - **Ingeniero Químico**, Universidad de Carabobo (ago. 2000 - ago. 2007)
742
-
743
- - **Certificaciones y Formación Adicional**:
744
- - Lean Six Sigma
745
- - Big Data
746
- - Transformación Digital
747
- - Control de Procesos Químicos
748
- - Auditoría de Sistemas Integrados de Gestión
749
- """)
750
-
751
- # Agregar un enlace al final de la pestaña
752
- st.markdown("Para más información consulta en: [LinkedIn](https://www.linkedin.com/in/josemaguilar/)")
 
1
+ import streamlit as st
2
+ import networkx as nx
3
+ from pyvis.network import Network
4
+ import streamlit.components.v1 as components
5
+ from PIL import Image
6
+ import io
7
+ import base64
8
+ import pandas as pd
9
+ import matplotlib.pyplot as plt
10
+ import plotly.express as px
11
+ import osmnx as ox
12
+ from geopy.distance import geodesic
13
+ import streamlit.components.v1 as components
14
+ import matplotlib.colors as mcolors
15
+ import plotly.graph_objects as go
16
+ import graphviz
17
+
18
+ # Configuración de la aplicación
19
+ st.set_page_config(page_title="Visualización de Marcos SCVID", layout="wide")
20
+ # Display the image above the title
21
+ st.image('Napkin App.png', use_column_width=True)
22
+
23
+ # Agregar una nueva pestaña para la reseña del libro
24
+ tabs = st.tabs([ "Reseña del Libro", "Quién/Qué", "Cuánto", "Dónde", "Cuándo", "Cómo", "Por qué","Acerca de mí"])
25
+
26
+ # Pestaña: Reseña del libro
27
+ with tabs[0]:
28
+
29
+ resumen = """
30
+ ## Resumen del libro: "The Back of the Napkin de Dan Roam"
31
+
32
+ Este resumen se centra en el marco de 6 preguntas, el SQVID y las estrategias visuales recomendadas en el libro
33
+ "The Back of the Napkin" de Dan Roam para resolver problemas y vender ideas.
34
+
35
+ El libro argumenta que el pensamiento visual, utilizando dibujos sencillos, es una herramienta poderosa para la
36
+ resolución de problemas y la comunicación efectiva. Roam explica que cualquier problema se puede aclarar con una
37
+ imagen, y cualquier imagen se puede crear utilizando un conjunto simple de herramientas y reglas.
38
+
39
+ ### El Marco de las 6 Preguntas (6W)
40
+
41
+ Roam propone un marco de seis preguntas fundamentales, conocidas como las 6W, para analizar y abordar cualquier problema:
42
+
43
+ * **Quién/Qué (Who/What):** Define los actores y objetos involucrados.
44
+ * **Cuánto (How Much):** Analiza las cantidades, medidas y datos.
45
+ * **Dónde (Where):** Ubica el problema en un contexto espacial.
46
+ * **Cuándo (When):** Establece un marco temporal para el problema.
47
+ * **Cómo (How):** Describe los procesos y relaciones de causa y efecto.
48
+ * **Por qué (Why):** Explora las razones, motivaciones y causas subyacentes.
49
+
50
+ Estas preguntas no solo ayudan a comprender el problema, sino que también guían la elección del gráfico o estrategia
51
+ visual más efectiva para comunicarlo.
52
+
53
+ ### El SQVID como Herramienta de Imaginación
54
+
55
+ El SQVID es un acrónimo que representa cinco preguntas para activar la imaginación visual y explorar diferentes
56
+ perspectivas de una idea:
57
+
58
+ * **Simple vs. Elaborado:** ¿Se necesita una representación simple o detallada?
59
+ * **Cualitativo vs. Cuantitativo:** ¿Se enfatizan las características o los datos numéricos?
60
+ * **Visión vs. Ejecución:** ¿Se busca inspirar o mostrar los pasos concretos?
61
+ * **Individual vs. Comparación:** ¿Se presenta una sola idea o se compara con otras?
62
+ * **Cambio vs. Status Quo:** ¿Se propone una transformación o se describe la situación actual?
63
+
64
+ Al responder estas preguntas, se pueden generar múltiples representaciones visuales de una idea y elegir la más
65
+ efectiva para la audiencia y el objetivo.
66
+
67
+ ### Gráficos y Estrategias Visuales Recomendadas
68
+
69
+ El libro presenta seis marcos visuales principales, cada uno correspondiente a una de las 6W:
70
+
71
+ * **Retrato:** Para mostrar **quién/qué** (ej. un organigrama, un diagrama de personajes).
72
+ * **Gráfico:** Para visualizar **cuánto** (ej. gráfico de barras, gráfico circular).
73
+ * **Mapa:** Para representar **dónde** (ej. mapa mental, diagrama de flujo de procesos).
74
+ * **Línea de Tiempo:** Para ilustrar **cuándo** (ej. cronograma, diagrama de Gantt).
75
+ * **Diagrama de Flujo:** Para explicar **cómo** (ej. diagrama de flujo de trabajo, algoritmo).
76
+ * **Gráfico de Múltiples Variables:** Para analizar **por qué** (ej. gráfico de dispersión, mapa de calor).
77
+
78
+ En resumen, "The Back of the Napkin" ofrece un enfoque práctico para utilizar el pensamiento visual como herramienta
79
+ para la resolución de problemas y la comunicación efectiva.
80
+ """
81
+
82
+ st.markdown(resumen)
83
+
84
+ st.subheader("Razones de la escogencia de los gráficos")
85
+
86
+ # Ejemplo de razones
87
+ explicacion_graficos = """
88
+ Los gráficos utilizados en esta aplicación se alinean con el marco visual propuesto por Dan Roam en "The Back of the Napkin".
89
+
90
+ 1. **Quién/Qué (Gráfico de Nodos):** Este gráfico permite mostrar las conexiones entre personas, estudios y habilidades, siguiendo la recomendación de usar retratos o gráficos de red para visualizar relaciones entre actores y objetos.
91
+
92
+ 2. **Cuánto (Gráfico de Pareto):** Este gráfico es ideal para visualizar cantidades y datos. En particular, resalta la importancia de unos pocos elementos clave sobre la mayoría, una estrategia visual efectiva para mostrar patrones cuantitativos.
93
+
94
+ 3. **Cómo (Diagrama de Flujo):** El uso de diagramas de flujo para mostrar procesos es una herramienta clara y directa para entender relaciones de causa y efecto.
95
+
96
+ 4. **Cuándo (Línea de Tiempo):** Para mostrar secuencias temporales, la línea de tiempo es el gráfico recomendado para organizar eventos de manera clara y ordenada.
97
+
98
+ 5. **Por qué (Gráfico de Burbujas):** El gráfico de burbujas permite analizar múltiples variables simultáneamente, destacando cómo las categorías se comparan en función de varias dimensiones (eje X, eje Y, tamaño y color de las burbujas). Esta visualización es útil para explorar las razones subyacentes y patrones complejos en los datos, facilitando la comprensión de cómo diferentes variables se relacionan entre sí.
99
+ """
100
+ st.markdown(explicacion_graficos)
101
+
102
+ # Explicación en audio precargada
103
+ st.subheader("AI Podcast del libro (EN)")
104
+
105
+ # Ruta del archivo de audio precargado
106
+ audio_path = "Napkin_podcast.mp3" # Cambia esta ruta al archivo correcto
107
+ audio_file = open(audio_path, "rb").read() # Cargar el archivo de audio
108
+
109
+ # Reproducir el archivo de audio
110
+ st.audio(audio_file, format="audio/mp3")
111
+
112
+ # Sidebar
113
+ st.sidebar.title("Datos de Entrada")
114
+ # Función para cargar imagen y convertirla en base64
115
+ def get_image_base64(image):
116
+ if isinstance(image, str): # Si la imagen es una ruta
117
+ with open(image, "rb") as image_file:
118
+ return "data:image/png;base64," + base64.b64encode(image_file.read()).decode("utf-8")
119
+ elif isinstance(image, Image.Image): # Si la imagen es un objeto PIL
120
+ buffered = io.BytesIO()
121
+ image = image.resize((150, 150)) # Ajustar tamaño de la imagen a 150x150 píxeles
122
+ image.save(buffered, format="PNG") # Convertir imagen a bytes
123
+ return "data:image/png;base64," + base64.b64encode(buffered.getvalue()).decode("utf-8")
124
+ return None
125
+
126
+
127
+ def crear_grafico_quien_que(nombre, categorias, imagen):
128
+ # Crear un network graph
129
+ G = nx.Graph()
130
+
131
+ # Agregar nodo principal (la persona o entidad)
132
+ G.add_node(nombre, type='central', shape='circularImage', image=get_image_base64(imagen) if imagen else None)
133
+
134
+ # Agregar nodos para las categorías y atributos
135
+ for categoria, atributos in categorias.items():
136
+ for atributo in atributos:
137
+ G.add_node(atributo, type=categoria)
138
+ G.add_edge(nombre, atributo) # Conectar atributo con el nodo central
139
+
140
+ # Conectar atributos entre sí si están en la misma categoría
141
+ for otro_atributo in atributos:
142
+ if atributo != otro_atributo:
143
+ G.add_edge(atributo, otro_atributo)
144
+
145
+ # Crear visualización con pyvis
146
+ person_net = Network(
147
+ height='600px',
148
+ width='100%',
149
+ bgcolor='#222222',
150
+ font_color='white'
151
+ )
152
+
153
+ # Definir colores para las categorías
154
+ categoria_colores = {
155
+ 'Categoría 1': '#1f77b4', # Azul oscuro
156
+ 'Categoría 2': '#2ca02c', # Verde oscuro
157
+ 'Categoría 3': '#9467bd', # Púrpura oscuro
158
+ 'Categoría 4': '#bcbd22', # Amarillo oliva oscuro
159
+ }
160
+
161
+ # Configurar los nodos con imágenes, colores y tamaños
162
+ for node in G.nodes(data=True):
163
+ node_categoria = node[1].get('type', 'central')
164
+ color = categoria_colores.get(node_categoria, 'gray') # Color por defecto si no se encuentra la categoría
165
+
166
+ # Determinar si el nodo es el central o uno de los otros
167
+ if node_categoria == 'central':
168
+ node_options = {
169
+ "label": node[0],
170
+ "shape": "circularImage" if node[1].get('image', '') else "circle",
171
+ "image": node[1].get('image', ''), # Convertir a base64 si es una imagen
172
+ "color": color,
173
+ "size": 80, # Tamaño más grande para el nodo central
174
+ "fixed": {"x": False, "y": False} # Mantener el nodo fijo en tamaño, no en posición
175
+ }
176
+ else:
177
+ node_options = {
178
+ "label": node[0],
179
+ "shape": "circle",
180
+ "color": color,
181
+ "size": 10, # Tamaño más pequeño para los nodos secundarios
182
+ "fixed": {"x": False, "y": False} # Mantener el nodo fijo en tamaño, no en posición
183
+ }
184
+ person_net.add_node(node[0], **node_options)
185
+
186
+ # Agregar edges
187
+ for edge in G.edges():
188
+ person_net.add_edge(edge[0], edge[1])
189
+
190
+ # Configurar layout del grafo con centrado
191
+ person_net.repulsion(
192
+ node_distance=200,
193
+ central_gravity=0.33,
194
+ spring_length=100,
195
+ spring_strength=0.10,
196
+ damping=0.95
197
+ )
198
+
199
+ # Agregar opciones para mantener el gráfico centrado
200
+ person_net.set_options("""
201
+ var options = {
202
+ "physics": {
203
+ "stabilization": {
204
+ "enabled": true,
205
+ "iterations": 1000
206
+ },
207
+ "minVelocity": 0.75
208
+ },
209
+ "interaction": {
210
+ "dragNodes": true,
211
+ "zoomView": true
212
+ }
213
+ }
214
+ """)
215
+
216
+ # Guardar y mostrar grafo en HTML
217
+ path = '/tmp'
218
+ person_net.save_graph(f'{path}/pyvis_graph.html')
219
+
220
+ with open(f'{path}/pyvis_graph.html', 'r', encoding='utf-8') as HtmlFile:
221
+ graph_html = HtmlFile.read()
222
+
223
+ # Mostrar grafo en la app con Streamlit Components con ancho responsivo
224
+ components.html(graph_html, height=600, width=800)
225
+
226
+ # Pestaña: Quién/Qué
227
+ with tabs[1]:
228
+ st.header("¿Quién/Qué?")
229
+ st.sidebar.subheader("Ingresos de datos del ¿Quién/Qué?:")
230
+
231
+ # Entrada de texto para el nombre
232
+ nombre = st.sidebar.text_input("Ingrese el nombre:", "Ai-ngineering")
233
+
234
+ # Imagen predeterminada si no se carga ninguna
235
+ imagen_predeterminada = "perfil.jpg"
236
+
237
+ # Cargar una imagen
238
+ imagen_subida = st.sidebar.file_uploader("Cargue una foto", type=["png", "jpg", "jpeg"])
239
+ imagen = Image.open(imagen_subida) if imagen_subida else imagen_predeterminada
240
+
241
+ # Ingreso de categorías y atributos
242
+ st.sidebar.write("Ingrese las diferentes categorías y atributos:")
243
+
244
+ # Crear DataFrame editable para las categorías
245
+ example_data = {
246
+ "Categoría 1": ["Ingeniero", "Big Data", "MBA"],
247
+ "Categoría 2": ["Procesos", "Razonamiento", "Cálculo", "Datos", "Storytelling", "Programación"],
248
+ "Categoría 3": ["Esposo", "Padre", "Hijo", "Músico"],
249
+ "Categoría 4": ["Venezuela", "Colombia", "Ecuador", "México", "Brasil"]
250
+ }
251
+ df_categorias = pd.DataFrame.from_dict(example_data, orient='index').transpose()
252
+
253
+ # Data editor en el sidebar
254
+ df_categorias = st.sidebar.data_editor(df_categorias, num_rows="dynamic", key="df_quienque")
255
+
256
+ # Convertir el DataFrame a un diccionario de listas para categorías
257
+ categorias = {col: df_categorias[col].dropna().tolist() for col in df_categorias.columns}
258
+
259
+ # Mostrar el gráfico solo si se ha ingresado un nombre
260
+ if nombre:
261
+ crear_grafico_quien_que(nombre, categorias, imagen)
262
+ # Agregar un enlace al final de la pestaña
263
+ st.markdown("Para más información consulta en: [Interactive network visualizations](https://pyvis.readthedocs.io/en/latest/)")
264
+
265
+ # Función para crear gráfico de Pareto
266
+ def crear_grafico_pareto(datos):
267
+ # Ordenar los datos de mayor a menor
268
+ datos = datos.sort_values(by='Valor', ascending=False).reset_index(drop=True)
269
+
270
+ # Calcular el valor acumulado y su porcentaje
271
+ datos['Acumulado'] = datos['Valor'].cumsum()
272
+ total = datos['Valor'].sum()
273
+ datos['% Valor'] = datos['Valor'] / total * 100
274
+ datos['% Acumulado'] = datos['Acumulado'] / total * 100
275
+
276
+ # Crear gráfico de Pareto
277
+ fig, ax1 = plt.subplots(figsize=(10, 6))
278
+
279
+ # Crear colormap para las barras
280
+ norm = mcolors.Normalize(vmin=datos['Valor'].min(), vmax=datos['Valor'].max())
281
+ cmap = plt.get_cmap('inferno') # Puedes cambiar 'inferno' por otro colormap
282
+
283
+ # Ajustar el ancho de las barras para que no haya separación
284
+ ancho_barras = 1.0
285
+
286
+ # Gráfico de barras con colores de heatmap
287
+ bars = ax1.bar(datos['Categoría'], datos['% Valor'], color=cmap(norm(datos['Valor'])), width=ancho_barras)
288
+ ax1.set_ylabel('Porcentaje del Valor')
289
+ ax1.set_xlabel('Categoría')
290
+ ax1.tick_params(axis='x', rotation=45)
291
+ ax1.set_ylim(0, 100) # Configurar el rango del eje y
292
+
293
+ # Crear un segundo eje para la línea acumulada
294
+ ax2 = ax1.twinx()
295
+ ax2.plot(datos['Categoría'], datos['% Acumulado'], color='red', marker='D', linestyle='-', label='Porcentaje Acumulado')
296
+ ax2.axhline(80, color='gray', linestyle='--') # Línea que marca el 80%
297
+ ax2.set_ylabel('Porcentaje Acumulado')
298
+ ax2.set_ylim(0, 100) # Configurar el rango del eje y
299
+
300
+ # Añadir leyenda y etiquetas
301
+ plt.title('Gráfico de Pareto')
302
+ ax2.legend(loc='best')
303
+
304
+ # Añadir barra de color
305
+ sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
306
+ sm.set_array([])
307
+ cbar = plt.colorbar(sm, ax=ax1, orientation='vertical')
308
+ cbar.set_label('Valor')
309
+
310
+ st.pyplot(fig)
311
+
312
+ # Pestaña: Cuánto (modificada)
313
+ with tabs[2]:
314
+ st.header("¿Cuánto?")
315
+ # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
316
+ st.sidebar.subheader("Ingresos de datos del ¿Cuánto?:")
317
+
318
+ example_data = {
319
+ "Categoría": ["A", "B", "C", "D", "E", "F"],
320
+ "Valor": [10, 40, 20, 15, 5, 2]
321
+ }
322
+ example_df = pd.DataFrame(example_data)
323
+
324
+ # Mostrar ejemplo de datos en el sidebar
325
+ st.sidebar.write("Ingrese las categorías y valores en el siguiente formato:")
326
+
327
+ # Permitir al usuario editar el DataFrame
328
+ df_cuanto = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_cuanto")
329
+
330
+ # Verificar si el DataFrame tiene datos antes de generar el gráfico
331
+ if not df_cuanto.empty:
332
+ # Llamada a la función para crear el gráfico de Pareto
333
+ crear_grafico_pareto(df_cuanto)
334
+ st.markdown("Para más información consulta en: [Pareto chart](https://asq.org/quality-resources/pareto)")
335
+
336
+ # Función para obtener las coordenadas de un lugar utilizando OSMNX
337
+ def obtener_coordenadas_lugar(lugar):
338
+ return ox.geocode(lugar)
339
+
340
+ # Función para calcular la distancia entre dos puntos (coordenadas)
341
+ def calcular_distancia(origen, destino):
342
+ return geodesic(origen, destino).kilometers
343
+
344
+ # Función para graficar las ubicaciones en un mapa y mostrar la distancia, conectando los puntos con una línea
345
+ def crear_grafico_lugares(origen, destino, coordenadas_origen, coordenadas_destino):
346
+ # Calcular la distancia entre las dos ubicaciones
347
+ distancia = calcular_distancia(coordenadas_origen, coordenadas_destino)
348
+
349
+ # Crear un DataFrame para los dos puntos
350
+ df = pd.DataFrame({
351
+ "Lugar": [origen, destino],
352
+ "Latitud": [coordenadas_origen[0], coordenadas_destino[0]],
353
+ "Longitud": [coordenadas_origen[1], coordenadas_destino[1]]
354
+ })
355
+
356
+ # Crear el mapa con los dos puntos
357
+ fig = px.scatter_mapbox(
358
+ df, lat="Latitud", lon="Longitud", zoom=5, height=600, text="Lugar"
359
+ )
360
+
361
+ # Añadir la línea que conecta los dos puntos
362
+ fig.add_scattermapbox(
363
+ lat=[coordenadas_origen[0], coordenadas_destino[0]], # Latitudes de origen y destino
364
+ lon=[coordenadas_origen[1], coordenadas_destino[1]], # Longitudes de origen y destino
365
+ mode="lines",
366
+ line=dict(width=2, color="blue"),
367
+ name="Línea de conexión"
368
+ )
369
+
370
+ fig.update_layout(mapbox_style="open-street-map")
371
+
372
+ # Mostrar el gráfico en la aplicación
373
+ st.plotly_chart(fig)
374
+
375
+ # Mostrar la distancia entre los dos lugares
376
+ st.write(f"La distancia entre {origen} y {destino} es de aproximadamente {distancia:.2f} km.")
377
+
378
+ # Pestaña: Dónde (modificada)
379
+ with tabs[3]:
380
+ st.header("¿Dónde?")
381
+ # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
382
+ st.sidebar.subheader("Ingresos de datos del ¿Dónde?:")
383
+ # Ingreso de las ubicaciones de origen y destino
384
+ origen = st.sidebar.text_input("Ingrese el lugar de origen:", "Valencia, Venezuela")
385
+ destino = st.sidebar.text_input("Ingrese el lugar de destino:", "Medellín, Colombia")
386
+
387
+ # Mostrar el gráfico de los puntos y la distancia si se ingresan ambas ubicaciones
388
+ if origen and destino:
389
+ try:
390
+ coordenadas_origen = obtener_coordenadas_lugar(origen)
391
+ coordenadas_destino = obtener_coordenadas_lugar(destino)
392
+
393
+ crear_grafico_lugares(origen, destino, coordenadas_origen, coordenadas_destino)
394
+ except Exception as e:
395
+ st.error(f"Error al obtener las coordenadas: {e}")
396
+
397
+ # Función para crear gráfico de Gantt usando Plotly Timeline
398
+ def crear_grafico_gantt(eventos):
399
+ # Crear el gráfico de Gantt con Plotly
400
+ fig = px.timeline(
401
+ eventos,
402
+ x_start="Fecha de Inicio",
403
+ x_end="Fecha de Fin",
404
+ y="Evento",
405
+ color='Categoría', # Categorías para colorear las tareas
406
+ title='Gráfico de Gantt de Eventos'
407
+ )
408
+
409
+ # Añadir el porcentaje de realización como etiquetas en el gráfico
410
+ for i, row in eventos.iterrows():
411
+ fig.add_annotation(
412
+ x=row['Fecha de Fin'],
413
+ y=row['Evento'],
414
+ text=f"{row['Porcentaje de Realización']}% completado",
415
+ showarrow=False,
416
+ font=dict(size=12, color='white'),
417
+ bgcolor="green" if row['Porcentaje de Realización'] == 100 else "blue",
418
+ bordercolor="black",
419
+ xanchor='left'
420
+ )
421
+
422
+ fig.update_yaxes(autorange="reversed") # Invertir el eje Y
423
+ fig.update_layout(height=600, width=900) # Ajustar tamaño del gráfico
424
+ st.plotly_chart(fig)
425
+
426
+ with tabs[4]:
427
+ st.header("¿Cuándo?")
428
+
429
+ # Ejemplo de datos ampliados en DataFrame
430
+ st.sidebar.subheader("Ingresos de datos del ¿Cuándo?:")
431
+ example_data = {
432
+ "Evento": ["Proyecto Inicio", "Análisis de Requisitos", "Desarrollo", "Pruebas", "Implementación", "Cierre"],
433
+ "Fecha de Inicio": ["2023-01-01", "2023-02-01", "2023-06-01", "2023-08-01", "2023-10-01", "2023-11-01"],
434
+ "Fecha de Fin": ["2023-02-01", "2023-06-01", "2023-08-01", "2023-10-01", "2023-11-01", "2023-12-01"],
435
+ "Categoría": ["Planificación", "Análisis", "Desarrollo", "Pruebas", "Implementación", "Cierre"],
436
+ "Porcentaje de Realización": [100, 100, 80, 50, 20, 0] # Porcentajes de realización
437
+ }
438
+ example_df = pd.DataFrame(example_data)
439
+
440
+ # Ingreso de datos de tareas mediante un DataFrame editable
441
+ st.sidebar.write("Ingrese los datos de las tareas en el siguiente formato:")
442
+ df_tareas = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_gantt")
443
+
444
+ # Verificar que el DataFrame tiene datos para generar el gráfico
445
+ if not df_tareas.empty:
446
+ # Convertir las columnas de fechas a formato de fecha
447
+ df_tareas['Fecha de Inicio'] = pd.to_datetime(df_tareas['Fecha de Inicio'])
448
+ df_tareas['Fecha de Fin'] = pd.to_datetime(df_tareas['Fecha de Fin'])
449
+
450
+ # Crear gráfico Gantt a partir del DataFrame ingresado
451
+ crear_grafico_gantt(df_tareas)
452
+
453
+ st.markdown("Para más información consulta en: [Gantt chart](https://www.apm.org.uk/resources/find-a-resource/gantt-chart/)")
454
+
455
+
456
+ # # Función para crear gráfico de Gantt usando Plotly Timeline
457
+ # def crear_grafico_gantt(eventos):
458
+ # fig = px.timeline(
459
+ # eventos,
460
+ # x_start="Fecha de Inicio",
461
+ # x_end="Fecha de Fin",
462
+ # y="Evento",
463
+ # color='Categoría', # Esto es opcional si deseas agregar categorías para colorear las tareas
464
+ # title='Gráfico de Gantt de Eventos'
465
+ # )
466
+
467
+ # fig.update_yaxes(autorange="reversed") # Invertir el eje y para que las tareas se vean de arriba hacia abajo
468
+ # fig.update_layout(height=600, width=900) # Ajustar el tamaño del gráfico
469
+ # st.plotly_chart(fig)
470
+
471
+ # with tabs[4]:
472
+ # st.header("¿Cuándo?")
473
+
474
+ # # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
475
+ # st.sidebar.subheader("Ingresos de datos del ¿Cuándo?:")
476
+ # example_data = {
477
+ # "Evento": ["Proyecto Inicio", "Desarrollo", "Fin"],
478
+ # "Fecha de Inicio": ["2023-01-01", "2023-06-01", "2023-09-01"],
479
+ # "Fecha de Fin": ["2023-06-01", "2023-09-01", "2023-12-01"],
480
+ # "Categoría": ["Planificación", "Ejecución", "Cierre"]
481
+ # }
482
+ # example_df = pd.DataFrame(example_data)
483
+ # #st.sidebar.write(example_df)
484
+
485
+ # # Ingreso de datos de tareas mediante un DataFrame editable
486
+ # st.sidebar.write("Ingrese los datos de las tareas en el siguiente formato:")
487
+ # df_tareas = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_gantt")
488
+
489
+ # # Verificar que el DataFrame tiene datos para generar el gráfico
490
+ # if not df_tareas.empty:
491
+ # # Convertir las columnas de fechas a formato de fecha
492
+ # df_tareas['Fecha de Inicio'] = pd.to_datetime(df_tareas['Fecha de Inicio'])
493
+ # df_tareas['Fecha de Fin'] = pd.to_datetime(df_tareas['Fecha de Fin'])
494
+
495
+ # # Crear gráfico Gantt a partir del DataFrame ingresado
496
+ # crear_grafico_gantt(df_tareas)
497
+ # st.markdown("Para más información consulta en: [Gantt chart](https://www.apm.org.uk/resources/find-a-resource/gantt-chart/)")
498
+
499
+ # Pestaña: Cómo (modificada con el diagrama de flujo con simbología ANSI)
500
+ with tabs[5]:
501
+ st.header("¿Cómo?")
502
+ # Ejemplo de datos en DataFrame (similar al formato que mencionaste)
503
+ st.sidebar.subheader("Ingresos de datos del ¿Cómo?:")
504
+ # Ingreso de datos mediante un DataFrame editable
505
+ st.sidebar.write("Modifique los datos de acuerdo a su necesidad para el diagrama de flujo:")
506
+
507
+ # Función para generar el diagrama de flujo con colores llamativos, texto blanco y proporciones ajustadas
508
+ def generate_flowchart(steps):
509
+ dot = graphviz.Digraph()
510
+
511
+ # Colores por tipo de actividad, más llamativos
512
+ color_map = {
513
+ "Inicio / Fin": "#32CD32", # LimeGreen
514
+ "Operación / Actividad": "#FFD700", # Gold
515
+ "Documento": "#1E90FF", # DodgerBlue
516
+ "Datos": "#FF6347", # Tomato
517
+ "Almacenamiento / Archivo": "#8A2BE2", # BlueViolet
518
+ "Decisión": "#FF4500" # OrangeRed
519
+ }
520
+
521
+ # Agregar nodos de inicio y fin fijos con colores llamativos y texto blanco
522
+ dot.node("Inicio", label="Inicio", shape="oval", style="filled", fillcolor=color_map["Inicio / Fin"], fontcolor="white")
523
+ dot.node("Fin", label="Fin", shape="oval", style="filled", fillcolor=color_map["Inicio / Fin"], fontcolor="white")
524
+
525
+ # Agregar los pasos del diagrama de flujo
526
+ for step in steps:
527
+ fillcolor = color_map.get(step['tipo'], 'white')
528
+ dot.node(step['id'], label=step['etiqueta'], shape=step['forma'], style="filled", fillcolor=fillcolor, fontcolor="white")
529
+
530
+ # Conectar los pasos
531
+ dot.edge("Inicio", steps[0]['id']) # Conexión desde el inicio al primer paso
532
+ for step in steps:
533
+ if 'siguiente' in step:
534
+ for next_step_id in step['siguiente']:
535
+ dot.edge(step['id'], next_step_id)
536
+ dot.edge(steps[-1]['id'], "Fin") # Conexión del último paso al fin
537
+
538
+ # Ajustar proporciones de ancho y alto (más alto que ancho)
539
+ dot.attr(size="6,8", ratio="fill")
540
+
541
+ return dot
542
+
543
+ # Ejemplo precargado utilizando todos los símbolos y colores llamativos
544
+ def cargar_proceso_ejemplo():
545
+ return [
546
+ {'id': 'Paso_1', 'etiqueta': 'Iniciar proceso', 'tipo': 'Operación / Actividad', 'forma': 'rectangle', 'siguiente': ['Paso_2']},
547
+ {'id': 'Paso_2', 'etiqueta': 'Recibir documento', 'tipo': 'Documento', 'forma': 'parallelogram', 'siguiente': ['Paso_3']},
548
+ {'id': 'Paso_3', 'etiqueta': 'Tomar decisión', 'tipo': 'Decisión', 'forma': 'diamond', 'siguiente': ['Paso_4', 'Paso_5']},
549
+ {'id': 'Paso_4', 'etiqueta': 'Almacenar documento', 'tipo': 'Almacenamiento / Archivo', 'forma': 'invtriangle', 'siguiente': ['Paso_6']},
550
+ {'id': 'Paso_5', 'etiqueta': 'Procesar datos', 'tipo': 'Datos', 'forma': 'parallelogram', 'siguiente': ['Paso_6']},
551
+ {'id': 'Paso_6', 'etiqueta': 'Finalizar proceso', 'tipo': 'Operación / Actividad', 'forma': 'rectangle', 'siguiente': []},
552
+ ]
553
+
554
+ # Cargar o editar un proceso de ejemplo por defecto
555
+ usar_ejemplo = st.sidebar.checkbox('Usar proceso de ejemplo', value=True)
556
+
557
+ if usar_ejemplo:
558
+ pasos = cargar_proceso_ejemplo()
559
+ else:
560
+ pasos = []
561
+
562
+ # Seleccionar número de pasos (si no se usa el proceso de ejemplo)
563
+ num_pasos = st.sidebar.number_input('Número de pasos', min_value=1, max_value=20, value=len(pasos) if usar_ejemplo else 1)
564
+
565
+ # IDs automáticos para los pasos
566
+ ids_disponibles = [f'Paso_{i + 1}' for i in range(num_pasos)]
567
+
568
+ for i in range(num_pasos):
569
+ st.sidebar.subheader(f'Paso {i + 1}')
570
+
571
+ # Datos para los pasos existentes o nuevos
572
+ if i < len(pasos):
573
+ paso = pasos[i]
574
+ etiqueta = st.sidebar.text_input(f'Etiqueta para el Paso {i + 1}', value=paso['etiqueta'])
575
+ tipo = st.sidebar.selectbox(f'Tipo de actividad del Paso {i + 1}',
576
+ ['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'],
577
+ index=['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'].index(paso['tipo']))
578
+ siguientes_pasos = st.sidebar.multiselect(f'Pasos siguientes desde el Paso {i + 1}', ids_disponibles, default=paso['siguiente'])
579
+ else:
580
+ etiqueta = st.sidebar.text_input(f'Etiqueta para el Paso {i + 1}', value=f'Paso {i + 1}')
581
+ tipo = st.sidebar.selectbox(f'Tipo de actividad del Paso {i + 1}', ['Inicio / Fin', 'Operación / Actividad', 'Documento', 'Datos', 'Almacenamiento / Archivo', 'Decisión'])
582
+ siguientes_pasos = st.sidebar.multiselect(f'Pasos siguientes desde el Paso {i + 1}', ids_disponibles)
583
+
584
+ # Definir la forma según el tipo de actividad
585
+ forma_map = {
586
+ 'Inicio / Fin': 'oval',
587
+ 'Operación / Actividad': 'rectangle',
588
+ 'Documento': 'parallelogram',
589
+ 'Datos': 'parallelogram',
590
+ 'Almacenamiento / Archivo': 'invtriangle',
591
+ 'Decisión': 'diamond'
592
+ }
593
+ forma = forma_map.get(tipo, 'rectangle')
594
+
595
+ # Actualizar o agregar el paso
596
+ if i < len(pasos):
597
+ pasos[i] = {
598
+ 'id': f'Paso_{i + 1}',
599
+ 'etiqueta': etiqueta,
600
+ 'tipo': tipo,
601
+ 'forma': forma,
602
+ 'siguiente': siguientes_pasos
603
+ }
604
+ else:
605
+ pasos.append({
606
+ 'id': f'Paso_{i + 1}',
607
+ 'etiqueta': etiqueta,
608
+ 'tipo': tipo,
609
+ 'forma': forma,
610
+ 'siguiente': siguientes_pasos
611
+ })
612
+
613
+ # Mostrar el diagrama de flujo generado
614
+ st.graphviz_chart(generate_flowchart(pasos))
615
+ st.markdown("Para más información consulta en: [Flowchart](https://asq.org/quality-resources/flowchart)")
616
+
617
+ # Pestaña: Por qué
618
+ with tabs[6]:
619
+ st.header("¿Por qué?")
620
+ st.sidebar.subheader("Ingresos de datos del ¿Por qué?:")
621
+
622
+ # Ejemplo de datos en DataFrame
623
+ example_data = {
624
+ "Categoría": ["Idea A", "Idea B", "Idea C", "Idea D", "Idea E", "Idea F"],
625
+ "Variable x": [10, 15, 5, 30, 2, 60],
626
+ "Variable y": [3, 5, 2, 10, 1, 15],
627
+ "Variable z": [1000, 1500, 100, 500, 0, 250]
628
+ }
629
+ example_df = pd.DataFrame(example_data)
630
+
631
+ # Ingreso de datos mediante un DataFrame editable
632
+ st.sidebar.write("Modifique los datos de acuerdo a su necesidad:")
633
+ df_porque = st.sidebar.data_editor(example_df, num_rows="dynamic", key="df_porque")
634
+
635
+ # Selección de columnas para el gráfico
636
+ columnas = df_porque.columns.tolist()
637
+
638
+ x_col = st.sidebar.selectbox("Seleccione la variable para el eje X:", columnas, index=1)
639
+ y_col = st.sidebar.selectbox("Seleccione la variable para el eje Y:", columnas, index=2)
640
+ size_col = st.sidebar.selectbox("Seleccione la variable para el tamaño de las burbujas:", columnas, index=3)
641
+ color_col = st.sidebar.selectbox("Seleccione la variable para el color de las burbujas:", columnas, index=3)
642
+
643
+ # Verificar si los nombres ingresados son válidos en el DataFrame
644
+ if not df_porque.empty:
645
+ if all(col in df_porque.columns for col in [x_col, y_col, size_col, color_col]):
646
+ # Crear gráfico de burbujas con Plotly si los nombres de las columnas son válidos
647
+ fig = px.scatter(
648
+ df_porque,
649
+ x=x_col, # Variable en el eje X
650
+ y=y_col, # Variable en el eje Y
651
+ size=size_col, # Tamaño de las burbujas
652
+ text="Categoría", # Etiquetas para las burbujas
653
+ color=color_col, # Color basado en la variable seleccionada
654
+ title="Gráfico de Burbujas Personalizado",
655
+ size_max=10, # Ajuste del tamaño máximo de las burbujas
656
+ color_continuous_scale=px.colors.sequential.Plasma # Escala de colores
657
+ )
658
+
659
+ # Personalizar diseño
660
+ fig.update_traces(marker=dict(sizemode='diameter', opacity=0.8, line=dict(width=2, color='DarkSlateGrey')),
661
+ selector=dict(mode='markers+text'))
662
+ fig.update_layout(height=600, width=900) # Ajustar tamaño del gráfico
663
+
664
+ # Mostrar gráfico en la aplicación
665
+ st.plotly_chart(fig)
666
+ else:
667
+ st.error("Algunas de las columnas ingresadas no existen en el DataFrame. Verifique los nombres.")
668
+
669
+ st.markdown("Para más información consulta en: [Bubble chart](https://plotly.com/python/bubble-charts/)")
670
+
671
+ # Pestaña: Acerca de mí
672
+ with tabs[7]:
673
+ #st.header("Acerca de mí")
674
+
675
+ st.subheader("José Miguel Aguilar Torrealba")
676
+ st.write("**Chemical Engineer | MBA Digital Business Administration | Innovation & CI Facilitator | Coaching | Citizen Data Scientist | Lean Six Sigma | Integrated Management Systems Auditor**")
677
+
678
+ st.subheader("Resumen profesional")
679
+ st.write("""
680
+ 20 años de experiencia en los sectores químico, educativo y de alimentos, me especializo en modelos de mejora continua, excelencia operacional y proyectos de tecnología e infraestructura. He trabajado en la industria y en el ámbito académico, facilitando y auditoriando sistemas de gestión.
681
+ """)
682
+
683
+ st.subheader("Experiencia Profesional")
684
+ st.write("""
685
+ - **Continuous Improvement Manager** en AkzoNobel (abr. 2022 - actualidad): Implementación de modelos globales de mejora continua, gestión de iniciativas de ahorro y transferencia de buenas prácticas.
686
+
687
+ - **SR Continuous Improvement Coordinator** en Andercol SAS (feb. 2016 - abr. 2022): Diseño de estrategias de mejora, Industria 4.0 y Big Data, y gestión de la excelencia operacional regional.
688
+
689
+ - **Senior Process & Project Coordinator** en C.A. Venezolana de Pinturas (feb. 2014 - ene. 2016): Control de productos y procesos, reducción del impacto ambiental y optimización de la producción.
690
+
691
+ - **Process Engineer** en C.A. QUIMICA INTEGRADA -INTEQUIM- (may. 2010 - feb. 2014): Coordinación de mejoras en procesos y equipos para optimizar la productividad.
692
+
693
+ - **Seminary Professor** en Universidad de Carabobo (sept. 2009 - sept. 2010): Facilitación de nuevas tendencias en ingeniería química y mejoramiento continuo.
694
+
695
+ - **CI & Quality Advisor** en Palma Products International C.A. (nov. 2009 - abr. 2010): Reingeniería del SGC y mejora continua.
696
+
697
+ - **Continuous Improvement Engineer** en Ajegroup (ene. 2005 - oct. 2008): Implementación de metodologías Lean Manufacturing, Kaizen, y TPM.
698
+
699
+ - **Process Engineer** en Ajegroup (ago. 2006 - nov. 2006): Mejora de sistemas hidráulicos y procesos de producción.
700
+
701
+ - **Quality Auditor** en Ajegroup (nov. 2005 - jul. 2006): Control y auditoría de calidad.
702
+
703
+ - **Continuous Improvement Intern** en Ajegroup (ago. 2004 - nov. 2005): Participación en control estadístico de procesos y Lean Six Sigma.
704
+
705
+ - **Analytical Chemistry Trainer** en Universidad de Carabobo (mar. 2004 - nov. 2005): Entrenador en química analítica y asistente del profesor.
706
+ """)
707
+
708
+ st.subheader("Formación")
709
+ st.write("""
710
+ - **Grand Master MBA Dirección de Negocio Digital**, TECH Universidad (ene. 2021 - feb. 2023)
711
+
712
+ - **Ingeniero Químico**, Universidad de Carabobo (ago. 2000 - ago. 2007)
713
+
714
+ - **Certificaciones y Formación Adicional**:
715
+ - Lean Six Sigma
716
+ - Big Data
717
+ - Transformación Digital
718
+ - Control de Procesos Químicos
719
+ - Auditoría de Sistemas Integrados de Gestión
720
+ """)
721
+
722
+ # Agregar un enlace al final de la pestaña
723
+ st.markdown("Para más información consulta en: [LinkedIn](https://www.linkedin.com/in/josemaguilar/)")