Search code examples
pythonplotplotlyinteractiveinputbox

Plotly interactive plot python: Change the dropdown menu to input box


I want to change the dropdown button with an input box so I can search for the item by starting to type the name and then select. So far I have a drop down box where you can select either one item or all of them at the same time. However, I want the user to be able to start typing the name of the item and then click and select the item they want to display their graph.

As I am new to plotly, any suggestion is very welcome and appreciated :)

Here is what the plot looks like so far:

enter image description here

My code:

def interactive_multi_plot(actual, forecast_1, forecast_2, title, addAll = True):
fig = go.Figure()
    

for column in forecast_1.columns.to_list():
    fig.add_trace(
        go.Scatter(
            x = forecast_1.index,
            y = forecast_1[column],
            name = "Forecast_SI"
        )

    )

    
    button_all = dict(label = 'All',
                  method = 'update',
                  args = [{'visible': forecast_1.columns.isin(forecast_1.columns),
                           'title': 'All',
                           'showlegend':True}])
    
for column in forecast_2.columns.to_list():
    fig.add_trace(
        go.Scatter(
            x = forecast_2.index,
            y = forecast_2[column],
            name = "Forecast_LSTM" 
        )

    )

    
    button_all = dict(label = 'All',
                  method = 'update',
                  args = [{'visible': forecast_2.columns.isin(forecast_2.columns),
                           'title': 'All',
                           'showlegend':True}])
for column in actual.columns.to_list():
    fig.add_trace(
        go.Scatter(
            x = actual.index,
            y = actual[column],
            name = "True values" 
        )

    )

    
    button_all = dict(label = 'All',
                  method = 'update',
                  args = [{'visible': actual.columns.isin(actual.columns),
                           'title': 'All',
                           'showlegend':True}])
    
fig.layout.plot_bgcolor = '#010028'
fig.layout.paper_bgcolor = '#010028'
def create_layout_button(column):
    return dict(label = column,
                method = 'update',
                args = [{'visible': actual.columns.isin([column]),
                         'title': column,
                         'showlegend': True}])
fig.update_layout(
    updatemenus=[go.layout.Updatemenu(
        active = 0,
        buttons = ([button_all] * addAll) +  list(actual.columns.map(lambda column: create_layout_button(column)))
        )
    ]     
)
# Update remaining layout properties
fig.update_layout(
    title_text=title,
    height=800,
    font = dict(color='#fff', size=12)
)


fig.show()

This is the error I receive:

enter image description here


Solution

    • small changes to interactive_multi_plot().
      1. for all three add_trace() add meta = column for each of the scatter creations
      2. change to return fig instead of fig.show()
    • simulate some data and call interactive_multi_plot(). I have assumed all three data frames have the same columns
    S = 100
    C = 10
    
    actual = pd.DataFrame(
        {
            c: np.sort(np.random.uniform(0, 600, S))
            for c in [
                f"{a}{b}-{c}"
                for a, b, c in zip(
                    np.random.randint(100, 200, C),
                    np.random.choice(list("ABCDEF"), C),
                    np.random.randint(300, 400, C),
                )
            ]
        }
    )
    
    f1 = actual.assign(**{c:actual[c]*1.1 for c in actual.columns})
    f2 = actual.assign(**{c:actual[c]*1.2 for c in actual.columns})
    
    fig = interactive_multi_plot(actual, f1, f2, "Orders")
    
    

    solution

    • use dash this does support interactive drop downs
    • simple case of show figure and define a callback on item selected from dash drop down
    • it could be considered that updatemenus is now redundant. I have not considered sync of updatemenus back to dash drop down
    import dash_core_components as dcc
    import dash_html_components as html
    from dash.dependencies import Input, Output, State
    from jupyter_dash import JupyterDash
    
    # Build App
    app = JupyterDash(__name__)
    app.layout = html.Div(
        [
            dcc.Dropdown(
                id="lines",
                options=[{"label": c, "value": c} for c in ["All"] + actual.columns.tolist()],
                value="All",
            ),
            dcc.Graph(id="interactive-multiplot", figure=fig),
        ]
    )
    
    @app.callback(
        Output("interactive-multiplot", "figure"),
        Input("lines", "value"),
        State("interactive-multiplot", "figure"),
    )
    def updateGraphCB(line, fig):
        # filter traces...
        fig = go.Figure(fig).update_traces(visible=False).update_traces(visible=True, selector={"meta":line} if line!="All" else {})
        # syn button to dash drop down
        fig = fig.update_layout(updatemenus=[{"active":0 if line=="All" else actual.columns.get_loc(line)+1}])
        return fig
    
    app.run_server(mode="inline")
    

    enter image description here