diff --git a/.gitignore b/.gitignore index 459c006..8b38332 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,4 @@ dmypy.json .github/desktop.ini *.ini +configs/configuraciones_generales.yaml diff --git a/run_all_urbantrips.py b/run_all_urbantrips.py new file mode 100644 index 0000000..1b31bf8 --- /dev/null +++ b/run_all_urbantrips.py @@ -0,0 +1,21 @@ +from urbantrips import initialize_environment +from urbantrips import process_transactions +from urbantrips import run_postprocessing +from urbantrips import create_viz +from urbantrips import run_dashboard + +import warnings + +# Filter and suppress specific warnings +warnings.filterwarnings("ignore") + +def main(): + + initialize_environment.main() + process_transactions.main() + run_postprocessing.main() + create_viz.main() + run_dashboard.main() + +if __name__ == "__main__": + main() diff --git a/urbantrips/dashboard/dash_utils.py b/urbantrips/dashboard/dash_utils.py index 7c9b7c5..69e1b14 100644 --- a/urbantrips/dashboard/dash_utils.py +++ b/urbantrips/dashboard/dash_utils.py @@ -12,7 +12,8 @@ from shapely import wkt from matplotlib import colors as mcolors from folium import Figure -from shapely.geometry import LineString, Point, Polygon +from shapely.geometry import LineString, Point, Polygon, shape, mapping +import h3 def leer_configs_generales(): @@ -127,12 +128,16 @@ def calculate_weighted_means(df, def normalize_vars(tabla): if 'dia' in tabla.columns: - tabla.loc[tabla.dia == 'weekday', 'dia'] = 'Día hábil' + tabla.loc[tabla.dia == 'weekday', 'dia'] = 'Hábil' tabla.loc[tabla.dia == 'weekend', 'dia'] = 'Fin de semana' if 'day_type' in tabla.columns: - tabla.loc[tabla.day_type == 'weekday', 'day_type'] = 'Día hábil' + tabla.loc[tabla.day_type == 'weekday', 'day_type'] = 'Hábil' tabla.loc[tabla.day_type == 'weekend', 'day_type'] = 'Fin de semana' + if 'tipo_dia' in tabla.columns: + tabla.loc[tabla.tipo_dia == 'Dia habil', 'tipo_dia'] = 'Hábil' + # tabla.loc[tabla.tipo_dia == 'weekend', 'tipo_dia'] = 'Fin de semana' + if 'nombre_linea' in tabla.columns: tabla['nombre_linea'] = tabla['nombre_linea'].str.replace(' -', '') if 'Modo' in tabla.columns: @@ -624,4 +629,10 @@ def traigo_zonas_values(tipo = 'etapas'): (zonas_values.inicio_norm.notna())& (zonas_values.inicio_norm!=' (cuenca)')].sort_values(['zona', 'inicio_norm']).rename(columns={'inicio_norm':'Nombre'}) - return zonas_values \ No newline at end of file + return zonas_values + +# Convert geometry to H3 indices +def get_h3_indices_in_geometry(geometry, resolution): + geojson = mapping(geometry) + h3_indices = list(h3.polyfill(geojson, resolution, geo_json_conformant=True)) + return h3_indices \ No newline at end of file diff --git a/urbantrips/dashboard/dashboard.py b/urbantrips/dashboard/dashboard.py index 3a3a1ea..bf9baf3 100644 --- a/urbantrips/dashboard/dashboard.py +++ b/urbantrips/dashboard/dashboard.py @@ -17,7 +17,7 @@ from folium import Figure from shapely.geometry import LineString -from dash_utils import levanto_tabla_sql, get_logo +from dash_utils import levanto_tabla_sql, get_logo, traigo_indicadores st.set_page_config(layout="wide") @@ -79,26 +79,24 @@ # Inject CSS with Markdown col3.markdown(hide_table_row_index, unsafe_allow_html=True) - - col3.table(df) df = indicadores.loc[indicadores.orden == 3, ['Indicador', 'Valor']].copy() titulo = indicadores.loc[indicadores.orden == 3].Titulo.unique()[0] - col2.text(titulo) - # CSS to inject contained in a string - hide_table_row_index = """ - - """ + # col2.text(titulo) + # # CSS to inject contained in a string + # hide_table_row_index = """ + # + # """ - # Inject CSS with Markdown - col2.markdown(hide_table_row_index, unsafe_allow_html=True) + # # Inject CSS with Markdown + # col2.markdown(hide_table_row_index, unsafe_allow_html=True) - col2.table(df) + # col2.table(df) else: # Usar HTML para personalizar el estilo del texto @@ -127,3 +125,21 @@ """ col2.markdown(texto_html, unsafe_allow_html=True) +with st.expander('Indicadores', True): + col1, col2, col3 = st.columns([2, 2, 2]) + + general, modal, distancias = traigo_indicadores('all') + + st.session_state.general_ = general.loc[general.mes==desc_dia_i, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') + st.session_state.modal_ = modal.loc[modal.mes==desc_dia_i, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') + st.session_state.distancias_ = distancias.loc[distancias.mes==desc_dia_i, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') + + + st.session_state.general_ = st.session_state.general_[~st.session_state.general_.Indicador.isin(['Cantidad de Usuarios', 'Cantidad de Viajes'])] + if len(st.session_state.general_) > 0: + col1.write(f'Periodo: {desc_dia_i}') + col1.table(st.session_state.general_) + # col2.write('') + # col2.table(st.session_state.modal_) + col3.write('') + col3.table(st.session_state.distancias_) \ No newline at end of file diff --git a/urbantrips/dashboard/pages/1_Datos Generales.py b/urbantrips/dashboard/pages/1_Datos Generales.py index 202804d..6353cfc 100644 --- a/urbantrips/dashboard/pages/1_Datos Generales.py +++ b/urbantrips/dashboard/pages/1_Datos Generales.py @@ -1,3 +1,4 @@ +import numpy as np import streamlit as st import pandas as pd import folium @@ -9,7 +10,206 @@ from dash_utils import (levanto_tabla_sql, get_logo, create_linestring_od, extract_hex_colors_from_cmap) - +import matplotlib.pyplot as plt +from itertools import combinations +import squarify +from matplotlib_venn import venn3 + +# Function to create activity combinations as tuples +def get_activity_tuple(cols_dummies, selected_cols_dummies): + return tuple(1 if activity in selected_cols_dummies else 0 for activity in cols_dummies) + +# Function to calculate subset sizes +def get_activity_combination_number(df, cols_dummies, activity_combination): + activity_str_filter = [ + f"{a} > 0" if a in activity_combination else f"{a} == 0" for a in cols_dummies + ] + activity_str_filter = " & ".join(activity_str_filter) + return len(df.query(activity_str_filter)) + +# Generate example data +def generate_example_data(num_rows=100): + np.random.seed(42) # For reproducibility + data = { + 'train': np.random.randint(0, 2, size=num_rows), # Binary: 0 (not used), 1 (used) + 'subway': np.random.randint(0, 2, size=num_rows), + 'bus': np.random.randint(0, 2, size=num_rows) + } + return pd.DataFrame(data) + +# Generate subset sizes dictionary +def calculate_subset_sizes(df, cols_dummies): + list_of_tuples = [ + item + for sublist in [list(combinations(cols_dummies, i)) for i in range(1, len(cols_dummies) + 1)] + for item in sublist + ] + return { + get_activity_tuple(cols_dummies, combo): get_activity_combination_number(df, cols_dummies, combo) + for combo in list_of_tuples + } + +# Extract subset sizes for Venn diagram +def get_venn_subsets(subset_sizes): + return ( + subset_sizes.get((1, 0, 0), 0), # Only 'train' + subset_sizes.get((0, 1, 0), 0), # Only 'subway' + subset_sizes.get((1, 1, 0), 0), # 'train' and 'subway' + subset_sizes.get((0, 0, 1), 0), # Only 'bus' + subset_sizes.get((1, 0, 1), 0), # 'train' and 'bus' + subset_sizes.get((0, 1, 1), 0), # 'subway' and 'bus' + subset_sizes.get((1, 1, 1), 0) # 'train', 'subway', and 'bus' + ) + +# Plot Venn diagram +def plot_venn_diagram(etapas_modos): + + cols_dummies = [x for x in etapas_modos.columns.tolist() if x not in ['mes', 'tipo_dia', 'genero', 'factor_expansion_linea']] + + # Calcular porcentajes + absolute_values = calculate_weighted_values(etapas_modos, cols_dummies, weight_column='factor_expansion_linea', as_percentage=False) + percentage_values = calculate_weighted_values(etapas_modos, cols_dummies, weight_column='factor_expansion_linea', as_percentage=True) + + modal_etapas = pd.DataFrame(list(absolute_values.items()), columns=['Modes', 'Cantidad']).round(0) + modal_etapas[cols_dummies] = pd.DataFrame(modal_etapas['Modes'].tolist(), index=modal_etapas.index) + modal_etapas['Modo'] = '' + for i in cols_dummies: + modal_etapas.loc[modal_etapas[i]>=1, 'Modo'] += i+'-' + modal_etapas.loc[modal_etapas.Modo.str[-1:]=='-', 'Modo'] = modal_etapas.loc[modal_etapas.Modo.str[-1:]=='-'].Modo.str[:-1] + modal_etapas = modal_etapas[['Modo', 'Cantidad']] + modal_etapas['Cantidad'] = modal_etapas['Cantidad'].astype(int) + modal_etapas['%'] = (modal_etapas['Cantidad'] / modal_etapas['Cantidad'].sum() * 100).round(1) + modal_etapas = modal_etapas.sort_values('Cantidad', ascending=False) + + venn_subsets = get_venn_subsets(percentage_values) + + fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6)) + + # Left subplot: Venn3 + venn3(subsets=venn_subsets, set_labels=[activity.capitalize() for activity in cols_dummies], ax=ax1) + ax1.set_title("Partición modal (%)") + + # Identificar multimodal (más de un modo utilizado) + etapas_modos['Multimodal'] = (etapas_modos[cols_dummies].gt(0).sum(axis=1) > 1).astype(int) + + # Identificar multietapa (más de una etapa en al menos un modo) + etapas_modos['Multietapa'] = (etapas_modos[cols_dummies].gt(1).any(axis=1)).astype(int) + + for i in cols_dummies: + etapas_modos.loc[etapas_modos.Multimodal==1, i] = 0 + etapas_modos.loc[etapas_modos.Multietapa==1, i] = 0 + # etapas_modos.loc[etapas_modos[i]>=1, i] = 1 + + etapas_modos.loc[(etapas_modos.Multietapa>0)&(etapas_modos.Multimodal>0), 'Multietapa'] = 0 + + cols_dummies = cols_dummies+['Multimodal', 'Multietapa'] + + etapas_modos["Modos"] = etapas_modos[cols_dummies].idxmax(axis=1) + + v = etapas_modos.groupby('Modos', as_index=False).factor_expansion_linea.sum().round() + v['p'] = (v.factor_expansion_linea /v.factor_expansion_linea.sum() * 100).round(1) + v['m'] = v.Modos +'\n('+v.p.astype(str)+'%)' + v.loc[v.p<3, 'm'] = v.loc[v.p<3, 'm'].str.replace('\n', ' ') + + values_data =v.p.values.tolist() + values_names = v.m.values.tolist() + + fixed_palette = [ + '#AED6F1', '#F9E79F', '#ABEBC6', '#F5B7B1', '#D2B4DE', + '#FAD7A0', '#85C1E9', '#A3E4D7', '#F7DC6F', '#F0B27A', + '#F8C471', '#D7BDE2', '#A2D9CE', '#FDEBD0', '#D5F5E3', + '#F9E79F', '#82E0AA', '#BB8FCE', '#EDBB99', '#A9CCE3' + ] + + colors = fixed_palette[:len(v)] + + # Right subplot: Squarify treemap + squarify.plot(values_data, + label=values_names, + color=colors, + ax=ax2, + text_kwargs={'fontsize': 10, 'color': 'black'}) #, 'fontweight': 'bold' + ax2.axis("off") + + plt.tight_layout() + plt.show() + + + modal_viajes = etapas_modos.groupby('Modos', as_index=False).factor_expansion_linea.sum().round(0).rename(columns={'factor_expansion_linea':'Cantidad'}) + modal_viajes['%'] = (modal_viajes['Cantidad'] / modal_viajes['Cantidad'].sum() * 100).round(1) + modal_viajes = modal_viajes.sort_values('Cantidad', ascending=False) + modal_viajes = modal_viajes.rename(columns={'Modos':'Modo'}) + + return fig, modal_etapas, modal_viajes + +# Función para calcular los porcentajes o valores absolutos ponderados +def calculate_weighted_values(df, cols_dummies, weight_column, as_percentage=True): + # Calcular el total ponderado + total_weight = df[weight_column].sum() + + # Crear el diccionario para cada combinación de actividades + subset_sizes = {} + for combo in [list(c) for i in range(1, len(cols_dummies) + 1) for c in combinations(cols_dummies, i)]: + activity_str_filter = [f"{a} > 0" if a in combo else f"{a} == 0" for a in cols_dummies] + query_str = " & ".join(activity_str_filter) + subset_weight = df.query(query_str)[weight_column].sum() + subset_sizes[tuple(1 if a in combo else 0 for a in cols_dummies)] = subset_weight + + # Convertir a porcentajes si as_percentage es True + if as_percentage: + subset_sizes = {key: round((value / total_weight) * 100, 1) for key, value in subset_sizes.items()} + + return subset_sizes + +def traigo_socio_indicadores(socio_indicadores): + totals = None + totals_porc = 0 + avg_distances = 0 + avg_times = 0 + avg_velocity = 0 + modos_genero_abs = 0 + modos_genero_porc = 0 + modos_tarifa_abs = 0 + modos_tarifa_porc = 0 + avg_viajes = 0 + avg_etapas = 0 + avg_tiempo_entre_viajes = 0 + + if len(socio_indicadores) > 0: + + df = socio_indicadores[socio_indicadores.tabla=='viajes-genero-tarifa'].copy() + totals = pd.crosstab(values=df.factor_expansion_linea, columns=df.Genero, index=df.Tarifa, aggfunc='sum', margins=True, margins_name='Total', normalize=False).fillna(0).round().astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) + totals_porc = (pd.crosstab(values=df.factor_expansion_linea, columns=df.Genero, index=df.Tarifa, aggfunc='sum', margins=True, margins_name='Total', normalize=True) * 100).round(2) + + modos = socio_indicadores[socio_indicadores.tabla=='etapas-genero-modo'].copy() + modos_genero_abs = pd.crosstab(values=modos.factor_expansion_linea, index=[modos.Genero], columns=modos.Modo, aggfunc='sum', normalize=False, margins=True, margins_name='Total').fillna(0).astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) + modos_genero_porc = (pd.crosstab(values=modos.factor_expansion_linea, index=modos.Genero, columns=modos.Modo, aggfunc='sum', normalize=True, margins=True, margins_name='Total') * 100).round(2) + + modos = socio_indicadores[socio_indicadores.tabla=='etapas-tarifa-modo'].copy() + modos_tarifa_abs = pd.crosstab(values=modos.factor_expansion_linea, index=[modos.Tarifa], columns=modos.Modo, aggfunc='sum', normalize=False, margins=True, margins_name='Total').fillna(0).astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) + modos_tarifa_porc = (pd.crosstab(values=modos.factor_expansion_linea, index=modos.Tarifa, columns=modos.Modo, aggfunc='sum', normalize=True, margins=True, margins_name='Total') * 100).round(2) + + avg_distances = pd.crosstab(values=df.Distancia, columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) + avg_times = pd.crosstab(values=df['Tiempo de viaje'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) + avg_velocity = pd.crosstab(values=df['Velocidad'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) + avg_etapas = pd.crosstab(values=df['Etapas promedio'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).round(2).fillna('') + user = socio_indicadores[socio_indicadores.tabla=='usuario-genero-tarifa'].copy() + avg_viajes = pd.crosstab(values=user['Viajes promedio'], + index=[user.Tarifa], + columns=user.Genero, + margins=True, + margins_name='Total', + aggfunc=lambda x: (x * user.loc[x.index, 'factor_expansion_linea']).sum() / user.loc[x.index, 'factor_expansion_linea'].sum(),).round(2).fillna('') + + avg_tiempo_entre_viajes = pd.crosstab(values=df['Tiempo entre viajes'], + columns=df.Genero, + index=df.Tarifa, + margins=True, + margins_name='Total', + aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) + + return totals, totals_porc, avg_distances, avg_times, avg_velocity, modos_genero_abs, modos_genero_porc, modos_tarifa_abs, modos_tarifa_porc, avg_viajes, avg_etapas, avg_tiempo_entre_viajes + def crear_mapa_folium(df_agg, cmap, var_fex, @@ -62,41 +262,36 @@ def crear_mapa_folium(df_agg, st.image(logo) -with st.expander('Partición modal'): - - col1, col2, col3 = st.columns([1, 3, 3]) - particion_modal = levanto_tabla_sql('particion_modal') - - desc_dia_m = col1.selectbox( - 'Periodo', options=particion_modal.desc_dia.unique(), key='desc_dia_m') - tipo_dia_m = col1.selectbox( - 'Tipo de día', options=particion_modal.tipo_dia.unique(), key='tipo_dia_m') - - # Etapas - particion_modal_etapas = particion_modal[(particion_modal.desc_dia == desc_dia_m) & ( - particion_modal.tipo_dia == tipo_dia_m) & (particion_modal.tipo == 'etapas')] - if col2.checkbox('Ver datos: etapas'): - col2.write(particion_modal_etapas) - fig2 = px.bar(particion_modal_etapas, x='modo', y='modal') - fig2.update_layout(title_text='Partición modal de Etapas') - fig2.update_xaxes(title_text='Modo') - fig2.update_yaxes(title_text='Partición modal (%)') - fig2.update_traces(marker_color='brown') - col2.plotly_chart(fig2) - - # Viajes - particion_modal_viajes = particion_modal[(particion_modal.desc_dia == desc_dia_m) & ( - particion_modal.tipo_dia == tipo_dia_m) & (particion_modal.tipo == 'viajes')] - if col3.checkbox('Ver datos: viajes'): - col3.write(particion_modal_viajes) - fig = px.bar(particion_modal_viajes, x='modo', y='modal') - fig.update_layout(title_text='Partición modal de Viajes') - fig.update_xaxes(title_text='Modo') - fig.update_yaxes(title_text='Partición modal (%)') - fig.update_traces(marker_color='navy') - col3.plotly_chart(fig) +with st.expander('Partición modal', True): + + col1, col2, col3, col4 = st.columns([1, 5, 1.5, 1.5]) + particion_modal = levanto_tabla_sql('datos_particion_modal') + + desc_mes = col1.selectbox( + 'Periodo', options=particion_modal.mes.unique(), key='desc_mes') + desc_tipo_dia = col1.selectbox( + 'Tipo de día', options=particion_modal.tipo_dia.unique(), key='desc_tipo_dia') + + list_genero = particion_modal.genero.unique() + list_genero = ['Todos' if item == '-' else item for item in list_genero] + + desc_genero = col1.selectbox( + 'Genero', options=list_genero, key='desc_genero') + query = f'select * from datos_particion_modal where mes="{desc_mes}" and tipo_dia="{desc_tipo_dia}"' + if desc_genero!='Todos': + query += f'and genero = "{desc_genero}"' + + etapas_modos = levanto_tabla_sql('datos_particion_modal', query=query) + + fig, modal_etapas, modal_viajes = plot_venn_diagram(etapas_modos) + col2.pyplot(fig) + col3.write('Etapas') + col3.dataframe(modal_etapas.set_index('Modo'), height=300, width=300) + col4.write('Viajes') + col4.dataframe(modal_viajes.set_index('Modo'), height=300, width=300) + with st.expander('Distancias de viajes'): col1, col2 = st.columns([1, 4]) @@ -112,18 +307,16 @@ def crear_mapa_folium(df_agg, if col2.checkbox('Ver datos: distribución de viajes'): col2.write(hist_values) - desc_dia_d = col1.selectbox( - 'Periodo', options=hist_values.desc_dia.unique(), key='desc_dia_d') - tipo_dia_d = col1.selectbox( - 'Tipo de dia', options=hist_values.tipo_dia.unique(), key='tipo_dia_d') - dist = hist_values.Modo.unique().tolist() dist.remove('Todos') dist = ['Todos'] + dist modo_d = col1.selectbox('Modo', options=dist) + col1.write(f'Mes: {desc_mes}') + col1.write(f'Tipo de día: {desc_tipo_dia}') - hist_values = hist_values[(hist_values.desc_dia == desc_dia_d) & ( - hist_values.tipo_dia == tipo_dia_d) & (hist_values.Modo == modo_d)] + + hist_values = hist_values[(hist_values.desc_dia == desc_mes) & ( + hist_values.tipo_dia == desc_tipo_dia) & (hist_values.Modo == modo_d)] fig = px.histogram(hist_values, x='Distancia (kms)', y='Viajes', nbins=len(hist_values)) @@ -176,19 +369,18 @@ def crear_mapa_folium(df_agg, viajes_hora = levanto_tabla_sql('viajes_hora') - desc_dia_h = col1.selectbox( - 'Periodo', options=viajes_hora.desc_dia.unique(), key='desc_dia_h') - tipo_dia_h = col1.selectbox( - 'Tipo de dia', options=viajes_hora.tipo_dia.unique(), key='tipo_dia_h') modo_h = col1.selectbox( 'Modo', options=['Todos', 'Por modos'], key='modo_h') if modo_h == 'Todos': - viajes_hora = viajes_hora[(viajes_hora.desc_dia == desc_dia_h) & ( - viajes_hora.tipo_dia == tipo_dia_h) & (viajes_hora.Modo == 'Todos')] + viajes_hora = viajes_hora[(viajes_hora.desc_dia == desc_mes) & ( + viajes_hora.tipo_dia == desc_tipo_dia) & (viajes_hora.Modo == 'Todos')] else: - viajes_hora = viajes_hora[(viajes_hora.desc_dia == desc_dia_h) & ( - viajes_hora.tipo_dia == tipo_dia_h) & (viajes_hora.Modo != 'Todos')] + viajes_hora = viajes_hora[(viajes_hora.desc_dia == desc_mes) & ( + viajes_hora.tipo_dia == desc_tipo_dia) & (viajes_hora.Modo != 'Todos')] + + col1.write(f'Mes: {desc_mes}') + col1.write(f'Tipo de día: {desc_tipo_dia}') viajes_hora = viajes_hora.sort_values('Hora') if col2.checkbox('Ver datos: viajes por hora'): @@ -203,163 +395,55 @@ def crear_mapa_folium(df_agg, col2.plotly_chart(fig_horas) -# with st.expander('Líneas de deseo'): - -# col1, col2 = st.columns([1, 4]) - -# lineas_deseo = levanto_tabla_sql('lineas_deseo') - -# if len(lineas_deseo) > 0: - -# lineas_deseo = create_linestring_od(lineas_deseo) - -# desc_dia = col1.selectbox( -# 'Periodo', options=lineas_deseo.desc_dia.unique()) -# tipo_dia = col1.selectbox( -# 'Tipo de dia', options=lineas_deseo.tipo_dia.unique()) -# var_zona = col1.selectbox( -# 'Zonificación', options=lineas_deseo.var_zona.unique()) -# filtro1 = col1.selectbox( -# 'Filtro', options=lineas_deseo.filtro1.unique()) - -# df_agg = lineas_deseo[( -# (lineas_deseo.desc_dia == desc_dia) & -# (lineas_deseo.tipo_dia == tipo_dia) & -# (lineas_deseo.var_zona == var_zona) & -# (lineas_deseo.filtro1 == filtro1) -# )].copy() - -# if len(df_agg) > 0: - -# map = crear_mapa_folium(df_agg, -# cmap='BuPu', -# var_fex='Viajes', -# k_jenks=5) - -# with col2: -# st_map = st_folium(map, width=900, height=700) -# else: - -# col2.markdown(""" -# -# """, unsafe_allow_html=True) - -# col2.markdown( -# '

