✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
🧬 Learn how to build RNA-Seq data apps with Python & Dash. Register for the May 20 Webinar!

Mixing and matching inputs to a single output graph

I’m relatively new to Plotly Dash and am trying to set up a callback/function that takes multiple inputs (selection from a Dropdown; selection from a RangeSlider) to a single output (a Graph).

The inputs should change the data displayed in my plot, which is a dcc.Graph (which I am currently rendering with plotly express). I have a dcc.Dropdown that I would like to filter my dataframe based on ‘Name’ and a dcc.RangeSlider that I would like to filter my dataframe based on ‘year’.

Ideally, I would like the user to be able to mix/match these as well as display all data the Graph without either of these (Dropdown or RangeSlider). I assume I would need to enable the data to be “reset” but am not sure how to do this.

Suggestions on how I could achieve this kind of interactivity would be greatly appreciated! My code is below:

import os
import pandas as pd
import numpy as np
import plotly.express as px
import dash
import dash_core_components as dcc
import plotly.graph_objects as go
import dash_html_components as html
from dash.dependencies import Input, Output

df = pd.read_csv('data.csv', index_col=0)

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

server = app.server

list_of_pis = df['Name'].unique().tolist()
list_of_pis.sort()

app.layout = html.Div(children=[
        html.Div(
            className="row",
            children=[
                html.Div(
                    className="four columns div-user-controls",
                    children=[
                        html.Img(className="logo", src=app.get_asset_url("ERI_logo_WEB_72dpi_1.png")),
                        html.H2("ERI Research Map"),
                        html.P(
                            """Filter documents in the map by researcher or year."""
                        ),
                        html.Div(
                            className="row",
                            children=[
                                html.Div(
                                    className="div-for-dropdown",
                                    children=[
                                        dcc.Dropdown(id="pi-selector",
                                            placeholder="Select a PI", 
                                            options=[{"label": i, "value": i} for i in list_of_pis],
                                            multi=False,
                                            #value=[df['Name'].sort_values()[0]],
                                            #style={'backgroundColor': '#1E1E1E'},
                                            className='pi-selector')
                                    ], 
                                    style={'color': '#1E1E1E'}
                                ),  
                            ],
                        ),
                        dcc.Markdown(
                            children=[
                                "**About:** This t-SNE map was created from a NMF topic model of 3,770 ERI research publications and funded projects from 2009 - 2019. Dashboard data and code are available on [Github](https://github.com/saralafia/ERI-dashboard)."
                            ]
                        ),
                    ],
                ),
                # Column for app graphs and plots
                html.Div(
                    className="eight columns div-for-charts bg-grey",
                    children=[
                        dcc.Graph(id="map-graph", 
                            #config={'displayModeBar': True}, 
                            #animate=True,
                            figure = px.scatter(df, 
                                x='x', 
                                y='y',
                                color='main_label',
                                hover_name='title', 
                                hover_data=['authors','doi','year','type','main_keys'], 
                                color_discrete_sequence=px.colors.qualitative.D3, 
                                opacity=0.6, 
                                template='plotly_white').update_layout(
                                   {'plot_bgcolor': 'rgba(0, 0, 0, 0)',
                                    'paper_bgcolor': 'rgba(0, 0, 0, 0)', 
                                    'xaxis_showgrid': False, 
                                    'yaxis_showgrid': False, 
                                    'xaxis_zeroline': False, 
                                    'yaxis_zeroline': False, 
                                    'yaxis_visible': False, 
                                    'xaxis_visible': False, 
                                    'font':{'family':"sans-serif", 'size':14, 'color':'white'},
                                    'legend': {'title':'Main Topic','itemsizing': 'constant'}, 
                                    'legend_font': {'family':"sans-serif", 'size':14, 'color':'white'},
                                    }
                                    )),
                    html.Div(
                        className="text-padding"),
                    dcc.RangeSlider(id='year-slider',
                        min=df['year'].min(),
                        max=df['year'].max(),
                        value=[2014, 2019],
                        marks={str(year): str(year) for year in df['year'].unique()}, 
                        step=None
                        ),
                    ]
                )
            ]
        )
    ])

# @app.callback(
#     Output('map-graph', 'figure'),
#     [Input('pi-selector', 'value')])

# def update_figure(selected_pi):
#     filtered_df = df[df.Name == selected_pi]
#     traces = []
#     for i in filtered_df.main_label.unique():
#         df_by_topic = filtered_df[filtered_df['main_label'] == i]
#         traces.append(dict(
#             x=df_by_topic['x'],
#             y=df_by_topic['y'],
#             text=df_by_topic['title'],
#             color=df_by_topic['main_label'], 
#             # hover_name='title', 
#             # hover_data=['authors','doi','year','type','main_keys'], 
#             color_discrete_sequence=px.colors.qualitative.D3,
#             mode='markers',
#             opacity=0.7,
#             marker={
#                 'size': 10,
#                 'line': {'width': 0, 'color': 'white'}
#             },
#             name=i
#         ))

