Bring Drag & Drop to Dash with Dashboard Engine. 💫 Learn how at our next webinar!

Updating selectedpoints of figure from Dropdown

Hello,

So based off the selectedpoint example in the docs I’ve made an app that when you select a point in a scatterplot it adds it as a value to the dropdown. One thing the example doesn’t show is how to do the reverse. When I add an item to the dropdown I want the value of the dropdown to update and add the index to the selectedpoint variable within the figure. The issues I’m running into is that the scatter plot has multiple traces and even when I preserve the index in pandas it will add all points with that same index across my 4 traces in the order they were added. The second issue I have is the customdata in the original example wants you to only have the index from the dataframe, but I already am using customdata for a seperate hoverData interaction, can I do both?

Here is my code:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objects as go
import pandas as pd
import numpy as np
# Internal imports
from models import session, Station, TimeCheck

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

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


def add_xy(df, id_column):
    df['y'] = df[id_column].str.slice(start=4, stop=6).astype('int64')
    df['x'] = df[id_column].str.slice(start=6, stop=8).astype('int64')

    return df


# Get DB tables into df
station_tbl = pd.read_sql(session.query(Station).statement, session.bind)

timecheck_tbl = pd.read_sql(session.query(TimeCheck).statement, session.bind, index_col='tc_id')
timecheck_tbl = timecheck_tbl.sort_values(by=['station_id', 'dt_server_local'])

# Get the most recent row for each station:
latest = timecheck_tbl.sort_values('tc_id', ascending=False).drop_duplicates(['station_id'])

# Get list of unique station ids:
station_ids = station_tbl['station_id'].unique()
station_idx = station_tbl.index
station_list = [(ids, idx) for ids, idx in zip(station_ids, station_idx)]

# Join metadata to latest df
station_df = station_tbl.join(latest.set_index('station_id'), on='station_id')
station_df['index'] = station_df.index
station_df = add_xy(station_df, 'station_id')
customdata_df = station_df.filter(items=['station_id', 'name', 'circuit', 'in_communication', 'interval',
                                         'always_powered', 'index'])
station_df['customdata'] = customdata_df.values.tolist()


def data_all_graph(df, selection=None):
    # Divide df into traces
    in_10 = df[(df['in_communication'] == True) & (df['interval'] == 10)]
    in_60 = df[(df['in_communication'] == True) & (df['interval'] == 60)]
    out_10 = df[(df['in_communication'] == False) & (df['interval'] == 10)]
    out_60 = df[(df['in_communication'] == False) & (df['interval'] == 60)]

    # List-ify individual traces with more information for trace
    df_dict = {
        'in_10': [in_10.set_index('index'), 'forestgreen', 'On Time, 10m'],
        'in_60': [in_60.set_index('index'), 'darkseagreen', 'On Time, 60m'],
        'out_10': [out_10.set_index('index'), 'darkslategray', 'Delayed, 10m'],
        'out_60': [out_60.set_index('index'), 'lightslategray', 'Delayed, 60m']
    }

    trace_list = []

    # Build trace objects
    for key, value in df_dict.items():
        key = go.Scatter(name=value[2],
                         x=value[0]['x'],
                         y=value[0]['y'],
                         customdata=value[0]['customdata'],
                         mode='markers',
                         marker=dict(
                             size=14,
                             color=value[1]),
                         selectedpoints=selection,
                         marker_symbol='square',
                         text=value[0]['customdata'],
                         hovertemplate='ID: %{text[0]}<br>' + 'Name: %{text[1]}<br>' +
                                       'Circuit: %{text[''2]}<br>' + 'Last Comm.: %{text[3]}<br>' +
                                       'Interval: %{text[4]}<br>' + 'Always Powered: %{text[5]}<br>' +
                                       '<extra></extra>',
                         )

        trace_list.append(key)

    return trace_list


def layout_all_graph():
    layout = go.Layout(font=dict(family='Arial',
                                 size=14,
                                 color='dimgray'),
                       title=dict(text='Southern California Edison Satellite Stations',
                                  font=dict(family='Arial Narrow',
                                            size=20,
                                            color='dimgray')),
                       xaxis=dict(tickmode='array',
                                  tickvals=[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50,
                                            55, 60, 65, 70, 75, 80, 85, 90, 95, 99],
                                  zeroline=False),
                       yaxis=dict(tickmode='array',
                                  tickvals=[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
                                  ticktext=['2001 - 2099', '2100 - 2199', '2200 - 2299',
                                            '2300 - 2399', '2400 - 2499', '2500 - 2599',
                                            '2600 - 2699', '2700 - 2799', '2800 - 2899',
                                            '2900 - 2999', '3000 - 3099']),
                       legend=dict(orientation='h',
                                   yanchor='top',
                                   y=-0.1,
                                   xanchor='right',
                                   x=1),
                       hovermode='closest',
                       clickmode='event+select',
                       paper_bgcolor='#fff',
                       plot_bgcolor='#fff',
                       showlegend=True,
                       dragmode=False)

    return layout


def make_all_graph(data=data_all_graph, layout=layout_all_graph(), selection=None):
    return dict(data=data(station_df, selection), layout=layout)


app.layout = \
    html.Div([
        html.Div(
            dcc.Graph(
                id='all-graph',
                figure=make_all_graph(),
                config={'displayModeBar': False},
                hoverData={'points': [{'customdata': ['SCE-2017']}]},
                selectedData={'points': [{'customdata': ['SCE-3000', 'SCE Redlands Blvd', 'ACCENT', True, 10, True, 706]
                                          }]}
            )
        ),
        html.Div([
            dcc.Graph(
                id='individual-graph',
                config={'displayModeBar': False}),
            dcc.Dropdown(
                id='station-dd',
                options=[{"label": ids, "value": idx} for ids, idx in station_list],
                value=station_list[0][1],
                multi=True,
            )
        ])
    ])


@app.callback(
    Output('station-dd', 'value'),
    Input('all-graph', 'selectedData'))
def add_to_dd(selectedData):
    if selectedData is None:
        return None
    else:
        idx_list = []
        for x in selectedData['points']:
            idx = x['customdata'][6]
            idx_list.append(idx)

    return idx_list


@app.callback(
    Output('all-graph', 'figure'),
    Input('station-dd', 'value'))
def callback(selection):
    return make_all_graph(selection=selection)




@app.callback(
    Output('individual-graph', 'figure'),
    Input('all-graph', 'hoverData'))
def display_hover_data(hoverData):
    station_id = hoverData['points'][0]['customdata'][0]

    dff = timecheck_tbl[timecheck_tbl['station_id'] == station_id]

    figure = {
        'data': [go.Scatter(
            x=dff['dt_server_local'],
            y=[1 for _ in dff['dt_server_local']],
            mode='markers',
            marker=dict(
                size=14,
                color='forestgreen'),
            marker_symbol='square',
            line_shape='linear')],
        'layout': go.Layout(
            xaxis=dict(
                showgrid=False,
                title_text="Time",
                tickangle=-45,
                type='date'
            ),
            yaxis=dict(
                tickmode='array',
                tickvals=[1],
                ticktext=[station_id]
            ))}

    return figure


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

Hi, did you find a solution to this?
I’m having a similar problem: passing selected_values from a drop down and calling fig.update_traces(selectedpoints=selected_values) selects those points for each group/color rather than from the whole underlying dataframe.

Update: see my solution here: Selectedpoint highlights data points for each category instead of from the dataset as a whole - #2 by T42