¡¡ No hay datos para mostrar !!

', unsafe_allow_html=True) - -# else: -# # Usar HTML para personalizar el estilo del texto -# texto_html = """ -# -#
-# No hay datos para mostrar -#
-# """ -# col2.markdown(texto_html, unsafe_allow_html=True) -# texto_html = """ -# -#
-# Verifique que los procesos se corrieron correctamente -#
-# """ -# col2.markdown(texto_html, unsafe_allow_html=True) -# with st.expander('Matrices OD'): -# col1, col2 = st.columns([1, 4]) - -# matriz = levanto_tabla_sql('matrices') - -# if len(matriz) > 0: - -# if col1.checkbox('Normalizar', value=True): -# normalize = True -# else: -# normalize = False - -# desc_dia_ = col1.selectbox( -# 'Periodo ', options=matriz.desc_dia.unique()) -# tipo_dia_ = col1.selectbox( -# 'Tipo de dia ', options=matriz.tipo_dia.unique()) -# var_zona_ = col1.selectbox( -# 'Zonificación ', options=matriz.var_zona.unique()) -# filtro1_ = col1.selectbox('Filtro ', options=matriz.filtro1.unique()) - -# matriz = matriz[((matriz.desc_dia == desc_dia_) & -# (matriz.tipo_dia == tipo_dia_) & -# (matriz.var_zona == var_zona_) & -# (matriz.filtro1 == filtro1_) -# )].copy() - -# od_heatmap = pd.crosstab( -# index=matriz['Origen'], -# columns=matriz['Destino'], -# values=matriz['Viajes'], -# aggfunc="sum", -# normalize=normalize, -# ) -# od_heatmap = (od_heatmap * 100).round(1) - -# od_heatmap = od_heatmap.reset_index() -# od_heatmap['Origen'] = od_heatmap['Origen'].str[4:] -# od_heatmap = od_heatmap.set_index('Origen') -# od_heatmap.columns = [i[4:] for i in od_heatmap.columns] - -# fig = px.imshow(od_heatmap, text_auto=True, -# color_continuous_scale='Blues',) - -# fig.update_coloraxes(showscale=False) - -# if len(od_heatmap) <= 20: -# fig.update_layout(width=800, height=800) -# elif (len(od_heatmap) > 20) & (len(od_heatmap) <= 40): -# fig.update_layout(width=1000, height=1000) -# elif len(od_heatmap) > 40: -# fig.update_layout(width=1200, height=1200) - -# col2.plotly_chart(fig) - -# else: -# st.write('No hay datos para mostrar') - -# zonas = levanto_tabla_sql('zonas') -# zonas = zonas[zonas.tipo_zona == var_zona_] - -# col1, col2 = st.columns([1, 4]) - -# if col1.checkbox('Mostrar zonificacion'): - -# # Create a folium map centered on the data -# map_center = [zonas.geometry.centroid.y.mean( -# ), zonas.geometry.centroid.x.mean()] - -# fig = Figure(width=800, height=800) -# m = folium.Map(location=map_center, zoom_start=10, -# tiles='cartodbpositron') - -# # Add GeoDataFrame to the map -# folium.GeoJson(zonas).add_to(m) - -# for idx, row in zonas.iterrows(): -# # Replace 'column_name' with the name of the column containing the detail -# detail = row['Zona'] -# point = [row['geometry'].representative_point( -# ).y, row['geometry'].representative_point().x] -# marker = folium.Marker(location=point, popup=detail) -# marker.add_to(m) - -# # Display the map using folium_static -# with col2: -# folium_static(m) +with st.expander('Género y tarifas'): + col1, col2, col3, col4 = st.columns([1, 2, 2, 2]) + socio_indicadores = levanto_tabla_sql('socio_indicadores') + + col1.write(f'Mes: {desc_mes}') + col1.write(f'Tipo de día: {desc_tipo_dia}') + + + if desc_mes != 'Todos': + st.session_state.socio_indicadores_ = socio_indicadores[(socio_indicadores.mes==desc_mes)&(socio_indicadores.tipo_dia==desc_tipo_dia)].copy() + + else: + st.session_state.socio_indicadores_ = socio_indicadores[(socio_indicadores.tipo_dia==desc_tipo_dia)].copy() + + totals, totals_porc, avg_distances, avg_times, avg_velocity, modos_genero_abs, modos_genero_porc, modos_tarifa_abs, modos_tarifa_porc, avg_viajes, avg_etapas, avg_tiempo_entre_viajes = traigo_socio_indicadores(st.session_state.socio_indicadores_) + + + if totals is not None: + col2.markdown("

