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

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +752 -0
app.py ADDED
@@ -0,0 +1,752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/)")