How to apply a Dash callback decorator appropriately to a Plotly figurewidget that includes a selection

Viewed 2770

I'm trying to take the interactive widget with multiple charting elements as found here and apply it in a Dash so I can have multi-chart brushing:

I have the widget showing appropriately in the dash and I am able to use the lasso to make a selection, which recolors the dots, but does not update the Sankey. I have searched and found that this might be a "callback" issue, but none of them seem to address this specific issue. Could someone please explain what I'm missing?

Plot:

def chart1():
    cars_df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/imports-85.csv')

    # Build parcats dimensions
    categorical_dimensions = ['body-style', 'drive-wheels', 'fuel-type'];

    dimensions = [dict(values=cars_df[label], label=label) for label in categorical_dimensions]

    # Build colorscale
    color = np.zeros(len(cars_df), dtype='uint8')
    colorscale = [[0, '#167b7e'], [1, '#4b3268']]

    # Build figure as FigureWidget
    fig = go.FigureWidget(
        data=[

        go.Scatter(x=cars_df.horsepower, y=cars_df['highway-mpg'],
            marker={'color': 'gray'}, mode='markers', selected={'marker': {'color': 'firebrick'}},
            unselected={'marker': {'opacity': 0.4}}), 

        go.Parcats(
            domain={'y': [0, 0.4]}, dimensions=dimensions,
            line={'colorscale': colorscale, 'cmin': 0,
                'cmax': 1, 'color': color, 'shape': 'hspline'})
        ])

    fig.update_layout(
            height=800, 
            xaxis={'title': 'Horsepower'},
            yaxis={'title': 'MPG', 'domain': [0.6, 1]},
            dragmode='lasso', 
            hovermode='closest',
            # plot_bgcolor='rgba(0, 0, 0, 0)',
            paper_bgcolor='rgba(0, 0, 0, 0)',
            autosize=False,
            bargap=0.35,
            font={"family": "Questrial", "size": 10})

    # Update color callback

    # @app.callback(Output("bigchart", "children"), [Input("points.point_inds", "value")])   <--- an attempt
    def update_color(trace, points, state):
        # Update scatter selection
        fig.data[0].selectedpoints = points.point_inds

        # Update parcats colors
        new_color = np.zeros(len(cars_df), dtype='uint8')
        new_color[points.point_inds] = 1
        fig.data[1].line.color = new_color

    # Register callback on scatter selection...
    fig.data[0].on_selection(update_color)
    # and parcats click
    fig.data[1].on_click(update_color)

    return fig

Plot location:

dcc.Graph(id="bigchart",figure=chart1())

Plot

Sorry, I would post images inline, but don't have the rep. Thank you.

1 Answers

If you want to achieve interactivity in Dash, you should convert all callbacks to Dash syntax. Here is one possible implementation of your example in Dash,

import dash
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import dash_core_components as dcc
import dash_html_components as html

from dash.dependencies import Input, Output, State

cars_df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/imports-85.csv')
# Build parcats dimensions
categorical_dimensions = ['body-style', 'drive-wheels', 'fuel-type']
dimensions = [dict(values=cars_df[label], label=label) for label in categorical_dimensions]
# Build colorscale.
color = np.zeros(len(cars_df), dtype='uint8')
colorscale = [[0, '#167b7e'], [1, '#4b3268']]


def build_figure():
    fig = go.Figure(
        data=[
            go.Scatter(x=cars_df.horsepower, y=cars_df['highway-mpg'],
                       marker={'color': 'gray'}, mode='markers', selected={'marker': {'color': 'firebrick'}},
                       unselected={'marker': {'opacity': 0.4}}),
            go.Parcats(
                domain={'y': [0, 0.4]}, dimensions=dimensions,
                line={'colorscale': colorscale, 'cmin': 0,
                      'cmax': 1, 'color': color, 'shape': 'hspline'})
        ])
    fig.update_layout(
        height=800,
        xaxis={'title': 'Horsepower'},
        yaxis={'title': 'MPG', 'domain': [0.6, 1]},
        dragmode='lasso',
        hovermode='closest',
        # plot_bgcolor='rgba(0, 0, 0, 0)',
        paper_bgcolor='rgba(0, 0, 0, 0)',
        autosize=False,
        bargap=0.35,
        font={"family": "Questrial", "size": 10})
    return fig


app = dash.Dash(prevent_initial_callbacks=True)
app.layout = html.Div([dcc.Graph(figure=build_figure(), id="graph")])


@app.callback(Output("graph", "figure"), [Input("graph", "selectedData"), Input("graph", "clickData")],
              [State("graph", "figure")])
def update_color(selectedData, clickData, fig):
    selection = None
    # Update selection based on which event triggered the update.
    trigger = dash.callback_context.triggered[0]["prop_id"]
    if trigger == 'graph.clickData':
        selection = [point["pointNumber"] for point in clickData["points"]]
    if trigger == 'graph.selectedData':
        selection = [point["pointIndex"] for point in selectedData["points"]]
    # Update scatter selection
    fig["data"][0]["selectedpoints"] = selection
    # Update parcats colors
    new_color = np.zeros(len(cars_df), dtype='uint8')
    new_color[selection] = 1
    fig["data"][1]["line"]["color"] = new_color
    return fig


if __name__ == '__main__':
    app.run_server()
Related