Total de viajes por género y tarifa

", unsafe_allow_html=True) + col2.table(totals) + col3.markdown("

Porcentaje de viajes por género y tarifa

", unsafe_allow_html=True) + col3.table(totals_porc.round(2).astype(str)) + + col2.markdown("

Cantidad promedio de viajes por género y tarifa

", unsafe_allow_html=True) + col2.table(avg_viajes.round(2).astype(str)) + col3.markdown("

Cantidad promedio de etapas por género y tarifa

", unsafe_allow_html=True) + col3.table(avg_etapas.round(2).astype(str)) + + + col2.markdown("

Total de etapas por género y modo

", unsafe_allow_html=True) + col2.table(modos_genero_abs) + col3.markdown("

Porcentaje de etapas por género y modo

", unsafe_allow_html=True) + col3.table(modos_genero_porc.round(2).astype(str)) + + col2.markdown("

Total de etapas por tarifa y modo

", unsafe_allow_html=True) + col2.table(modos_tarifa_abs) + col3.markdown("

Porcentaje de etapas por tarifa y modo

", unsafe_allow_html=True) + col3.table(modos_tarifa_porc.round(2).astype(str)) + + col2.markdown("

Distancias promedio (kms)

", unsafe_allow_html=True) + col2.table(avg_distances.round(2).astype(str)) + + col3.markdown("

Tiempos promedio (minutos)

", unsafe_allow_html=True) + col3.table(avg_times.round(2).astype(str)) + + col2.markdown("

Velocidades promedio (kms/hora)

", unsafe_allow_html=True) + col2.table(avg_velocity.round(2).astype(str)) + + col3.markdown("

Tiempos promedio entre viajes (minutos)