#     return {
#         'data': traces,
#         'layout': dict(
#             xaxis={'range':[-150, 150],'showgrid': False,'zeroline': False,'visible': False,},
#             yaxis={'range': [-150, 150],'showgrid': False,'zeroline': False,'visible': False,},
#             # margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
#             # xaxis_showgrid=False, 
#             # yaxis_showgrid=False, 
#             # xaxis_zeroline=False, 
#             # yaxis_zeroline=False, 
#             # yaxis_visible=False, 
#             # xaxis_visible=False,
#             # legend={'x': 0, 'y': 1},
#             hovermode='closest',
#             transition = {'duration': 500},
#         )
#     }

if __name__ == '__main__':
    app.run_server(debug=True)

Hi @slafia welcome to the forum! Inside the callback you can have a series of if tests to test whether an input property is None or not, and if it is not None you can filter the dataframe using the value. You can also add a “reset” button as input and use dash.callback_context (see https://dash.plotly.com/advanced-callbacks) to determine whether the callback was triggered by the reset button, in which case you build the graph from the unfiltered dataframe.

@Emmanuelle, thank you very much for the advice! I was able to resolve my issue as follows:

@app.callback(
    Output('graphs','figure'),
    [Input('pi-selector', 'value'), Input('year--slider', 'value')])

def update_graph(selected_pi, year_value):
    #dff = df[df['year'] == year_value]
    dff = df[(df['year']>year_value[0])&(df['year']<year_value[1])]
    if not selected_pi:
        fig = px.scatter(dff, 
                    x='x', 
                    y='y',
                    color='main_label',
                    hover_name='title', 
                    hover_data=['authors','doi','year','type','main_keys'], 
                    color_discrete_sequence=px.colors.qualitative.D3, 
                    opacity=0.6,
                    )

        fig.update_layout(
                       {'autosize': True, 
                       'width':900,
                       'height':700,
                       'plot_bgcolor': 'rgba(0, 0, 0, 0)',
                        'paper_bgcolor': 'rgba(0, 0, 0, 0)', 
                        'xaxis_showgrid': False, 
                        'yaxis_showgrid': False, 
                        'xaxis_zeroline': False, 
                        'yaxis_zeroline': False, 
                        'yaxis_visible': False, 
                        'xaxis_visible': False, 
                        'font':{'family':"sans-serif", 'size':14, 'color':'white'},
                        'legend': {'title':'Main Topic','itemsizing': 'constant'}, 
                        'legend_font': {'family':"sans-serif", 'size':14, 'color':'white'},
                        }
                        )

        return fig
    
    else:
        fig = px.scatter(dff, 
                    x='x', 
                    y='y',
                    color='main_label',
                    hover_name='title', 
                    hover_data=['authors','doi','year','type','main_keys'], 
                    color_discrete_sequence=px.colors.qualitative.D3, 
                    opacity=0.2, 
                    )

        fig.update_layout(
                       {'autosize': True,
                       'width':900,
                       'height':700,
                       'plot_bgcolor': 'rgba(0, 0, 0, 0)',
                        'paper_bgcolor': 'rgba(0, 0, 0, 0)', 
                        'xaxis_showgrid': False, 
                        'yaxis_showgrid': False, 
                        'xaxis_zeroline': False, 
                        'yaxis_zeroline': False, 
                        'yaxis_visible': False, 
                        'xaxis_visible': False, 
                        'font':{'family':"sans-serif", 'size':14, 'color':'white'},
                        'legend': {'title':'Main Topic','itemsizing': 'constant'}, 
                        'legend_font': {'family':"sans-serif", 'size':14, 'color':'white'},
                        }
                        )

        filtered_df = dff[dff.Name == selected_pi]
        
        fig.add_trace(go.Scatter(
                    x=filtered_df['x'], 
                    y=filtered_df['y'], 
                    #name='title',
                    text=filtered_df['title'],
                    mode='markers', 
                    #marker_symbol='x',
                    marker=dict(
                        color='whitesmoke',
                        #size=11,
                        opacity=0.9,
                        line=dict(
                            color='White',
                            width=2)
                        ),
                    hoverinfo='none', 
                    hovertemplate= 
                    "<b>Title: %{text}</b><br><br>" +
                    "<extra></extra>",
                    showlegend=False))
        
        return fig

    return graphs