Search code examples
pythonplotlydashboardplotly-dash

Dash scatter plot is not updating when the dropdown change


Im trying to code an Scatterplot using Plotly and making it interactive trhough Dash callbacks, the issue is that my graph is not updating when I change the dropdown list. the data structure is the following:

periodo Direccion fecha_rep revisados errores 202002 Dir5 29/02/2020 16793 3 202001 Dir5 31/01/2020 18933 1 202101 Dir5 31/01/2021 6246 132 202001 Dir4 17/01/2020 13 1

#Importar librerías
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd

#Carga de datos
df_temp = pd.read_excel(r'D:\Python\Datos_maestros\periodo.xlsx')

app = dash.Dash()
fechas = []
for fecha in df_temp['periodo'].unique():
    fechas.append({'label': str(fecha), 'value': fecha})

#Definición del layout de la app a partir de componentes HTML y Core
app.layout = html.Div([
    dcc.Graph(id='graph_line'),
    dcc.Dropdown(id='selector_fecha', options = fechas, value = '') #Seleccionamos todo el rango de fechas de nuestro dataframe
])

# CREACIÓN DE GRÁFICOS E INTERACTIVIDAD
#Callback para actualizar gráfico en función del rango de fechas seleccionadas
@app.callback(Output('graph_linea', 'figure'),
              [Input('selector_fecha', 'value')])
def actualizar_graph(fecha):
    filtered_df = df_temp[df_temp['periodo'] == fecha]
    #Creación de 1 traza por cada ciudad de nuestro dataframe
    traces = []
    for nombre_area in filtered_df['Direccion'].unique():
        df_area = filtered_df[filtered_df['Direccion'] == nombre_area]
        traces.append(go.Scatter(
            x=df_area['revisados'],
            y=df_area['errores'],
            text=df_area['Direccion'],
            mode='markers',
            opacity=0.7,
            marker={'size': 15},
            name=nombre_area
        ))
    
        return { #Se retornan los objetos data y layout para ser enviados al Output con identificador graph_linea
            'data': traces,
            'layout': go.Layout(
                xaxis={'title': 'Fecha'},
                yaxis={'title': 'Errores'},
                hovermode='closest'
            )
        }


if __name__ == '__main__':
    app.run_server(port=5000)

Solution

  • I know this does not always work, but for future questions try to refactor your problem into a reproducible example. Meaning a piece of code that each of us can reproduce. This makes it a lot easier for us to help you since we can't run your code because of the missing missing .xslx file.

    That said, I think there are two main problems. As @coralvanda already mentioned it is weird that your return statement is inside the for loop. I assume you should pull it out there unless you intend to stop the loop after the first iteration. Second, updating a plotly graph involves a figure object. Have a look at go.Figure(). Add the figure object first, then add your desired traces. The fixed code could look like this:

    #Importar librerías
    import dash
    import dash_core_components as dcc
    import dash_html_components as html
    from dash.dependencies import Input, Output
    import plotly.graph_objs as go
    import pandas as pd
    
    #Carga de datos
    df_temp = pd.read_excel(r'D:\Python\Datos_maestros\periodo.xlsx')
    
    app = dash.Dash()
    fechas = []
    for fecha in df_temp['periodo'].unique():
        fechas.append({'label': str(fecha), 'value': fecha})
    
    #Definición del layout de la app a partir de componentes HTML y Core
    app.layout = html.Div([
        dcc.Graph(id='graph_line'),
        dcc.Dropdown(id='selector_fecha', options = fechas, value = '') #Seleccionamos todo el rango de fechas de nuestro dataframe
    ])
    
    # CREACIÓN DE GRÁFICOS E INTERACTIVIDAD
    #Callback para actualizar gráfico en función del rango de fechas seleccionadas
    @app.callback(Output('graph_linea', 'figure'),
                  [Input('selector_fecha', 'value')])
    def actualizar_graph(fecha):
        fig = go.Figure()
        
        filtered_df = df_temp[df_temp['periodo'] == fecha]
        #Creación de 1 traza por cada ciudad de nuestro dataframe
        traces = []
        for nombre_area in filtered_df['Direccion'].unique():
            df_area = filtered_df[filtered_df['Direccion'] == nombre_area]
            fig.add_trace(
                go.Scatter(
                    x=df_area['revisados'],
                    y=df_area['errores'],
                    text=df_area['Direccion'],
                    mode='markers',
                    opacity=0.7,
                    marker={'size': 15},
                    name=nombre_area
                )
            )
        
        fig['layout'] = go.Layout(
            xaxis={'title': 'Fecha'},
            yaxis={'title': 'Errores'},
            hovermode='closest'
        )
        
        return fig
    
    
    if __name__ == '__main__':
        app.run_server(port=5000)