Update app.py
Browse files
app.py
CHANGED
|
@@ -91,6 +91,190 @@ else:
|
|
| 91 |
# Procesar valores
|
| 92 |
y_values_lists = [[float(i) for i in y_values.split(",") if i.strip()] for y_values in y_values_list]
|
| 93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
# Verificar si las listas tienen el mismo tamaño
|
| 95 |
if any(len(x) != len(y) for y in y_values_lists):
|
| 96 |
st.error("Todos los valores de X y Y deben tener la misma cantidad de elementos.")
|
|
|
|
| 91 |
# Procesar valores
|
| 92 |
y_values_lists = [[float(i) for i in y_values.split(",") if i.strip()] for y_values in y_values_list]
|
| 93 |
|
| 94 |
+
# Verificar si las listas tienen el mismo tamaño
|
| 95 |
+
if any(len(x) != len(y) for y in y_values_lists):
|
| 96 |
+
st.error("Todos los valores de X y Y deben tener la misma cantidad de elementos.")
|
| 97 |
+
else:
|
| 98 |
+
# Crear un DataFrame
|
| 99 |
+
data = pd.DataFrame({"X": x})
|
| 100 |
+
for idx, y_set in enumerate(y_values_lists):
|
| 101 |
+
data[y_names_list[idx]] = y_set
|
| 102 |
+
|
| 103 |
+
# Configuración común para todos los gráficos
|
| 104 |
+
common_layout = dict(
|
| 105 |
+
xaxis_title=x_label,
|
| 106 |
+
yaxis_title=y_label,
|
| 107 |
+
plot_bgcolor="white",
|
| 108 |
+
hovermode="x unified",
|
| 109 |
+
width=graph_width,
|
| 110 |
+
height=graph_height,
|
| 111 |
+
margin=dict(l=60, r=40, t=100, b=40),
|
| 112 |
+
xaxis=dict(showgrid=True, zeroline=True, gridcolor='rgba(211,211,211,0.5)', zerolinecolor='rgba(128,128,128,0.5)', autorange=True),
|
| 113 |
+
yaxis=dict(showgrid=True, zeroline=True, gridcolor='rgba(211,211,211,0.5)', zerolinecolor='rgba(128,128,128,0.5)', autorange=True),
|
| 114 |
+
font=dict(family=font_family, size=18, color="black"),
|
| 115 |
+
showlegend=show_legend,
|
| 116 |
+
legend=dict(orientation="v", yanchor="top", y=1, xanchor="left", x=1.02)
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
# Personalizar el hovertemplate
|
| 120 |
+
hovertemplate = '<b>%{y}</b>'
|
| 121 |
+
|
| 122 |
+
# Generar el gráfico basado en el tipo seleccionado
|
| 123 |
+
if chart_type == "Línea":
|
| 124 |
+
fig = px.line(data, x="X", y=y_names_list, line_shape="spline")
|
| 125 |
+
fig.update_traces(hovertemplate=hovertemplate)
|
| 126 |
+
if use_multiple_colors:
|
| 127 |
+
for i, name in enumerate(y_names_list):
|
| 128 |
+
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)])
|
| 129 |
+
else:
|
| 130 |
+
fig.update_traces(line_color=color)
|
| 131 |
+
elif chart_type == "Área":
|
| 132 |
+
fig = px.area(data, x="X", y=y_names_list, line_shape="spline")
|
| 133 |
+
fig.update_traces(hovertemplate=hovertemplate)
|
| 134 |
+
if use_multiple_colors:
|
| 135 |
+
for i, name in enumerate(y_names_list):
|
| 136 |
+
fig.update_traces(selector=dict(name=name), line_color=colors[i % len(colors)], fillcolor=colors[i % len(colors)])
|
| 137 |
+
else:
|
| 138 |
+
fig.update_traces(line_color=color, fillcolor=color)
|
| 139 |
+
elif chart_type == "Dispersión":
|
| 140 |
+
fig = px.scatter(data, x="X", y=y_names_list)
|
| 141 |
+
fig.update_traces(hovertemplate=hovertemplate)
|
| 142 |
+
if use_multiple_colors:
|
| 143 |
+
for i, name in enumerate(y_names_list):
|
| 144 |
+
fig.update_traces(selector=dict(name=name), marker_color=colors[i % len(colors)], marker_line_color=border_colors[i % len(border_colors)], marker_line_width=border_width)
|
| 145 |
+
else:
|
| 146 |
+
fig.update_traces(marker_color=color, marker_line_color=border_color, marker_line_width=border_width)
|
| 147 |
+
elif chart_type == "Barras":
|
| 148 |
+
if horizontal_bars:
|
| 149 |
+
fig = px.bar(data, x=y_names_list[0], y="X", orientation='h', barmode='stack' if stacked_bars else 'group')
|
| 150 |
+
else:
|
| 151 |
+
fig = px.bar(data, x="X", y=y_names_list, barmode='stack' if stacked_bars else 'group')
|
| 152 |
+
fig.update_traces(hovertemplate=hovertemplate)
|
| 153 |
+
if use_multiple_colors:
|
| 154 |
+
for i in range(len(data)):
|
| 155 |
+
fig.data[i].marker.color = colors[i % len(colors)]
|
| 156 |
+
fig.data[i].marker.line.color = border_colors[i % len(border_colors)]
|
| 157 |
+
fig.data[i].marker.line.width = border_width
|
| 158 |
+
else:
|
| 159 |
+
fig.update_traces(marker_color=color, marker_line_color=border_color, marker_line_width=border_width)
|
| 160 |
+
fig.update_layout(bargap=0.2)
|
| 161 |
+
elif chart_type == "Donut":
|
| 162 |
+
figs = []
|
| 163 |
+
for i, y_name in enumerate(y_names_list):
|
| 164 |
+
fig = px.pie(data, values=y_name, names="X", hole=hole_size, color_discrete_sequence=colors[:len(x)])
|
| 165 |
+
fig.update_traces(hovertemplate='<b>%{label}</b>: %{value} (%{percent})', textinfo='percent+label')
|
| 166 |
+
fig.update_layout(
|
| 167 |
+
title=dict(
|
| 168 |
+
text=f"{chart_title}<br><sub>{y_name}</sub>" if len(y_names_list) > 1 else chart_title,
|
| 169 |
+
x=0.5,
|
| 170 |
+
y=0.95,
|
| 171 |
+
xanchor='center',
|
| 172 |
+
yanchor='top',
|
| 173 |
+
font=dict(
|
| 174 |
+
family=font_family,
|
| 175 |
+
size=18,
|
| 176 |
+
color="#374151",
|
| 177 |
+
weight="normal"
|
| 178 |
+
)
|
| 179 |
+
),
|
| 180 |
+
**common_layout
|
| 181 |
+
)
|
| 182 |
+
figs.append(fig)
|
| 183 |
+
for fig in figs:
|
| 184 |
+
st.plotly_chart(fig)
|
| 185 |
+
|
| 186 |
+
# Añadir anotación para el título
|
| 187 |
+
if chart_type != "Donut":
|
| 188 |
+
fig.update_layout(
|
| 189 |
+
title=dict(
|
| 190 |
+
text=f"{chart_title}",
|
| 191 |
+
x=0.5,
|
| 192 |
+
y=0.95,
|
| 193 |
+
xanchor='center',
|
| 194 |
+
yanchor='top',
|
| 195 |
+
font=dict(
|
| 196 |
+
family=font_family,
|
| 197 |
+
size=18,
|
| 198 |
+
color="#374151",
|
| 199 |
+
weight="normal"
|
| 200 |
+
)
|
| 201 |
+
)
|
| 202 |
+
)
|
| 203 |
+
|
| 204 |
+
# Aplicar configuración común
|
| 205 |
+
fig.update_layout(**common_layout)
|
| 206 |
+
|
| 207 |
+
# Aplicar múltiples colores si se seleccionó la opción
|
| 208 |
+
if use_multiple_colors and chart_type != "Donut":
|
| 209 |
+
for i, trace in enumerate(fig.data):
|
| 210 |
+
trace.update(marker_color=colors[i % len(colors)], marker_line_color=border_colors[i % len(border_colors)], marker_line_width=border_width)
|
| 211 |
+
|
| 212 |
+
# Mostrar el gráfico
|
| 213 |
+
st.plotly_chart(fig)
|
| 214 |
+
|
| 215 |
+
# Forzar el autoescale
|
| 216 |
+
fig.update_layout(xaxis_autorange=True, yaxis_autorange=True)
|
| 217 |
+
|
| 218 |
+
# Información adicional
|
| 219 |
+
st.write("""
|
| 220 |
+
""")
|
| 221 |
+
|
| 222 |
+
# Selector de número de variables Y
|
| 223 |
+
num_y_vars = st.sidebar.number_input("Número de variables Y", min_value=1, max_value=100, value=1, step=1)
|
| 224 |
+
y_values_list = []
|
| 225 |
+
y_names_list = []
|
| 226 |
+
for i in range(num_y_vars):
|
| 227 |
+
if f"y_values_{i}" not in st.session_state:
|
| 228 |
+
st.session_state[f"y_values_{i}"] = ','.join([str(np.random.randint(1, 25)) for _ in x])
|
| 229 |
+
y_values = st.sidebar.text_area(f"Valores para Y-{i+1} (separados por comas)", st.session_state[f"y_values_{i}"])
|
| 230 |
+
st.session_state[f"y_values_{i}"] = y_values # Guardar los valores en session_state
|
| 231 |
+
y_name = st.sidebar.text_input(f"Nombre de la Variable Y-{i+1}", f"Variable Y-{i+1}")
|
| 232 |
+
y_values_list.append(y_values)
|
| 233 |
+
y_names_list.append(y_name)
|
| 234 |
+
|
| 235 |
+
# Etiquetas personalizadas para los ejes
|
| 236 |
+
x_label = st.sidebar.text_input("Etiqueta para el eje X", "X")
|
| 237 |
+
y_label = st.sidebar.text_input("Etiqueta para el eje Y", "Y")
|
| 238 |
+
|
| 239 |
+
# Desplegable de opciones adicionales
|
| 240 |
+
with st.sidebar.expander("Opciones Adicionales"):
|
| 241 |
+
graph_width = st.slider("Ancho del Gráfico", min_value=400, max_value=1000, value=800, step=50)
|
| 242 |
+
graph_height = st.slider("Alto del Gráfico", min_value=300, max_value=800, value=600, step=50)
|
| 243 |
+
font_family = st.selectbox("Fuente", font_options, index=font_options.index("Times New Roman"))
|
| 244 |
+
show_legend = st.checkbox("Mostrar Leyenda", value=True)
|
| 245 |
+
opacity = st.slider("Opacidad (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
| 246 |
+
border_width = st.slider("Grosor del Borde", min_value=0.0, max_value=3.0, value=1.5, step=0.1)
|
| 247 |
+
border_opacity = st.slider("Opacidad del Borde (%)", min_value=0, max_value=100, value=60, step=1) / 100
|
| 248 |
+
if chart_type == "Barras" and num_y_vars > 1:
|
| 249 |
+
stacked_bars = st.checkbox("Superpuestas")
|
| 250 |
+
else:
|
| 251 |
+
stacked_bars = False
|
| 252 |
+
if chart_type == "Barras":
|
| 253 |
+
horizontal_bars = st.checkbox("Invertidas")
|
| 254 |
+
if chart_type == "Donut":
|
| 255 |
+
hole_size = st.slider("Tamaño del agujero (%)", min_value=0, max_value=100, value=30, step=1) / 100
|
| 256 |
+
|
| 257 |
+
# Opción para múltiples colores (siempre activada para Donut)
|
| 258 |
+
use_multiple_colors = st.sidebar.checkbox("Usar múltiples colores", value=True if chart_type == "Donut" or num_y_vars > 1 else False)
|
| 259 |
+
|
| 260 |
+
# Seleccionar color(es) para el gráfico
|
| 261 |
+
selected_color = st.sidebar.color_picker("Color", "#24CBA0", key="single_color")
|
| 262 |
+
|
| 263 |
+
# Definir colores
|
| 264 |
+
if use_multiple_colors or chart_type == "Donut":
|
| 265 |
+
num_colors = max(len(y_values_list), len(x))
|
| 266 |
+
colors = [hex_to_rgba(selected_color if i == 0 else st.sidebar.color_picker(f"Color {i+1}", predefined_colors[i % len(predefined_colors)], key=f"color_{i}"), alpha=opacity)
|
| 267 |
+
for i in range(num_colors)]
|
| 268 |
+
border_colors = [hex_to_rgba(selected_color if i == 0 else predefined_colors[i % len(predefined_colors)], alpha=border_opacity)
|
| 269 |
+
for i in range(num_colors)]
|
| 270 |
+
else:
|
| 271 |
+
color = hex_to_rgba(selected_color, alpha=opacity)
|
| 272 |
+
border_color = hex_to_rgba(selected_color, alpha=border_opacity)
|
| 273 |
+
colors = [color] * len(x) # Definir colors para casos donde no se usa múltiple colores
|
| 274 |
+
|
| 275 |
+
# Procesar valores
|
| 276 |
+
y_values_lists = [[float(i) for i in y_values.split(",") if i.strip()] for y_values in y_values_list]
|
| 277 |
+
|
| 278 |
# Verificar si las listas tienen el mismo tamaño
|
| 279 |
if any(len(x) != len(y) for y in y_values_lists):
|
| 280 |
st.error("Todos los valores de X y Y deben tener la misma cantidad de elementos.")
|