", unsafe_allow_html=True) + col3.table(avg_tiempo_entre_viajes.round(2).astype(str)) + else: + col2.write('No hay datos para mostrar') diff --git "a/urbantrips/dashboard/pages/3_L\303\255neas de Deseo.py" "b/urbantrips/dashboard/pages/3_L\303\255neas de Deseo.py" index 8d1bc33..95dc3af 100644 --- "a/urbantrips/dashboard/pages/3_L\303\255neas de Deseo.py" +++ "b/urbantrips/dashboard/pages/3_L\303\255neas de Deseo.py" @@ -13,7 +13,7 @@ create_data_folium, traigo_indicadores, extract_hex_colors_from_cmap, iniciar_conexion_db, normalize_vars, - bring_latlon, traigo_zonas_values + bring_latlon, traigo_zonas_values, get_h3_indices_in_geometry ) def crear_mapa_lineas_deseo(df_viajes, @@ -71,6 +71,10 @@ def crear_mapa_lineas_deseo(df_viajes, bins = [df_etapas[var_fex].min()-1] + \ mapclassify.FisherJenks( df_etapas[var_fex], k=k_jenks-3).bins.tolist() + except ValueError: + bins = [df_etapas[var_fex].min()-1] + \ + mapclassify.FisherJenks( + df_etapas[var_fex], k=1).bins.tolist() range_bins = range(0, len(bins)-1) bins_labels = [ @@ -94,20 +98,40 @@ def crear_mapa_lineas_deseo(df_viajes, # Viajes line_w = 0.5 if len(df_viajes) > 0: + try: - bins = [df_viajes[var_fex].min()-1] + \ - mapclassify.FisherJenks( - df_viajes[var_fex], k=k_jenks).bins.tolist() + # Intentar clasificar con k clases + bins = [df_viajes[var_fex].min() - 1] + \ + mapclassify.FisherJenks(df_viajes[var_fex], k=k_jenks).bins.tolist() except ValueError: - bins = [df_viajes[var_fex].min()-1] + \ - mapclassify.FisherJenks( - df_viajes[var_fex], k=k_jenks-2).bins.tolist() - + # Si falla, reducir k dinámicamente + while k_jenks > 1: + try: + bins = [df_viajes[var_fex].min() - 1] + \ + mapclassify.FisherJenks(df_viajes[var_fex], k=k_jenks - 1).bins.tolist() + break + except ValueError: + k_jenks -= 1 + else: + # Si no se puede crear ni una categoría, asignar un único bin + bins = [df_viajes[var_fex].min() - 1, df_viajes[var_fex].max()] + + # Eliminar duplicados en bins + bins = sorted(set(bins)) + + # Crear etiquetas únicas para los bins range_bins = range(0, len(bins)-1) bins_labels = [ - f'{int(bins[n])} a {int(bins[n+1])} viajes' for n in range_bins] + f'{int(bins[n])} a {int(bins[n+1])} viajes' for n in range_bins + ] + + # Garantizar que las etiquetas sean únicas + bins_labels = [f"{label} ({i})" for i, label in enumerate(bins_labels)] + + # Aplicar pd.cut con ordered=False para evitar el error df_viajes['cuts'] = pd.cut( - df_viajes[var_fex], bins=bins, labels=bins_labels) + df_viajes[var_fex], bins=bins, labels=bins_labels, ordered=False + ) n = 0 for i in bins_labels: @@ -124,13 +148,25 @@ def crear_mapa_lineas_deseo(df_viajes, if len(origenes) > 0: try: - bins = [origenes['factor_expansion_linea'].min()-1] + \ - mapclassify.FisherJenks( - origenes['factor_expansion_linea'], k=5).bins.tolist() + # Intentar clasificar con k inicial + bins = [origenes['factor_expansion_linea'].min() - 1] + \ + mapclassify.FisherJenks(origenes['factor_expansion_linea'], k=5).bins.tolist() except ValueError: - bins = [origenes['factor_expansion_linea'].min()-1] + \ - mapclassify.FisherJenks( - origenes['factor_expansion_linea'], k=5-3).bins.tolist() + # Reducir k dinámicamente en caso de error + k = 5 + while k > 1: + try: + bins = [origenes['factor_expansion_linea'].min() - 1] + \ + mapclassify.FisherJenks(origenes['factor_expansion_linea'], k=k).bins.tolist() + break + except ValueError: + k -= 1 + else: + # Si no se pueden generar bins, usar un único bin + bins = [origenes['factor_expansion_linea'].min() - 1, origenes['factor_expansion_linea'].max()] + + print(bins) + range_bins = range(0, len(bins)-1) bins_labels = [ @@ -265,54 +301,7 @@ def levanto_tabla_sql_local(tabla_sql, tabla_tipo="dash", query=''): return tabla -def traigo_socio_indicadores(socio_indicadores): - totals = None - totals_porc = 0 - avg_distances = 0 - avg_times = 0 - avg_velocity = 0 - modos_genero_abs = 0 - modos_genero_porc = 0 - modos_tarifa_abs = 0 - modos_tarifa_porc = 0 - avg_viajes = 0 - avg_etapas = 0 - avg_tiempo_entre_viajes = 0 - - if len(socio_indicadores) > 0: - - df = socio_indicadores[socio_indicadores.tabla=='viajes-genero-tarifa'].copy() - totals = pd.crosstab(values=df.factor_expansion_linea, columns=df.Genero, index=df.Tarifa, aggfunc='sum', margins=True, margins_name='Total', normalize=False).fillna(0).round().astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) - totals_porc = (pd.crosstab(values=df.factor_expansion_linea, columns=df.Genero, index=df.Tarifa, aggfunc='sum', margins=True, margins_name='Total', normalize=True) * 100).round(2) - - modos = socio_indicadores[socio_indicadores.tabla=='etapas-genero-modo'].copy() - modos_genero_abs = pd.crosstab(values=modos.factor_expansion_linea, index=[modos.Genero], columns=modos.Modo, aggfunc='sum', normalize=False, margins=True, margins_name='Total').fillna(0).astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) - modos_genero_porc = (pd.crosstab(values=modos.factor_expansion_linea, index=modos.Genero, columns=modos.Modo, aggfunc='sum', normalize=True, margins=True, margins_name='Total') * 100).round(2) - - modos = socio_indicadores[socio_indicadores.tabla=='etapas-tarifa-modo'].copy() - modos_tarifa_abs = pd.crosstab(values=modos.factor_expansion_linea, index=[modos.Tarifa], columns=modos.Modo, aggfunc='sum', normalize=False, margins=True, margins_name='Total').fillna(0).astype(int).apply(lambda col: col.map(lambda x: f'{x:,.0f}'.replace(',', '.'))) - modos_tarifa_porc = (pd.crosstab(values=modos.factor_expansion_linea, index=modos.Tarifa, columns=modos.Modo, aggfunc='sum', normalize=True, margins=True, margins_name='Total') * 100).round(2) - - avg_distances = pd.crosstab(values=df.Distancia, columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) - avg_times = pd.crosstab(values=df['Tiempo de viaje'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) - avg_velocity = pd.crosstab(values=df['Velocidad'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) - avg_etapas = pd.crosstab(values=df['Etapas promedio'], columns=df.Genero, index=df.Tarifa, margins=True, margins_name='Total',aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).round(2).fillna('') - user = socio_indicadores[socio_indicadores.tabla=='usuario-genero-tarifa'].copy() - avg_viajes = pd.crosstab(values=user['Viajes promedio'], - index=[user.Tarifa], - columns=user.Genero, - margins=True, - margins_name='Total', - aggfunc=lambda x: (x * user.loc[x.index, 'factor_expansion_linea']).sum() / user.loc[x.index, 'factor_expansion_linea'].sum(),).round(2).fillna('') - - avg_tiempo_entre_viajes = pd.crosstab(values=df['Tiempo entre viajes'], - columns=df.Genero, - index=df.Tarifa, - margins=True, - margins_name='Total', - aggfunc=lambda x: (x * df.loc[x.index, 'factor_expansion_linea']).sum() / df.loc[x.index, 'factor_expansion_linea'].sum(), ).fillna(0).round(2) - - return totals, totals_porc, avg_distances, avg_times, avg_velocity, modos_genero_abs, modos_genero_porc, modos_tarifa_abs, modos_tarifa_porc, avg_viajes, avg_etapas, avg_tiempo_entre_viajes + # Función para detectar cambios def hay_cambios_en_filtros(current, last): @@ -354,7 +343,7 @@ def hay_cambios_en_filtros(current, last): # st.session_state.etapas_all = st.session_state.etapas_all[st.session_state.etapas_all.factor_expansion_linea > 0].copy() - general, modal, distancias = traigo_indicadores('all') + # general, modal, distancias = traigo_indicadores('all') # Inicializar valores de `st.session_state` solo si no existen if 'last_filters' not in st.session_state: @@ -403,7 +392,10 @@ def hay_cambios_en_filtros(current, last): desc_etapas = False zonas_values_all = ['Todos'] + zonas_values[zonas_values.zona == desc_zona].Nombre.unique().tolist() - desc_zonas_values = col1.selectbox('Filtro', options=zonas_values_all) + desc_zonas_values1 = col1.selectbox('Filtro 1', options=zonas_values_all, key='filtro1') + desc_zonas_values2 = col1.selectbox('Filtro 2', options=zonas_values_all, key='filtro2') + + desc_origenes = col1.checkbox( @@ -433,7 +425,8 @@ def hay_cambios_en_filtros(current, last): 'modo_agregado': None if modos_list == 'Todos' else modos_list, 'rango_hora': None if rango_hora == 'Todos' else rango_hora, 'distancia': None if distancia == 'Todas' else distancia, - 'desc_zonas_values': None if desc_zonas_values == 'Todos' else desc_zonas_values, + 'desc_zonas_values1': None if desc_zonas_values1 == 'Todos' else desc_zonas_values1, + 'desc_zonas_values2': None if desc_zonas_values2 == 'Todos' else desc_zonas_values2, } current_options = { 'desc_etapas': desc_etapas, @@ -451,17 +444,35 @@ def hay_cambios_en_filtros(current, last): if hay_cambios_en_filtros(current_filters, st.session_state.last_filters): query = "" - conditions = " AND ".join(f"{key} = '{value}'" for key, value in current_filters.items() if (value is not None)&(key != 'desc_zonas_values')) + conditions = " AND ".join(f"{key} = '{value}'" for key, value in current_filters.items() if (value is not None)&(key != 'desc_zonas_values1')&(key != 'desc_zonas_values2')) if conditions: query += f" WHERE {conditions}" - conditions_etapas = '' - conditions_matrices = '' - if desc_zonas_values != 'Todos': - conditions_etapas = f" AND (inicio_norm = '{desc_zonas_values}' OR transfer1_norm = '{desc_zonas_values}' OR transfer2_norm = '{desc_zonas_values}' OR fin_norm = '{desc_zonas_values}')" - conditions_matrices = f" AND (inicio = '{desc_zonas_values}' OR fin = '{desc_zonas_values}')" - query_etapas = query + conditions_etapas - query_matrices = query + conditions_matrices + conditions_etapas1 = '' + conditions_matrices1 = '' + st.session_state['zona_1'] = [] + + if desc_zonas_values1 != 'Todos': + + conditions_etapas1 = f" AND (inicio_norm = '{desc_zonas_values1}' OR transfer1_norm = '{desc_zonas_values1}' OR transfer2_norm = '{desc_zonas_values1}' OR fin_norm = '{desc_zonas_values1}')" + conditions_matrices1 = f" AND (inicio = '{desc_zonas_values1}' OR fin = '{desc_zonas_values1}')" + + geometry = zonificaciones[(zonificaciones.zona == desc_zona)&(zonificaciones.id==desc_zonas_values1)].geometry.values[0] + h3_indices = get_h3_indices_in_geometry(geometry, 8) + st.session_state['zona_1'].extend(h3_indices) + + conditions_etapas2 = '' + conditions_matrices2 = '' + st.session_state['zona_2'] = [] + if desc_zonas_values2 != 'Todos': + conditions_etapas2 = f" AND (inicio_norm = '{desc_zonas_values2}' OR transfer1_norm = '{desc_zonas_values2}' OR transfer2_norm = '{desc_zonas_values2}' OR fin_norm = '{desc_zonas_values2}')" + conditions_matrices2 = f" AND (inicio = '{desc_zonas_values2}' OR fin = '{desc_zonas_values2}')" + geometry = zonificaciones[(zonificaciones.zona == desc_zona)&(zonificaciones.id==desc_zonas_values2)].geometry.values[0] + h3_indices = get_h3_indices_in_geometry(geometry, 8) + st.session_state['zona_2'].extend(h3_indices) + + query_etapas = query + conditions_etapas1 + conditions_etapas2 + query_matrices = query + conditions_matrices1 + conditions_matrices2 st.session_state.etapas_ = levanto_tabla_sql_local('agg_etapas', tabla_tipo='dash', query=f"SELECT * FROM agg_etapas{query_etapas}") st.session_state.matrices_ = levanto_tabla_sql_local('agg_matrices', tabla_tipo='dash', query=f"SELECT * FROM agg_matrices{query_matrices}") @@ -523,10 +534,6 @@ def hay_cambios_en_filtros(current, last): ]] .mean().round(2) - st.session_state.general_ = general.loc[general.mes==desc_mes, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') - st.session_state.modal_ = modal.loc[modal.mes==desc_mes, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') - st.session_state.distancias_ = distancias.loc[distancias.mes==desc_mes, ['Tipo', 'Indicador', 'Valor']].set_index('Tipo') - if transf_list == 'Todos': st.session_state.desc_transfers = True else: @@ -633,28 +640,27 @@ def hay_cambios_en_filtros(current, last): col2.text("No hay datos suficientes para mostrar el mapa.") -with st.expander('Indicadores'): - col1, col2, col3 = st.columns([2, 2, 2]) - - if len(st.session_state.etapas_) > 0: - col1.table(st.session_state.general_) - col2.table(st.session_state.modal_) - col3.table(st.session_state.distancias_) - with st.expander('Matrices'): col1, col2 = st.columns([1, 4]) + if len(st.session_state.matriz) > 0: - # col2.table(st.session_state.matriz) - tipo_matriz = col1.selectbox( 'Variable', options=['Viajes', 'Distancia promedio (kms)', 'Tiempo promedio (min)', 'Velocidad promedio (km/h)']) - + normalize = False if tipo_matriz == 'Viajes': var_matriz = 'factor_expansion_linea' normalize = col1.checkbox('Normalizar', value=True) + + col1.write(f'Mes: {desc_mes}') + col1.write(f'Tipo día: {desc_tipo_dia}') + col1.write(f'Transferencias: {transf_list}') + col1.write(f'Modos: {modos_list}') + col1.write(f'Rango hora: {rango_hora}') + col1.write(f'Distancias: {distancia}') + if tipo_matriz == 'Distancia promedio (kms)': var_matriz = 'distance_osm_drive' if tipo_matriz == 'Tiempo promedio (min)': @@ -696,45 +702,265 @@ def hay_cambios_en_filtros(current, last): else: col2.text('No hay datos para mostrar') -with st.expander('Género y tarifas'): - col1, col2, col3, col4 = st.columns([1, 2, 2, 2]) - totals, totals_porc, avg_distances, avg_times, avg_velocity, modos_genero_abs, modos_genero_porc, modos_tarifa_abs, modos_tarifa_porc, avg_viajes, avg_etapas, avg_tiempo_entre_viajes = traigo_socio_indicadores(st.session_state.socio_indicadores_) - if totals is not None: - col2.markdown("

Total de viajes por género y tarifa

", unsafe_allow_html=True) - col2.table(totals) - col3.markdown("

Porcentaje de viajes por género y tarifa

", unsafe_allow_html=True) - col3.table(totals_porc.round(2).astype(str)) +with st.expander('Zonas', expanded=False): + col1, col2, col3, col4, col5 = st.columns([1, 2, 2, 2, 2]) + zona1 = st.session_state['zona_1'] + zona2 = st.session_state['zona_2'] + + + if len(zona1) > 0: + query1 = f"SELECT * FROM etapas_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND ({desc_zona}_o = '{desc_zonas_values1}');" + etapas1 = levanto_tabla_sql_local('etapas_agregadas', tabla_tipo='dash', query=query1) + + + if len(etapas1) > 0: + etapas1['Zona_1'] = 'Zona 1' + + ## Viajes + query1 = f"SELECT * FROM viajes_agregados WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND {desc_zona}_o = '{desc_zonas_values1}';" + viajes1 = levanto_tabla_sql_local('viajes_agregados', tabla_tipo='dash', query=query1) + viajes1['Zona_1'] = 'Zona 1' + + modos_e1 = etapas1.groupby(['modo', 'nombre_linea'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas', + 'nombre_linea': 'Línea', 'modo': 'Modo'}) + + modos_v1 = viajes1.groupby(['modo'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'modo': 'Modo'}) + + # Calculate the total and append as a new row + total_row1e = pd.DataFrame({ + 'Modo': ['Total'], + 'Línea': ['-'], + 'Etapas': [modos_e1['Etapas'].sum()] + }) + modos_e1 = pd.concat([modos_e1, total_row1e], ignore_index=True) + + + # Calculate the total and append as a new row + total_row1 = pd.DataFrame({ + 'Modo': ['Total'], + 'Viajes': [modos_v1['Viajes'].sum()] + }) + modos_v1 = pd.concat([modos_v1, total_row1], ignore_index=True) + + col2.markdown( + f""" +

{desc_zonas_values1}

