Update app.py
Browse files
app.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import plotly.express as px
|
| 3 |
import pandas as pd
|
| 4 |
-
import numpy as np
|
| 5 |
from datetime import datetime
|
|
|
|
|
|
|
| 6 |
|
| 7 |
# Configuración de la página
|
| 8 |
st.set_page_config(page_title="Generador de Gráficos Personalizado", layout="wide")
|
|
@@ -29,13 +31,9 @@ st.sidebar.header("Configuración del Gráfico")
|
|
| 29 |
# Título del gráfico
|
| 30 |
chart_title = st.sidebar.text_input("Título del Gráfico", "Generador de Gráfico")
|
| 31 |
|
| 32 |
-
# Tipo de gráfico
|
| 33 |
chart_type = st.sidebar.selectbox("Tipo de Gráfico", ["Línea", "Área", "Dispersión", "Barras", "Donut"])
|
| 34 |
|
| 35 |
-
# Función para generar rangos de fechas (eliminada la opción de línea temporal)
|
| 36 |
-
def generate_date_range(start_date, end_date, freq):
|
| 37 |
-
return pd.date_range(start=start_date, end=end_date, freq=freq).strftime('%Y-%m-%d').tolist()
|
| 38 |
-
|
| 39 |
# Ingresar valores para los ejes
|
| 40 |
x_values = st.sidebar.text_area("Valores para X (separados por comas)", "2013,2014,2015,2016,2017,2018")
|
| 41 |
x = x_values.split(",")
|
|
@@ -58,22 +56,32 @@ y_label = st.sidebar.text_input("Etiqueta para el eje Y", "Y")
|
|
| 58 |
|
| 59 |
# Desplegable de opciones adicionales
|
| 60 |
with st.sidebar.expander("Opciones Adicionales"):
|
| 61 |
-
graph_width = st.slider("Ancho del Gráfico", min_value=400, max_value=
|
| 62 |
-
graph_height = st.slider("Alto del Gráfico", min_value=300, max_value=
|
| 63 |
-
font_family = st.selectbox("Fuente", font_options, index=font_options.index("Times New Roman"))
|
|
|
|
|
|
|
| 64 |
show_legend = st.checkbox("Mostrar Leyenda", value=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
opacity = st.slider("Opacidad (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
| 66 |
border_width = st.slider("Grosor del Borde", min_value=0.0, max_value=3.0, value=1.5, step=0.1)
|
| 67 |
border_opacity = st.slider("Opacidad del Borde (%)", min_value=0, max_value=100, value=60, step=1) / 100
|
| 68 |
show_grid = st.checkbox("Activar rejilla", value=True)
|
|
|
|
| 69 |
if chart_type == "Barras" and num_y_vars > 1:
|
| 70 |
superposed_bars = st.checkbox("Superpuestas", value=True, key="superposed_bars")
|
| 71 |
else:
|
| 72 |
superposed_bars = False
|
| 73 |
if chart_type == "Barras":
|
| 74 |
horizontal_bars = st.checkbox("Invertidas", key="horizontal_bars")
|
|
|
|
| 75 |
if chart_type == "Donut":
|
| 76 |
hole_size = st.slider("Tamaño del agujero (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
|
|
|
| 77 |
|
| 78 |
# Opción para múltiples colores (siempre activada para Donut)
|
| 79 |
use_multiple_colors = st.sidebar.checkbox("Usar múltiples colores", value=True if chart_type == "Donut" or num_y_vars > 1 else False, key="use_multiple_colors")
|
|
@@ -84,11 +92,10 @@ selected_color = st.sidebar.color_picker("Color", "#24CBA0", key="single_color")
|
|
| 84 |
# Definir colores
|
| 85 |
if use_multiple_colors:
|
| 86 |
num_colors = len(x)
|
| 87 |
-
colors = [hex_to_rgba(st.sidebar.color_picker(f"Color {i+1}", predefined_colors[i % len(predefined_colors)], key=f"color_{i}"), alpha=opacity)
|
| 88 |
-
for i in range(num_colors)]
|
| 89 |
else:
|
| 90 |
color = hex_to_rgba(selected_color, alpha=opacity)
|
| 91 |
-
colors = [color] * len(x)
|
| 92 |
|
| 93 |
# Procesar valores
|
| 94 |
y_values_lists = [[float(i) for i in y_values.split(",") if i.strip()] for y_values in y_values_list]
|
|
@@ -113,21 +120,27 @@ common_layout = dict(
|
|
| 113 |
hovermode="x unified",
|
| 114 |
width=graph_width,
|
| 115 |
height=graph_height,
|
| 116 |
-
margin=dict(l=
|
| 117 |
-
xaxis=dict(showgrid=show_grid, zeroline=True, gridcolor='rgba(211,211,211,0.5)', zerolinecolor='rgba(128,128,128,0.5)', autorange=True
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
| 120 |
showlegend=show_legend,
|
| 121 |
-
legend=dict(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
)
|
| 123 |
|
| 124 |
-
# Personalizar el hovertemplate
|
| 125 |
-
hovertemplate = '<b>%{y}</b>'
|
| 126 |
-
|
| 127 |
# Generar el gráfico basado en el tipo seleccionado
|
| 128 |
if chart_type == "Línea":
|
| 129 |
fig = px.line(data, x="X", y=y_names_list, line_shape="spline")
|
| 130 |
-
fig.update_traces(hovertemplate=
|
| 131 |
if use_multiple_colors:
|
| 132 |
for i, name in enumerate(y_names_list):
|
| 133 |
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)])
|
|
@@ -136,7 +149,7 @@ if chart_type == "Línea":
|
|
| 136 |
|
| 137 |
elif chart_type == "Área":
|
| 138 |
fig = px.area(data, x="X", y=y_names_list, line_shape="spline")
|
| 139 |
-
fig.update_traces(hovertemplate=
|
| 140 |
if use_multiple_colors:
|
| 141 |
for i, name in enumerate(y_names_list):
|
| 142 |
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)], fillcolor=colors[i % len(colors)])
|
|
@@ -145,19 +158,19 @@ elif chart_type == "Área":
|
|
| 145 |
|
| 146 |
elif chart_type == "Dispersión":
|
| 147 |
fig = px.scatter(data, x="X", y=y_names_list)
|
| 148 |
-
fig.update_traces(hovertemplate=
|
| 149 |
if use_multiple_colors:
|
| 150 |
for i, name in enumerate(y_names_list):
|
| 151 |
-
fig.update_traces(selector=dict(name=name), marker_color=colors[i % len(colors)], marker_line_color=colors[i % len(colors)]
|
| 152 |
else:
|
| 153 |
-
fig.update_traces(marker_color=color, marker_line_color=color
|
| 154 |
|
| 155 |
elif chart_type == "Barras":
|
| 156 |
if horizontal_bars:
|
| 157 |
fig = px.bar(data, x=y_names_list, y="X", orientation='h', barmode='overlay' if superposed_bars else 'group')
|
| 158 |
else:
|
| 159 |
fig = px.bar(data, x="X", y=y_names_list, barmode='overlay' if superposed_bars else 'group')
|
| 160 |
-
fig.update_traces(hovertemplate=
|
| 161 |
if use_multiple_colors:
|
| 162 |
for i, trace in enumerate(fig.data):
|
| 163 |
trace.update(marker_color=colors[i % len(colors)])
|
|
@@ -165,34 +178,36 @@ elif chart_type == "Barras":
|
|
| 165 |
fig.update_traces(marker_color=color)
|
| 166 |
fig.update_layout(bargap=0.2)
|
| 167 |
|
| 168 |
-
|
| 169 |
elif chart_type == "Donut":
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
|
|
|
| 173 |
fig.update_traces(hovertemplate='<b>%{label}</b>: %{value} (%{percent})', textinfo='percent+label')
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
|
|
|
|
|
|
| 193 |
|
| 194 |
# Añadir anotación para el título
|
| 195 |
-
if chart_type != "Donut"
|
| 196 |
fig.update_layout(
|
| 197 |
title=dict(
|
| 198 |
text=f"{chart_title}",
|
|
@@ -202,27 +217,33 @@ if chart_type != "Donut" and chart_type != "Enlace externo":
|
|
| 202 |
yanchor='top',
|
| 203 |
font=dict(
|
| 204 |
family=font_family,
|
| 205 |
-
size=
|
| 206 |
-
color="#374151"
|
| 207 |
-
weight="normal"
|
| 208 |
)
|
| 209 |
),
|
| 210 |
legend_title_text='' # Eliminar el texto del título de la leyenda
|
| 211 |
)
|
| 212 |
-
|
| 213 |
-
# Aplicar configuración común
|
| 214 |
-
fig.update_layout(**common_layout)
|
| 215 |
-
|
| 216 |
-
# Aplicar múltiples colores si se seleccionó la opción
|
| 217 |
-
if use_multiple_colors and chart_type != "Donut":
|
| 218 |
-
for i, trace in enumerate(fig.data):
|
| 219 |
-
trace.update(marker_color=colors[i % len(colors)])
|
| 220 |
-
|
| 221 |
-
# Mostrar el gráfico
|
| 222 |
-
st.plotly_chart(fig)
|
| 223 |
|
| 224 |
-
|
| 225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
# Información adicional
|
| 228 |
-
st.write("")
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import plotly.express as px
|
| 3 |
import pandas as pd
|
| 4 |
+
import numpy as np # Asegúrate de importar numpy
|
| 5 |
from datetime import datetime
|
| 6 |
+
import plotly.io as pio
|
| 7 |
+
import io
|
| 8 |
|
| 9 |
# Configuración de la página
|
| 10 |
st.set_page_config(page_title="Generador de Gráficos Personalizado", layout="wide")
|
|
|
|
| 31 |
# Título del gráfico
|
| 32 |
chart_title = st.sidebar.text_input("Título del Gráfico", "Generador de Gráfico")
|
| 33 |
|
| 34 |
+
# Tipo de gráfico
|
| 35 |
chart_type = st.sidebar.selectbox("Tipo de Gráfico", ["Línea", "Área", "Dispersión", "Barras", "Donut"])
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
# Ingresar valores para los ejes
|
| 38 |
x_values = st.sidebar.text_area("Valores para X (separados por comas)", "2013,2014,2015,2016,2017,2018")
|
| 39 |
x = x_values.split(",")
|
|
|
|
| 56 |
|
| 57 |
# Desplegable de opciones adicionales
|
| 58 |
with st.sidebar.expander("Opciones Adicionales"):
|
| 59 |
+
graph_width = st.slider("Ancho del Gráfico", min_value=400, max_value=1200, value=1000, step=50)
|
| 60 |
+
graph_height = st.slider("Alto del Gráfico", min_value=300, max_value=900, value=700, step=50)
|
| 61 |
+
font_family = st.selectbox("Fuente del Gráfico", font_options, index=font_options.index("Times New Roman"))
|
| 62 |
+
axis_font_size = st.slider("Tamaño de la Fuente de los Ejes", min_value=10, max_value=30, value=24, step=1)
|
| 63 |
+
tick_font_size = st.slider("Tamaño de los Números de los Ejes", min_value=8, max_value=24, value=19, step=1)
|
| 64 |
show_legend = st.checkbox("Mostrar Leyenda", value=True)
|
| 65 |
+
|
| 66 |
+
# Configuración de la leyenda
|
| 67 |
+
legend_font_family = st.selectbox("Fuente de la Leyenda", font_options, index=font_options.index("Arial"))
|
| 68 |
+
legend_font_size = st.slider("Tamaño de la Fuente de la Leyenda", min_value=10, max_value=30, value=22, step=1)
|
| 69 |
+
|
| 70 |
opacity = st.slider("Opacidad (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
| 71 |
border_width = st.slider("Grosor del Borde", min_value=0.0, max_value=3.0, value=1.5, step=0.1)
|
| 72 |
border_opacity = st.slider("Opacidad del Borde (%)", min_value=0, max_value=100, value=60, step=1) / 100
|
| 73 |
show_grid = st.checkbox("Activar rejilla", value=True)
|
| 74 |
+
|
| 75 |
if chart_type == "Barras" and num_y_vars > 1:
|
| 76 |
superposed_bars = st.checkbox("Superpuestas", value=True, key="superposed_bars")
|
| 77 |
else:
|
| 78 |
superposed_bars = False
|
| 79 |
if chart_type == "Barras":
|
| 80 |
horizontal_bars = st.checkbox("Invertidas", key="horizontal_bars")
|
| 81 |
+
|
| 82 |
if chart_type == "Donut":
|
| 83 |
hole_size = st.slider("Tamaño del agujero (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
| 84 |
+
show_values = st.checkbox("Mostrar valores en el gráfico", value=False)
|
| 85 |
|
| 86 |
# Opción para múltiples colores (siempre activada para Donut)
|
| 87 |
use_multiple_colors = st.sidebar.checkbox("Usar múltiples colores", value=True if chart_type == "Donut" or num_y_vars > 1 else False, key="use_multiple_colors")
|
|
|
|
| 92 |
# Definir colores
|
| 93 |
if use_multiple_colors:
|
| 94 |
num_colors = len(x)
|
| 95 |
+
colors = [hex_to_rgba(st.sidebar.color_picker(f"Color {i+1}", predefined_colors[i % len(predefined_colors)], key=f"color_{i}"), alpha=opacity) for i in range(num_colors)]
|
|
|
|
| 96 |
else:
|
| 97 |
color = hex_to_rgba(selected_color, alpha=opacity)
|
| 98 |
+
colors = [color] * len(x)
|
| 99 |
|
| 100 |
# Procesar valores
|
| 101 |
y_values_lists = [[float(i) for i in y_values.split(",") if i.strip()] for y_values in y_values_list]
|
|
|
|
| 120 |
hovermode="x unified",
|
| 121 |
width=graph_width,
|
| 122 |
height=graph_height,
|
| 123 |
+
margin=dict(l=80, r=40, t=100, b=60), # Ajustar los márgenes para separar el texto de los ejes
|
| 124 |
+
xaxis=dict(showgrid=show_grid, zeroline=True, gridcolor='rgba(211,211,211,0.5)', zerolinecolor='rgba(128,128,128,0.5)', autorange=True,
|
| 125 |
+
tickfont=dict(size=tick_font_size), title_font=dict(size=axis_font_size)),
|
| 126 |
+
yaxis=dict(showgrid=show_grid, zeroline=True, gridcolor='rgba(211,211,211,0.5)', zerolinecolor='rgba(128,128,128,0.5)', autorange=True,
|
| 127 |
+
tickfont=dict(size=tick_font_size), title_font=dict(size=axis_font_size)),
|
| 128 |
+
font=dict(family=font_family, size=axis_font_size, color="#374151"),
|
| 129 |
showlegend=show_legend,
|
| 130 |
+
legend=dict(
|
| 131 |
+
orientation="v",
|
| 132 |
+
yanchor="top",
|
| 133 |
+
y=1,
|
| 134 |
+
xanchor="left",
|
| 135 |
+
x=1.02,
|
| 136 |
+
font=dict(family=legend_font_family, size=legend_font_size)
|
| 137 |
+
)
|
| 138 |
)
|
| 139 |
|
|
|
|
|
|
|
|
|
|
| 140 |
# Generar el gráfico basado en el tipo seleccionado
|
| 141 |
if chart_type == "Línea":
|
| 142 |
fig = px.line(data, x="X", y=y_names_list, line_shape="spline")
|
| 143 |
+
fig.update_traces(hovertemplate='<b>%{y}</b>', line=dict(width=border_width)) # Aplicar el grosor del borde a línea
|
| 144 |
if use_multiple_colors:
|
| 145 |
for i, name in enumerate(y_names_list):
|
| 146 |
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)])
|
|
|
|
| 149 |
|
| 150 |
elif chart_type == "Área":
|
| 151 |
fig = px.area(data, x="X", y=y_names_list, line_shape="spline")
|
| 152 |
+
fig.update_traces(hovertemplate='<b>%{y}</b>', line=dict(width=border_width), fillcolor=color)
|
| 153 |
if use_multiple_colors:
|
| 154 |
for i, name in enumerate(y_names_list):
|
| 155 |
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)], fillcolor=colors[i % len(colors)])
|
|
|
|
| 158 |
|
| 159 |
elif chart_type == "Dispersión":
|
| 160 |
fig = px.scatter(data, x="X", y=y_names_list)
|
| 161 |
+
fig.update_traces(hovertemplate='<b>%{y}</b>', marker=dict(size=10, line=dict(width=border_width, color=hex_to_rgba('#000000', border_opacity))))
|
| 162 |
if use_multiple_colors:
|
| 163 |
for i, name in enumerate(y_names_list):
|
| 164 |
+
fig.update_traces(selector=dict(name=name), marker_color=colors[i % len(colors)], marker_line_color=colors[i % len(colors)])
|
| 165 |
else:
|
| 166 |
+
fig.update_traces(marker_color=color, marker_line_color=color)
|
| 167 |
|
| 168 |
elif chart_type == "Barras":
|
| 169 |
if horizontal_bars:
|
| 170 |
fig = px.bar(data, x=y_names_list, y="X", orientation='h', barmode='overlay' if superposed_bars else 'group')
|
| 171 |
else:
|
| 172 |
fig = px.bar(data, x="X", y=y_names_list, barmode='overlay' if superposed_bars else 'group')
|
| 173 |
+
fig.update_traces(hovertemplate='<b>%{y}</b>', marker=dict(line=dict(width=border_width, color=hex_to_rgba('#000000', border_opacity))))
|
| 174 |
if use_multiple_colors:
|
| 175 |
for i, trace in enumerate(fig.data):
|
| 176 |
trace.update(marker_color=colors[i % len(colors)])
|
|
|
|
| 178 |
fig.update_traces(marker_color=color)
|
| 179 |
fig.update_layout(bargap=0.2)
|
| 180 |
|
|
|
|
| 181 |
elif chart_type == "Donut":
|
| 182 |
+
# Generar solo un gráfico Donut y asegurarse de que no haya duplicados
|
| 183 |
+
fig = px.pie(data, values=y_values_lists[0], names="X", hole=hole_size, color_discrete_sequence=colors[:len(x)])
|
| 184 |
+
|
| 185 |
+
if show_values:
|
| 186 |
fig.update_traces(hovertemplate='<b>%{label}</b>: %{value} (%{percent})', textinfo='percent+label')
|
| 187 |
+
else:
|
| 188 |
+
fig.update_traces(hovertemplate='<b>%{label}</b>', textinfo='none') # Ocultar valores y etiquetas
|
| 189 |
+
|
| 190 |
+
fig.update_traces(marker=dict(colors=colors[:len(x)])) # Aplicar múltiples colores
|
| 191 |
+
|
| 192 |
+
fig.update_layout(
|
| 193 |
+
title=dict(
|
| 194 |
+
text=f"{chart_title}",
|
| 195 |
+
x=0.5,
|
| 196 |
+
y=0.95,
|
| 197 |
+
xanchor='center',
|
| 198 |
+
yanchor='top',
|
| 199 |
+
font=dict(
|
| 200 |
+
family=font_family,
|
| 201 |
+
size=axis_font_size,
|
| 202 |
+
color="#374151"
|
| 203 |
+
)
|
| 204 |
+
),
|
| 205 |
+
**common_layout
|
| 206 |
+
)
|
| 207 |
+
st.plotly_chart(fig)
|
| 208 |
|
| 209 |
# Añadir anotación para el título
|
| 210 |
+
if chart_type != "Donut":
|
| 211 |
fig.update_layout(
|
| 212 |
title=dict(
|
| 213 |
text=f"{chart_title}",
|
|
|
|
| 217 |
yanchor='top',
|
| 218 |
font=dict(
|
| 219 |
family=font_family,
|
| 220 |
+
size=axis_font_size,
|
| 221 |
+
color="#374151"
|
|
|
|
| 222 |
)
|
| 223 |
),
|
| 224 |
legend_title_text='' # Eliminar el texto del título de la leyenda
|
| 225 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
+
# Aplicar configuración común
|
| 228 |
+
fig.update_layout(**common_layout)
|
| 229 |
+
|
| 230 |
+
# Aplicar múltiples colores si se seleccionó la opción
|
| 231 |
+
if use_multiple_colors and chart_type != "Donut":
|
| 232 |
+
for i, trace in enumerate(fig.data):
|
| 233 |
+
trace.update(marker_color=colors[i % len(colors)])
|
| 234 |
+
|
| 235 |
+
# Mostrar el gráfico
|
| 236 |
+
st.plotly_chart(fig)
|
| 237 |
+
|
| 238 |
+
# Descargar gráfico con mayor resolución usando los valores personalizados
|
| 239 |
+
export_as_png = st.button("Descargar gráfico en alta calidad")
|
| 240 |
+
|
| 241 |
+
if export_as_png:
|
| 242 |
+
img_bytes = pio.to_image(fig, format="png", width=graph_width, height=graph_height, scale=6)
|
| 243 |
+
st.download_button(label="Descargar imagen", data=img_bytes, file_name="grafico_personalizado.png", mime="image/png")
|
| 244 |
+
|
| 245 |
+
# Forzar el autoescale
|
| 246 |
+
fig.update_layout(xaxis_autorange=True, yaxis_autorange=True)
|
| 247 |
|
| 248 |
# Información adicional
|
| 249 |
+
st.write("")
|