+ """, + unsafe_allow_html=True + ) + + col2.write('Etapas') + modos_e1['Etapas'] = modos_e1['Etapas'].round() + col2.dataframe(modos_e1.set_index('Modo'), height=400, width=400) + + col3.markdown( + f""" +

+ """, + unsafe_allow_html=True + ) + col3.write('Viajes') + modos_v1['Viajes'] = modos_v1['Viajes'].round() + col3.dataframe(modos_v1.set_index('Modo'), height=400, width=300) - col2.markdown("

Cantidad promedio de viajes por género y tarifa

", unsafe_allow_html=True) - col2.table(avg_viajes.round(2).astype(str)) - col3.markdown("

Cantidad promedio de etapas por género y tarifa

", unsafe_allow_html=True) - col3.table(avg_etapas.round(2).astype(str)) + if len(zona2) > 0: + + query2 = f"SELECT * FROM etapas_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND ({desc_zona}_o = '{desc_zonas_values2}');" + etapas2 = levanto_tabla_sql_local('etapas_agregadas', tabla_tipo='dash', query=query2) + + if len(etapas2) > 0: + + ## Etapas + if len(etapas2) > 0: + etapas2['Zona_2'] = 'Zona 2' + + ## Viajes + query2 = f"SELECT * FROM viajes_agregados WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND {desc_zona}_o = '{desc_zonas_values2}';" + viajes2 = levanto_tabla_sql_local('viajes_agregados', tabla_tipo='dash', query=query2) + viajes2['Zona_2'] = 'Zona 2' - - col2.markdown("

Total de etapas por género y modo

", unsafe_allow_html=True) - col2.table(modos_genero_abs) - col3.markdown("

Porcentaje de etapas por género y modo

", unsafe_allow_html=True) - col3.table(modos_genero_porc.round(2).astype(str)) + modos_e2 = etapas2.groupby(['modo', 'nombre_linea'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas', + 'nombre_linea': 'Línea', 'modo': 'Modo'}) - col2.markdown("

Total de etapas por tarifa y modo

", unsafe_allow_html=True) - col2.table(modos_tarifa_abs) - col3.markdown("

Porcentaje de etapas por tarifa y modo

", unsafe_allow_html=True) - col3.table(modos_tarifa_porc.round(2).astype(str)) + modos_v2 = viajes2.groupby(['modo'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'modo': 'Modo'}) + # Calculate the total and append as a new row + total_row2e = pd.DataFrame({ + 'Modo': ['Total'], + 'Línea': ['-'], + 'Etapas': [modos_e2['Etapas'].sum()] + }) + modos_e2 = pd.concat([modos_e2, total_row2e], ignore_index=True) + + # Calculate the total and append as a new row + total_row2 = pd.DataFrame({ + 'Modo': ['Total'], + 'Viajes': [modos_v2['Viajes'].sum()] + }) + modos_v2 = pd.concat([modos_v2, total_row2], ignore_index=True) + + col4.markdown( + f""" +

{desc_zonas_values2}

+ """, + unsafe_allow_html=True + ) + col4.write('Etapas') + modos_e2['Etapas'] = modos_e2['Etapas'].round() + col4.dataframe(modos_e2.set_index('Modo'), height=400, width=400) - col2.markdown("

Distancias promedio (kms)

", unsafe_allow_html=True) - col2.table(avg_distances.round(2).astype(str)) + modos_v2['Viajes'] = modos_v2['Viajes'].round() + col5.markdown( + f""" +

+ """, + unsafe_allow_html=True + ) - col3.markdown("

Tiempos promedio (minutos)

", unsafe_allow_html=True) - col3.table(avg_times.round(2).astype(str)) + col5.write('Viajes') + col5.dataframe(modos_v2.set_index('Modo'), height=400, width=300) + +with st.expander('Viajes entre zonas', expanded=True): + col1, col2, col3 = st.columns([1, 2, 4]) + + transferencias_modos = pd.DataFrame([]) + modos_e = pd.DataFrame([]) + modos_v = pd.DataFrame([]) + transferencias = pd.DataFrame([]) + zonasod_e = pd.DataFrame([]) + zonasod_v = pd.DataFrame([]) - col2.markdown("

Velocidades promedio (kms/hora)

", unsafe_allow_html=True) - col2.table(avg_velocity.round(2).astype(str)) + if len(zona1) > 0 and len(zona2) > 0: + + col1.write(f'Mes: {desc_mes}') + col1.write(f'Tipo día: {desc_tipo_dia}') + col1.write(f'Zona 1: {desc_zonas_values1}') + col1.write(f'Zona 2: {desc_zonas_values2}') + + ## Etapas + h3_values = [desc_zonas_values1, desc_zonas_values2] + h3_values = ', '.join(f"'{valor}'" for valor in h3_values) + query = f"SELECT * FROM etapas_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND ({desc_zona}_o IN ({h3_values}) OR {desc_zona}_d IN ({h3_values}));" + etapas = levanto_tabla_sql_local('etapas_agregadas', tabla_tipo='dash', query=query) + + if len(etapas) > 0: + + etapas['Zona_1'] = '' + etapas['Zona_2'] = '' + etapas.loc[etapas.h3_o.isin(zona1), 'Zona_1'] = 'Zona 1' + etapas.loc[etapas.h3_o.isin(zona2), 'Zona_1'] = 'Zona 2' + etapas.loc[etapas.h3_d.isin(zona1), 'Zona_2'] = 'Zona 1' + etapas.loc[etapas.h3_d.isin(zona2), 'Zona_2'] = 'Zona 2' + etapas = etapas[(etapas.Zona_1 != '') & (etapas.Zona_2 != '') & (etapas.Zona_1 != etapas.Zona_2)] + + etapas = etapas.fillna('') + + zonasod_e = etapas.groupby(['Zona_1', 'Zona_2'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas'}) #.round() + zonasod_e['Etapas'] = zonasod_e['Etapas'].apply(lambda x: f"{int(x):,}") + + zonasod_e['Zonas'] = zonasod_e['Zona_1'] + ' - ' + zonasod_e['Zona_2'] + zonasod_e = zonasod_e[['Zonas', 'Etapas']] + + modos_e = etapas.groupby(['modo', 'nombre_linea'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'nombre_linea': 'Líneas', + 'modo': 'Modo'}) #.round() + + + ## Viajes + # ({desc_zona}_o = '{desc_zonas_values2}') + h3_values = [desc_zonas_values1, desc_zonas_values2] + h3_values = ', '.join(f"'{valor}'" for valor in h3_values) + query = f"SELECT * FROM viajes_agregados WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND ({desc_zona}_o IN ({h3_values}) OR {desc_zona}_d IN ({h3_values}));" + viajes = levanto_tabla_sql_local('viajes_agregados', tabla_tipo='dash', query=query) + if len(viajes) > 0: + + viajes['Zona_1'] = '' + viajes['Zona_2'] = '' + viajes.loc[viajes.h3_o.isin(zona1), 'Zona_1'] = 'Zona 1' + viajes.loc[viajes.h3_o.isin(zona2), 'Zona_1'] = 'Zona 2' + viajes.loc[viajes.h3_d.isin(zona1), 'Zona_2'] = 'Zona 1' + viajes.loc[viajes.h3_d.isin(zona2), 'Zona_2'] = 'Zona 2' + viajes = viajes[(viajes.Zona_1 != '') & (viajes.Zona_2 != '') & (viajes.Zona_1 != viajes.Zona_2)] + + zonasod_v = viajes.groupby(['Zona_1', 'Zona_2'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes'}) + + zonasod_v['Zonas'] = zonasod_v['Zona_1'] + ' - ' + zonasod_v['Zona_2'] + zonasod_v = zonasod_v[['Zonas', 'Viajes']] + zonasod_v['Viajes'] = zonasod_v['Viajes'].apply(lambda x: f"{int(x):,}") + + modos_v = viajes.groupby(['modo'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'modo': 'Modo'}) + + if len(modos_v)>0: + # Calculate the total and append as a new row + total_row = pd.DataFrame({ + 'Modo': ['Total'], + 'Viajes': [modos_v['Viajes'].sum()] + }) + modos_v = pd.concat([modos_v, total_row], ignore_index=True) + modos_v['Viajes'] = modos_v['Viajes'].apply(lambda x: f"{int(x):,}") + + # Transferencias + h3_values = [desc_zonas_values1, desc_zonas_values2] + h3_values = ', '.join(f"'{valor}'" for valor in h3_values) + query = f"SELECT * FROM transferencias_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND ({desc_zona}_o IN ({h3_values}) OR {desc_zona}_d IN ({h3_values}));" + transferencias = levanto_tabla_sql_local('transferencias_agregadas', tabla_tipo='dash', query=query) + + if len(transferencias) > 0: - col3.markdown("

Tiempos promedio entre viajes (minutos)

", unsafe_allow_html=True) - col3.table(avg_tiempo_entre_viajes.round(2).astype(str)) - else: - col2.write('No hay datos para mostrar') + transferencias['Zona_1'] = '' + transferencias['Zona_2'] = '' + transferencias.loc[transferencias.h3_o.isin(zona1), 'Zona_1'] = 'Zona 1' + transferencias.loc[transferencias.h3_o.isin(zona2), 'Zona_1'] = 'Zona 2' + transferencias.loc[transferencias.h3_d.isin(zona1), 'Zona_2'] = 'Zona 1' + transferencias.loc[transferencias.h3_d.isin(zona2), 'Zona_2'] = 'Zona 2' + transferencias = transferencias[(transferencias.Zona_1 != '') & (transferencias.Zona_2 != '') & (transferencias.Zona_1 != transferencias.Zona_2)] + + transferencias = transferencias.fillna('') + + transferencias = transferencias.groupby(['modo', + 'seq_lineas'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'modo':'Modo', 'seq_lineas':'Líneas'}).sort_values('Viajes', ascending=False) + + # Calculate the total and append as a new row + if len(transferencias)>0: + total_rowe = pd.DataFrame({ + 'Modo': ['Total'], + 'Líneas': ['-'], + 'Viajes': [transferencias['Viajes'].sum()] + }) + transferencias = pd.concat([transferencias, total_rowe], ignore_index=True) + transferencias['Viajes'] = transferencias['Viajes'].apply(lambda x: f"{int(x):,}") + + + # Muestro resultados en el dashboard + col2.write('Etapas') + if len(zonasod_e) > 0: + col2.dataframe(zonasod_e.set_index('Zonas'), height=100, width=300) + else: + col2.write('No hay datos para mostrar') + + col2.write('Viajes') + if len(zonasod_v): + col2.dataframe(zonasod_v.set_index('Zonas'), height=100, width=300) + else: + col2.write('No hay datos para mostrar') + + col2.write('Modal') + if len(modos_v)>0: + col2.dataframe(modos_v.set_index('Modo'), height=300, width=300) + else: + col2.write('No hay datos para mostrar') + + col3.write('Viajes por líneas') + if len(transferencias)>0: + col3.dataframe(transferencias.set_index('Modo'), height=700, width=800) + else: + col3.write('No hay datos para mostrar') + + diff --git a/urbantrips/dashboard/pages/4_Poligonos.py b/urbantrips/dashboard/pages/4_Poligonos.py index 43b82e2..3891c42 100644 --- a/urbantrips/dashboard/pages/4_Poligonos.py +++ b/urbantrips/dashboard/pages/4_Poligonos.py @@ -53,6 +53,10 @@ def crear_mapa_poligonos(df_viajes, bins = [df_etapas[var_fex].min()-1] + \ mapclassify.FisherJenks( df_etapas[var_fex], k=k_jenks-3).bins.tolist() + except ValueError: + bins = [df_etapas[var_fex].min()-1] + \ + mapclassify.FisherJenks( + df_etapas[var_fex], k=1).bins.tolist() range_bins = range(0, len(bins)-1) bins_labels = [ @@ -77,13 +81,21 @@ def crear_mapa_poligonos(df_viajes, line_w = 0.5 if len(df_viajes) > 0: try: - bins = [df_viajes[var_fex].min()-1] + \ - mapclassify.FisherJenks( - df_viajes[var_fex], k=k_jenks).bins.tolist() + # Intentar clasificar con k clases + bins = [df_viajes[var_fex].min() - 1] + \ + mapclassify.FisherJenks(df_viajes[var_fex], k=k_jenks).bins.tolist() except ValueError: - bins = [df_viajes[var_fex].min()-1] + \ - mapclassify.FisherJenks( - df_viajes[var_fex], k=k_jenks-2).bins.tolist() + # Si falla, reducir k dinámicamente + while k_jenks > 1: + try: + bins = [df_viajes[var_fex].min() - 1] + \ + mapclassify.FisherJenks(df_viajes[var_fex], k=k_jenks - 1).bins.tolist() + break + except ValueError: + k_jenks -= 1 + else: + # Si no se puede crear ni una categoría, asignar un único bin + bins = [df_viajes[var_fex].min() - 1, df_viajes[var_fex].max()] range_bins = range(0, len(bins)-1) bins_labels = [ @@ -177,7 +189,7 @@ def crear_mapa_poligonos(df_viajes, name=poly.id.values[0], style_function=lambda feature: { 'fillColor': 'grey', - 'color': 'black', + 'color': 'white', 'weight': 2, 'fillOpacity': .5, @@ -361,7 +373,8 @@ def hay_cambios_en_filtros(current, last): desc_etapas = False zonas_values_all = ['Todos'] + zonas_values[zonas_values.zona == desc_zona].Nombre.unique().tolist() - desc_zonas_values = col1.selectbox('Filtro', options=zonas_values_all) + desc_zonas_values1 = col1.selectbox('Filtro 1', options=zonas_values_all, key='filtro1') + desc_zonas_values2 = col1.selectbox('Filtro 2', options=zonas_values_all, key='filtro2') desc_origenes = col1.checkbox( ':blue[Origenes]', value=False) @@ -370,7 +383,7 @@ def hay_cambios_en_filtros(current, last): ':orange[Destinos]', value=False) desc_zonif = col1.checkbox( - 'Mostrar zonificación', value=False) + 'Mostrar zonificación', value=True) if desc_zonif: st.session_state.zonif = zonificaciones[zonificaciones.zona == desc_zona] else: @@ -401,7 +414,8 @@ def hay_cambios_en_filtros(current, last): 'distancia': None if distancia == 'Todas' else distancia, 'coincidencias': None if desc_cuenca == False else True, 'id_polygon': st.session_state.desc_poly, - 'desc_zonas_values': None if desc_zonas_values == 'Todos' else desc_zonas_values, + 'desc_zonas_values1': None if desc_zonas_values1 == 'Todos' else desc_zonas_values1, + 'desc_zonas_values2': None if desc_zonas_values2 == 'Todos' else desc_zonas_values2, } current_options = { 'desc_etapas': desc_etapas, 'desc_viajes': desc_viajes, @@ -420,23 +434,31 @@ def hay_cambios_en_filtros(current, last): if hay_cambios_en_filtros(current_filters, st.session_state.last_filters): query = "" - conditions = " AND ".join(f"{key} = '{value}'" for key, value in current_filters.items() if (value is not None)&(key != 'desc_zonas_values')) + conditions = " AND ".join(f"{key} = '{value}'" for key, value in current_filters.items() if (value is not None)&(key != 'desc_zonas_values1')&(key != 'desc_zonas_values2')) if conditions: query += f" WHERE {conditions}" - conditions_etapas = '' - conditions_matrices = '' - if desc_zonas_values != 'Todos': - conditions_etapas = f" AND (inicio_norm = '{desc_zonas_values}' OR transfer1_norm = '{desc_zonas_values}' OR transfer2_norm = '{desc_zonas_values}' OR fin_norm = '{desc_zonas_values}')" - conditions_matrices = f" AND (inicio = '{desc_zonas_values}' OR fin = '{desc_zonas_values}')" - query_etapas = query + conditions_etapas - query_matrices = query + conditions_matrices + conditions_etapas1 = '' + conditions_matrices1 = '' + if desc_zonas_values1 != 'Todos': + conditions_etapas1 = f" AND (inicio_norm = '{desc_zonas_values1}' OR transfer1_norm = '{desc_zonas_values1}' OR transfer2_norm = '{desc_zonas_values1}' OR fin_norm = '{desc_zonas_values1}')" + conditions_matrices1 = f" AND (inicio = '{desc_zonas_values1}' OR fin = '{desc_zonas_values1}')" + + + conditions_etapas2 = '' + conditions_matrices2 = '' + if desc_zonas_values2 != 'Todos': + conditions_etapas2 = f" AND (inicio_norm = '{desc_zonas_values2}' OR transfer1_norm = '{desc_zonas_values2}' OR transfer2_norm = '{desc_zonas_values2}' OR fin_norm = '{desc_zonas_values2}')" + conditions_matrices2 = f" AND (inicio = '{desc_zonas_values2}' OR fin = '{desc_zonas_values2}')" + + query_etapas = query + conditions_etapas1 + conditions_etapas2 + query_matrices = query + conditions_matrices1 + conditions_matrices2 + st.session_state.etapas_ = levanto_tabla_sql_local('poly_etapas', tabla_tipo='dash', query=f"SELECT * FROM poly_etapas{query_etapas}") st.session_state.matrices_ = levanto_tabla_sql_local('poly_matrices', tabla_tipo='dash', query=f"SELECT * FROM poly_matrices{query_matrices}") - if len(st.session_state.etapas_)==0: col2.write('No hay datos para mostrar') else: diff --git "a/urbantrips/dashboard/pages/5_An\303\241lisis de zonas.py" "b/urbantrips/dashboard/pages/5_An\303\241lisis de zonas.py" index 18f6c69..4d50fad 100644 --- "a/urbantrips/dashboard/pages/5_An\303\241lisis de zonas.py" +++ "b/urbantrips/dashboard/pages/5_An\303\241lisis de zonas.py" @@ -10,7 +10,7 @@ from folium import plugins from shapely import wkt from dash_utils import ( - iniciar_conexion_db, get_logo, bring_latlon + iniciar_conexion_db, get_logo, bring_latlon, get_h3_indices_in_geometry ) from streamlit_folium import folium_static pd.options.display.float_format = '{:,.0f}'.format @@ -48,11 +48,7 @@ def traigo_mes_dia(): tipo_dia = mes_dia.tipo_dia.values.tolist() return mes, tipo_dia -# Convert geometry to H3 indices -def get_h3_indices_in_geometry(geometry, resolution): - geojson = mapping(geometry) - h3_indices = list(h3.polyfill(geojson, resolution, geo_json_conformant=True)) - return h3_indices + # Convert H3 indices to GeoDataFrame def h3_indices_to_gdf(h3_indices): @@ -66,7 +62,6 @@ def h3_indices_to_gdf(h3_indices): st.session_state['zona_2'] = [] def main(): - st.set_page_config(layout="wide") logo = get_logo() @@ -221,7 +216,9 @@ def main(): col5.dataframe(modos_v2.set_index('Modo'), height=400, width=300) with st.expander('Viajes entre zonas', expanded=True): - col1, col2, col3, col4 = st.columns([1, 2, 2, 3]) + col1, col2, col3, col4 = st.columns([1, 2, 2, 3]) + + if len(zona1) > 0 and len(zona2) > 0: h3_values = ", ".join(f"'{item}'" for item in zona1 + zona2) @@ -230,6 +227,7 @@ def main(): etapas = levanto_tabla_sql_local('etapas_agregadas', tabla_tipo='dash', query=query) if len(etapas) > 0: + etapas['Zona_1'] = '' etapas['Zona_2'] = '' @@ -240,16 +238,15 @@ def main(): etapas = etapas[(etapas.Zona_1 != '') & (etapas.Zona_2 != '') & (etapas.Zona_1 != etapas.Zona_2)] etapas = etapas.fillna('') - - zonasod_e = etapas.groupby(['Zona_1', 'Zona_2'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas'}) + + + zonasod_e = etapas.groupby(['Zona_1', 'Zona_2'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas'}).round(0) modos_e = etapas.groupby(['modo', 'nombre_linea'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Etapas', 'nombre_linea': 'Línea', - 'modo': 'Modo'}) - - col2.write(zonasod_e) - + 'modo': 'Modo'}).round(0) + # Calculate the total and append as a new row total_rowe = pd.DataFrame({ 'Modo': ['Total'], @@ -259,10 +256,18 @@ def main(): modos_e = pd.concat([modos_e, total_rowe], ignore_index=True) modos_e['Etapas'] = modos_e['Etapas'].round() + + + zonasod_e['Zonas'] = zonasod_e['Zona_1'] + ' - ' + zonasod_e['Zona_2'] + zonasod_e = zonasod_e[['Zonas', 'Etapas']] + zonasod_e['Etapas'] = zonasod_e['Etapas'].apply(lambda x: f"{int(x):,}") + - col2.write('Etapas') - col2.markdown(zonasod_e.to_html(index=False), unsafe_allow_html=True) - col2.dataframe(modos_e.set_index('Modo'), height=500, width=400) + col2.write('Etapas') + if len(zonasod_e) > 0: + col2.dataframe(zonasod_e.set_index('Zonas'), height=100, width=300) + else: + col2.write('No hay datos para mostrar') ## Viajes @@ -279,8 +284,10 @@ def main(): viajes = viajes[(viajes.Zona_1 != '') & (viajes.Zona_2 != '') & (viajes.Zona_1 != viajes.Zona_2)] zonasod_v = viajes.groupby(['Zona_1', 'Zona_2'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes'}) - - + zonasod_v['Zonas'] = zonasod_v['Zona_1'] + ' - ' + zonasod_v['Zona_2'] + zonasod_v = zonasod_v[['Zonas', 'Viajes']] + zonasod_v['Viajes'] = zonasod_v['Viajes'].apply(lambda x: f"{int(x):,}") + modos_v = viajes.groupby(['modo'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', 'modo': 'Modo'}) @@ -292,11 +299,15 @@ def main(): }) modos_v = pd.concat([modos_v, total_row], ignore_index=True) - col3.write('Viajes') - col3.markdown(zonasod_v.to_html(index=False), unsafe_allow_html=True) + col3.write('Viajes') + if len(zonasod_v): + col3.dataframe(zonasod_v.set_index('Zonas'), height=100, width=300) + else: + col3.write('No hay datos para mostrar') + modos_v['Viajes'] = modos_v['Viajes'].round() - # col3.markdown(modos_v.to_html(index=False), unsafe_allow_html=True) - col3.dataframe(modos_v.set_index('Modo'), height=500, width=300) + col2.write('Modal') + col2.dataframe(modos_v.set_index('Modo'), height=200, width=300) ## Mapa @@ -319,31 +330,30 @@ def zona_to_geometry(h3_list): with col4: output2 = st_folium(m2, width=700, height=700) - with st.expander('Viajes con transferencias', expanded=False): - col1, col2 = st.columns([1, 3]) - ## Transferencias - h3_values = ", ".join(f"'{item}'" for item in zona1 + zona2) - query = f"SELECT * FROM transferencias_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND (h3_o IN ({h3_values}) OR h3_d IN ({h3_values}));" - transferencias = levanto_tabla_sql_local('transferencias_agregadas', tabla_tipo='dash', query=query) - - if len(transferencias) > 0: - - transferencias['Zona_1'] = '' - transferencias['Zona_2'] = '' - transferencias.loc[transferencias.h3_o.isin(zona1), 'Zona_1'] = 'Zona 1' - transferencias.loc[transferencias.h3_o.isin(zona2), 'Zona_1'] = 'Zona 2' - transferencias.loc[transferencias.h3_d.isin(zona1), 'Zona_2'] = 'Zona 1' - transferencias.loc[transferencias.h3_d.isin(zona2), 'Zona_2'] = 'Zona 2' - transferencias = transferencias[(transferencias.Zona_1 != '') & (transferencias.Zona_2 != '') & (transferencias.Zona_1 != transferencias.Zona_2)] - - transferencias = transferencias.fillna('') - - transferencias = transferencias.groupby(['modo', - 'seq_lineas'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', - 'modo':'Modo', 'seq_lineas':'Líneas'}).sort_values('Viajes', ascending=False) - transferencias['Viajes'] = transferencias['Viajes'].astype(int) + ## Transferencias + h3_values = ", ".join(f"'{item}'" for item in zona1 + zona2) + query = f"SELECT * FROM transferencias_agregadas WHERE mes = '{desc_mes}' AND tipo_dia = '{desc_tipo_dia}' AND (h3_o IN ({h3_values}) OR h3_d IN ({h3_values}));" + transferencias = levanto_tabla_sql_local('transferencias_agregadas', tabla_tipo='dash', query=query) - col2.dataframe(transferencias.set_index('Modo'), height=500, width=500) + if len(transferencias) > 0: + + transferencias['Zona_1'] = '' + transferencias['Zona_2'] = '' + transferencias.loc[transferencias.h3_o.isin(zona1), 'Zona_1'] = 'Zona 1' + transferencias.loc[transferencias.h3_o.isin(zona2), 'Zona_1'] = 'Zona 2' + transferencias.loc[transferencias.h3_d.isin(zona1), 'Zona_2'] = 'Zona 1' + transferencias.loc[transferencias.h3_d.isin(zona2), 'Zona_2'] = 'Zona 2' + transferencias = transferencias[(transferencias.Zona_1 != '') & (transferencias.Zona_2 != '') & (transferencias.Zona_1 != transferencias.Zona_2)] + + transferencias = transferencias.fillna('') + + transferencias = transferencias.groupby(['modo', + 'seq_lineas'], as_index=False).factor_expansion_linea.sum().rename(columns={'factor_expansion_linea':'Viajes', + 'modo':'Modo', 'seq_lineas':'Líneas'}).sort_values('Viajes', ascending=False) + transferencias['Viajes'] = transferencias['Viajes'].astype(int) + + col3.write('Viajes por líneas') + col3.dataframe(transferencias.set_index('Modo'), height=500, width=500) if __name__ == '__main__': diff --git "a/urbantrips/dashboard/pages/6_Comparaci\303\263n de l\303\255neas.py" "b/urbantrips/dashboard/pages/6_Comparaci\303\263n de l\303\255neas.py" new file mode 100644 index 0000000..8e41fdc --- /dev/null +++ "b/urbantrips/dashboard/pages/6_Comparaci\303\263n de l\303\255neas.py" @@ -0,0 +1,306 @@ +import pandas as pd +import streamlit as st +from streamlit_folium import st_folium +from streamlit_folium import folium_static +from dash_utils import ( + get_logo) +try: + from urbantrips.utils.utils import iniciar_conexion_db + from urbantrips.utils import utils + from urbantrips.kpi import overlapping as ovl + from urbantrips.viz import overlapping as ovl_viz +except ImportError as e: + st.error(f"Falta una librería requerida: {e}. Algunas funcionalidades no estarán disponibles. \nSe requiere full acceso a Urbantrips para correr esta página") + st.stop() + + +# --- Función para levantar tablas SQL y almacenar en session_state --- +def cargar_tabla_sql(tabla_sql, tipo_conexion="dash", query=""): + if f"{tabla_sql}_{tipo_conexion}" not in st.session_state: + conn = iniciar_conexion_db(tipo=tipo_conexion) + try: + query = query or f"SELECT * FROM {tabla_sql}" + tabla = pd.read_sql_query(query, conn) + st.session_state[f"{tabla_sql}_{tipo_conexion}"] = tabla + except Exception: + st.error(f"{tabla_sql} no existe") + st.session_state[f"{tabla_sql}_{tipo_conexion}"] = pd.DataFrame() + finally: + conn.close() + return st.session_state[f"{tabla_sql}_{tipo_conexion}"] + +def seleccionar_linea(nombre_columna, key_input, key_select, branch_key, conn_insumos): + texto_a_buscar = st.text_input( + f"Ingrese el texto a buscar para {nombre_columna}", key=key_input + ) + if texto_a_buscar: + if f"df_filtrado_{texto_a_buscar}_{branch_key}" not in st.session_state: + st.session_state[f"df_filtrado_{texto_a_buscar}_{branch_key}"] = ( + metadata_lineas[ + metadata_lineas.apply( + lambda row: row.astype(str) + .str.contains(texto_a_buscar, case=False, na=False) + .any(), + axis=1, + ) + ] + ) + df_filtrado = st.session_state[f"df_filtrado_{texto_a_buscar}_{branch_key}"] + + if not df_filtrado.empty: + opciones = df_filtrado.apply( + lambda row: f"{row['nombre_linea']}", axis=1 + ).tolist() + seleccion_texto = st.selectbox( + f"Seleccione una línea de colectivo para {nombre_columna}", + opciones, + key=key_select, + ) + st.session_state[f"seleccion_{branch_key}"] = df_filtrado.iloc[ + opciones.index(seleccion_texto) + ] + + if use_branches: + if ( + f"metadata_branches_{st.session_state[f'seleccion_{branch_key}']['id_linea']}" + not in st.session_state + ): + st.session_state[ + f"metadata_branches_{st.session_state[f'seleccion_{branch_key}']['id_linea']}" + ] = pd.read_sql( + f"SELECT * FROM metadata_ramales WHERE id_linea = {st.session_state[f'seleccion_{branch_key}']['id_linea']}", + conn_insumos, + ) + metadata_branches = st.session_state[ + f"metadata_branches_{st.session_state[f'seleccion_{branch_key}']['id_linea']}" + ] + + selected_branch = st.selectbox( + "Seleccione un ramal", + metadata_branches.nombre_ramal.unique(), + key=f"branch_{branch_key}", + ) + st.session_state[f"seleccion_{branch_key}"]["branch_id"] = ( + metadata_branches.loc[ + metadata_branches.nombre_ramal == selected_branch, "id_ramal" + ].values[0] + ) + st.session_state[f"seleccion_{branch_key}"][ + "branch_name" + ] = selected_branch + else: + st.warning("No se encontró ninguna coincidencia.") + + +st.set_page_config(layout="wide") +logo = get_logo() +st.image(logo) + +try: + # --- Cargar configuraciones y conexiones en session_state --- + if "configs" not in st.session_state: + st.session_state.configs = utils.leer_configs_generales() + + configs = st.session_state.configs + h3_legs_res = configs["resolucion_h3"] + alias = configs["alias_db_data"] + use_branches = configs["lineas_contienen_ramales"] + metadata_lineas = cargar_tabla_sql("metadata_lineas", "insumos")[ + ["id_linea", "nombre_linea"] + ] + conn_insumos = iniciar_conexion_db(tipo="insumos") +except ValueError as e: + st.error(f"Falta una base de datos requerida: {e}. \nSe requiere full acceso a Urbantrips para correr esta página") + st.stop() + + +# --- Inicializar variables en session_state --- +for var in [ + "id_linea_1", + "nombre_linea_1", + "branch_id_1", + "branch_name_1", + "id_linea_2", + "nombre_linea_2", + "branch_id_2", + "branch_name_2", +]: + if var not in st.session_state: + st.session_state[var] = None + +# --- Selección de líneas y ramales con almacenamiento en session_state --- +with st.expander("Seleccionar líneas", expanded=True): + col1, col2, col3 = st.columns([1, 3, 3]) + + with col1: + h3_res_comp = st.slider( + "Resolución H3", min_value=7, max_value=h3_legs_res, value=h3_legs_res + ) + + if st.button("Comparar líneas"): + for i in [1, 2]: + if f"seleccion_{i}" in st.session_state: + st.session_state[f"id_linea_{i}"] = st.session_state[ + f"seleccion_{i}" + ]["id_linea"] + st.session_state[f"nombre_linea_{i}"] = st.session_state[ + f"seleccion_{i}" + ]["nombre_linea"] + st.session_state[f"branch_id_{i}"] = st.session_state[ + f"seleccion_{i}" + ].get("branch_id") + st.session_state[f"branch_name_{i}"] = st.session_state[ + f"seleccion_{i}" + ].get("branch_name") + st.write( + f"Línea {i} guardada:", + st.session_state[f"nombre_linea_{i}"], + "ramal", + ( + st.session_state[f"branch_name_{i}"] + if st.session_state[f"branch_name_{i}"] + else "N/A" + ), + ) + else: + st.write( + f"No hay ninguna línea seleccionada para guardar como Línea {i}." + ) + + with col2: + st.subheader("Línea base:") + seleccionar_linea("Línea base", "base_input", "base_select", "1", conn_insumos) + with col3: + st.subheader("Línea comparación:") + seleccionar_linea( + "Línea comparación", "comp_input", "comp_select", "2", conn_insumos + ) + +# --- Comparación de líneas --- +with st.expander("Comparación de líneas", expanded=True): + col1, col2 = st.columns([2, 2]) + + if st.session_state.id_linea_1 and st.session_state.id_linea_2: + if use_branches: + base_route_id, comp_route_id = int(st.session_state.branch_id_1), int( + st.session_state.branch_id_2 + ) + else: + base_route_id, comp_route_id = int(st.session_state.id_linea_1), int( + st.session_state.id_linea_2 + ) + + # Evita cálculos repetidos si ya se han realizado para las mismas líneas + if ( + f"overlapping_dict_{base_route_id}_{comp_route_id}_res{h3_res_comp}" + not in st.session_state + ): + + overlapping_dict = ovl.compute_supply_overlapping( + "weekday", + base_route_id, + comp_route_id, + "branches" if use_branches else "lines", + h3_res_comp, + ) + st.session_state[ + f"overlapping_dict_{base_route_id}_{comp_route_id}_res{h3_res_comp}" + ] = overlapping_dict + st.session_state[f"supply_overlapping_{base_route_id}_{comp_route_id}"] = ( + overlapping_dict["text_base_v_comp"] + ) + st.session_state[f"supply_overlapping_{comp_route_id}_{base_route_id}"] = ( + overlapping_dict["text_comp_v_base"] + ) + + overlapping_dict = st.session_state[ + f"overlapping_dict_{base_route_id}_{comp_route_id}_res{h3_res_comp}" + ] + + # Renderiza el primer mapa + f = ovl_viz.plot_interactive_supply_overlapping(overlapping_dict) + # Muestra la salida solo en col1 + with col1: + if f is not None: + folium_static(f, width=800, height=600) + st.write( + st.session_state[ + f"supply_overlapping_{base_route_id}_{comp_route_id}" + ] + ) + st.write( + st.session_state[ + f"supply_overlapping_{comp_route_id}_{base_route_id}" + ] + ) + else: + st.error(overlapping_dict["text_base_v_comp"]) + + # Cálculo y visualización de la demanda, si no se ha realizado previamente + if ( + f"base_demand_comp_demand_{base_route_id}_{comp_route_id}" + not in st.session_state + ): + base_gdf = overlapping_dict["base"]["h3"] + comp_gdf = overlapping_dict["comp"]["h3"] + if (base_gdf is not None) and (comp_gdf is not None): + demand_overlapping = ovl.compute_demand_overlapping( + st.session_state.id_linea_1, + st.session_state.id_linea_2, + "weekday", + base_route_id, + comp_route_id, + base_gdf, + comp_gdf, + ) + st.session_state[ + f"base_demand_comp_demand_{base_route_id}_{comp_route_id}" + ] = demand_overlapping + + st.session_state[ + f"demand_overlapping_{base_route_id}_{comp_route_id}" + ] = demand_overlapping["base"]["output_text"] + st.session_state[ + f"demand_overlapping_{comp_route_id}_{base_route_id}" + ] = demand_overlapping["comp"]["output_text"] + else: + st.session_state[ + f"base_demand_comp_demand_{base_route_id}_{comp_route_id}" + ] = None + + demand_overlapping = st.session_state[ + f"base_demand_comp_demand_{base_route_id}_{comp_route_id}" + ] + + # Renderiza el segundo mapa y muestra el texto justo después del mapa en col2 + if demand_overlapping is not None: + base_demand = demand_overlapping["base"]["data"] + comp_demand = demand_overlapping["comp"]["data"] + + demand_overlapping_fig = ovl_viz.plot_interactive_demand_overlapping( + base_demand, comp_demand, overlapping_dict + ) + fig = demand_overlapping_fig["fig"] + base_gdf_to_db = demand_overlapping_fig["base_gdf_to_db"] + comp_gdf_to_db = demand_overlapping_fig["comp_gdf_to_db"] + + with col2: + folium_static(fig, width=800, height=600) + st.write( + st.session_state[ + f"demand_overlapping_{base_route_id}_{comp_route_id}" + ] + ) # Muestra la segunda salida justo después del mapa + st.write( + st.session_state[ + f"demand_overlapping_{comp_route_id}_{base_route_id}" + ] + ) # Muestra la segunda salida justo después del mapa + + +with st.expander("Exportar datos", expanded=True): + col1_db, col2_db = st.columns([2, 2]) + if col1_db.checkbox("Ver datos recorrido base"): + col1_db.write(base_gdf_to_db) + if col2_db.checkbox("Ver datos recorrido comparacion"): + col2_db.write(comp_gdf_to_db) diff --git a/urbantrips/lineas_deseo/lineas_deseo.py b/urbantrips/lineas_deseo/lineas_deseo.py index 04599e8..398ed9e 100644 --- a/urbantrips/lineas_deseo/lineas_deseo.py +++ b/urbantrips/lineas_deseo/lineas_deseo.py @@ -791,6 +791,7 @@ def crea_socio_indicadores(etapas, viajes): return socio_indicadores def preparo_etapas_agregadas(etapas, viajes): + e_agg = etapas.groupby(['dia', 'mes', 'tipo_dia', 'h3_o', 'h3_d', 'modo', 'id_linea'], as_index=False).factor_expansion_linea.sum() e_agg = e_agg.groupby(['mes', 'tipo_dia', 'h3_o', 'h3_d', 'modo', 'id_linea'], as_index=False).factor_expansion_linea.mean() e_agg = e_agg[e_agg.h3_o!=e_agg.h3_d] @@ -802,19 +803,38 @@ def preparo_etapas_agregadas(etapas, viajes): v_agg = v_agg[v_agg.h3_o!=v_agg.h3_d] etapas['etapas_max'] = etapas.groupby(['dia', 'id_tarjeta', 'id_viaje']).id_etapa.transform('max') - transfers = etapas.loc[(etapas.etapas_max>1), ['dia', 'id_tarjeta', 'id_viaje', 'id_etapa', 'etapas_max', 'id_linea', 'h3_o', 'h3_d', 'factor_expansion_linea']] + + transfers = etapas.loc[:, ['dia', 'id_tarjeta', 'id_viaje', 'id_etapa', 'etapas_max', 'id_linea', 'h3_o', 'h3_d', 'factor_expansion_linea']] #(etapas.etapas_max>1) transfers = transfers.merge(lineas[['id_linea', 'nombre_linea']], how='left') transfers = transfers.pivot(index=['dia', 'id_tarjeta', 'id_viaje'], columns='id_etapa', values='nombre_linea').reset_index().fillna('') transfers['seq_lineas'] = '' for i in range(1, etapas.etapas_max.max()+1): transfers['seq_lineas'] += transfers[i] + ' -- ' transfers['seq_lineas'] = transfers['seq_lineas'].str.replace(' -- -- ', '') - - transfers['seq_lineas'] = transfers.seq_lineas.str[:-4] + + transfers.loc[transfers.seq_lineas.str[-4:] == ' -- ', 'seq_lineas'] = transfers.loc[transfers.seq_lineas.str[-4:] == ' -- ', 'seq_lineas'].str[:-4] transfers = viajes.merge(transfers[['dia', 'id_tarjeta', 'id_viaje', 'seq_lineas']]) transfers = transfers.groupby(['dia', 'mes', 'tipo_dia', 'h3_o', 'h3_d', 'modo', 'seq_lineas'], as_index=False).factor_expansion_linea.sum() transfers = transfers.groupby(['mes', 'tipo_dia', 'h3_o', 'h3_d', 'modo', 'seq_lineas'], as_index=False).factor_expansion_linea.mean() + zonas = levanto_tabla_sql('zonas', 'insumos') + zonas_cols = zonas.columns.tolist() + zonas_cols = [item for item in zonas_cols if item not in ['fex', 'latitud', 'longitud']] + zonas = zonas[zonas_cols] + + zonas_cols_o = [f'{item}_o' for item in zonas_cols] + zonas_cols_d = [f'{item}_d' for item in zonas_cols] + + zonas.columns = zonas_cols_o + e_agg = e_agg.merge(zonas, how='left') + v_agg = v_agg.merge(zonas, how='left') + transfers = transfers.merge(zonas, how='left') + + zonas.columns = zonas_cols_d + e_agg = e_agg.merge(zonas, how='left') + v_agg = v_agg.merge(zonas, how='left') + transfers = transfers.merge(zonas, how='left') + return e_agg, v_agg, transfers def preparo_lineas_deseo(etapas_selec, viajes_selec, polygons_h3='', poligonos='', res=6): @@ -1460,7 +1480,19 @@ def preparo_lineas_deseo(etapas_selec, viajes_selec, polygons_h3='', poligonos=' ### return etapas_agrupadas_zon, viajes_matrices, zonificaciones - +def guarda_particion_modal(etapas): + df_dummies = pd.get_dummies(etapas.modo) + etapas = pd.concat([etapas, df_dummies], axis=1) + cols_dummies = df_dummies.columns.tolist() + + etapas_modos = etapas.groupby(['mes', 'tipo_dia', 'genero', 'id_tarjeta', 'id_viaje'], as_index=False).factor_expansion_linea.mean().merge( + etapas.groupby(['dia', 'id_tarjeta', 'id_viaje'], as_index=False)[cols_dummies].sum(), how='left') + + cols = ['mes', 'tipo_dia', 'genero', ]+cols_dummies + etapas_modos = etapas_modos.groupby(cols, as_index=False).factor_expansion_linea.sum().copy() + for i in cols_dummies: + etapas_modos = etapas_modos.rename(columns={i:i.capitalize()}) + guardar_tabla_sql(etapas_modos, 'datos_particion_modal', filtros={'mes': etapas_modos.mes.unique().tolist()}) def proceso_poligonos(check_configs=True): @@ -1490,17 +1522,6 @@ def proceso_poligonos(check_configs=True): indicadores = construyo_indicadores(viajes_selec, poligonos=True) - - ## guardar_tabla_sql(etapas_agrupadas, - ## 'poly_etapas', - ## 'dash', - ## {'mes': etapas_agrupadas.mes.unique().tolist()}) - - ## guardar_tabla_sql(viajes_matrices, - ## 'poly_matrices', - ## 'dash', - ## {'mes': viajes_matrices.mes.unique().tolist()}) - guardar_tabla_sql(indicadores, 'poly_indicadores', 'dash', @@ -1529,15 +1550,7 @@ def proceso_lineas_deseo(check_configs=False): print('Guardo datos para dashboard') - ## guardar_tabla_sql(etapas_agrupadas, - ## 'agg_etapas', - ## 'dash', - ## {'mes': etapas_agrupadas.mes.unique().tolist()}) - - ## guardar_tabla_sql(viajes_matrices, - ## 'agg_matrices', - ## 'dash', - ## {'mes': viajes_matrices.mes.unique().tolist()}) + guarda_particion_modal(etapas) guardar_tabla_sql(indicadores, 'agg_indicadores', @@ -1566,3 +1579,4 @@ def proceso_lineas_deseo(check_configs=False): {'mes': v_agg.mes.unique().tolist()}) imprimo_matrices_od() + diff --git a/urbantrips/viz/viz.py b/urbantrips/viz/viz.py index 823dade..5151639 100755 --- a/urbantrips/viz/viz.py +++ b/urbantrips/viz/viz.py @@ -2476,47 +2476,47 @@ def save_zones(): conn_dash.close() -def particion_modal(viajes_dia, etapas_dia, tipo_dia, desc_dia): - - particion_viajes = ( - viajes_dia.groupby("modo", as_index=False).factor_expansion_linea.sum().round() - ) - particion_viajes["modal"] = ( - particion_viajes["factor_expansion_linea"] - / viajes_dia.factor_expansion_linea.sum() - * 100 - ).round() - particion_viajes = particion_viajes.sort_values("modal", ascending=False).drop( - ["factor_expansion_linea"], axis=1 - ) - particion_viajes["tipo"] = "viajes" - particion_viajes["tipo_dia"] = tipo_dia - particion_viajes["desc_dia"] = desc_dia - particion_etapas = ( - etapas_dia.groupby("modo", as_index=False).factor_expansion_linea.sum().round() - ) - - particion_etapas["modal"] = ( - particion_etapas["factor_expansion_linea"] - / etapas_dia.factor_expansion_linea.sum() - * 100 - ).round() - particion_etapas = particion_etapas.sort_values("modal", ascending=False).drop( - ["factor_expansion_linea"], axis=1 - ) - particion_etapas["tipo"] = "etapas" - particion_etapas["desc_dia"] = desc_dia - particion_etapas["tipo_dia"] = tipo_dia - particion = pd.concat([particion_viajes, particion_etapas], ignore_index=True) - - conn_dash = iniciar_conexion_db(tipo="dash") - - query = f'DELETE FROM particion_modal WHERE desc_dia = "{desc_dia}" & tipo_dia = "{tipo_dia}"' - conn_dash.execute(query) - conn_dash.commit() - particion["modo"] = particion.modo.str.capitalize() - particion.to_sql("particion_modal", conn_dash, if_exists="append", index=False) - conn_dash.close() +# def particion_modal(viajes_dia, etapas_dia, tipo_dia, desc_dia): + +# particion_viajes = ( +# viajes_dia.groupby("modo", as_index=False).factor_expansion_linea.sum().round() +# ) +# particion_viajes["modal"] = ( +# particion_viajes["factor_expansion_linea"] +# / viajes_dia.factor_expansion_linea.sum() +# * 100 +# ).round() +# particion_viajes = particion_viajes.sort_values("modal", ascending=False).drop( +# ["factor_expansion_linea"], axis=1 +# ) +# particion_viajes["tipo"] = "viajes" +# particion_viajes["tipo_dia"] = tipo_dia +# particion_viajes["desc_dia"] = desc_dia +# particion_etapas = ( +# etapas_dia.groupby("modo", as_index=False).factor_expansion_linea.sum().round() +# ) + +# particion_etapas["modal"] = ( +# particion_etapas["factor_expansion_linea"] +# / etapas_dia.factor_expansion_linea.sum() +# * 100 +# ).round() +# particion_etapas = particion_etapas.sort_values("modal", ascending=False).drop( +# ["factor_expansion_linea"], axis=1 +# ) +# particion_etapas["tipo"] = "etapas" +# particion_etapas["desc_dia"] = desc_dia +# particion_etapas["tipo_dia"] = tipo_dia +# particion = pd.concat([particion_viajes, particion_etapas], ignore_index=True) + +# conn_dash = iniciar_conexion_db(tipo="dash") + +# query = f'DELETE FROM particion_modal WHERE desc_dia = "{desc_dia}" & tipo_dia = "{tipo_dia}"' +# conn_dash.execute(query) +# conn_dash.commit() +# particion["modo"] = particion.modo.str.capitalize() +# particion.to_sql("particion_modal", conn_dash, if_exists="append", index=False) +# conn_dash.close() def plot_dispatched_services_wrapper(): @@ -3202,7 +3202,7 @@ def create_visualizations(): # ) # partición modal - particion_modal(viajes_dia, etapas_dia, tipo_dia=i.tipo_dia, desc_dia=desc_dia) + # particion_modal(viajes_dia, etapas_dia, tipo_dia=i.tipo_dia, desc_dia=desc_dia) print("Imprimiendo gráficos") titulo = f"Cantidad de viajes en transporte público {desc_dia}"