Recreating "Parallel Categories Linked Brushing" example in Dash

Hi, I’m very new to Dash and have been going through plotly graphing libraries and Dash user guide a lot to create,

  • a parallel categories plot,
  • and adding a capability to change line color “on_click” (clicking any item in plot).

There is an exact example in plotly tutorial (Parallel Categories Linked Brushing), BUT which is not written with exact Dash format, so that’s where I’m having trouble with.

First question
In the example, “def update_color” and “fig.data[1].on_click(update_color)” are only used to enable color change when clicking the bar. But in Dash, I should be including “@app.callback” appended by the “def update_color” function correct (as shown below)?

@app.callback(
Output(‘parallel_categories_plot_id’, ‘children’),
[Input(???)]
)
def update_color(trace, points, state):
# Update parcats colors
new_color = np.zeros(len(df_com), dtype=‘uint8’)
new_color[points.point_inds] = 1
figure.data[0].line.color = new_color

Second question
I saw lots of examples where “@app.callback” connects output from inputs with core components (e.g., dropdown, slider etc.), but have not found inputs with “on_click”. How should I be specifying component_id and _property in this case?

Third question
should I be adding this “fig.data[1].on_click(update_color)” anywhere in Dash coding format? or having callback and update_color function will automatically trigger the change in line color without “fig.data[1].on_click(update_color)” code?

I’d appreciate some advice, and any posts/webpages with relevant examples will also be appreciated.

Thanks!!

FYI, I have something like this for now.

app.layout = ddk.App(show_editor=True, children=[

ddk.Graph(

	id = 'graph1',
	
	figure=go.FigureWidget(
		data = [go.Parcats(
			dimensions=[
				datasource_com,
				region_com,
				state_com,
				climatezone_com,
				buildingtype_com,
				measurementtype_com,
				timeresolution_com,
			],
			line = {
				'color': color_click,
				'colorscale': colorscale_click,
				'shape': 'hspline',
				'cmin': 0,
				'cmax': 1,
			},
		)],
		
		layout= dict(
			title_font_size = 25,
			height=1000,
			width=1500,
		),			
	)
),

])

@app.callback(
Output(‘graph1’,‘children’),
[Input(???)]
)
def update_color(trace, points, state):
# Update parcats colors
new_color = np.zeros(len(df_com), dtype=‘uint8’)
new_color[points.point_inds] = 1
figure.data[0].line.color = new_color

if name == ‘main’:
app.run_server(debug=True)

2 Likes

Have you been able to resolve this issue?

1 Like

no, ended up trying different thing.

Hi, @geekshock have you resolved this by any chance?

Recreating in Dash seems really non-intuitive due to the callbacks with color.

The different thing that you tried, what was it?

Thank you!

Hello @alejohenao,

Thank you for showing interest in this topic and welcome to the community.

Please find the below example:

import plotly.graph_objects as go
import pandas as pd
from dash import Dash, dcc, Output, State, Input, ctx

app = Dash(__name__)

import plotly.graph_objects as go
import pandas as pd
import numpy as np

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, 'gray'], [1, 'firebrick']]

# 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.3}}), 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')

@app.callback(
    Output('myChart', 'figure'),
    Input('myChart','clickData'),
    Input('myChart', 'selectedData'),
    State('myChart', 'figure'),
    prevent_initial_call=True
)

# Update color callback
def update_color(click, points, fig):

    if 'myChart.clickData' in ctx.triggered_prop_ids:
        fig['data'][0]['selectedpoints'] = []
        try:
            fig['data'][0]['selectedpoints'] = [i['pointNumber'] for i in click['points']]
        except:
            fig['data'][0]['selectedpoints'] = []
        # Update parcats colors
        try:
            new_color = np.zeros(len(cars_df), dtype='uint8')
            new_color[[i['pointNumber'] for i in click['points']]] = 1
            fig['data'][1]['line']['color'] = new_color
        except:
            fig['data'][1]['line']['color'] = [0]
    elif 'myChart.selectedData' in ctx.triggered_prop_ids:
        fig['data'][0]['selectedpoints'] = []
        try:
            fig['data'][0]['selectedpoints'] = [i['pointIndex'] for i in points['points']]
        except:
            fig['data'][0]['selectedpoints'] = []
        # Update parcats colors
        try:
            new_color = np.zeros(len(cars_df), dtype='uint8')
            new_color[[i['pointIndex'] for i in points['points']]] = 1
            fig['data'][1]['line']['color'] = new_color
        except:
            fig['data'][1]['line']['color'] = [0]

    return fig


app.layout = dcc.Graph(figure=fig, id='myChart')

app.run(debug=True, port='12345')

As a note, the figure gives off the selectedData and clickData that needed to be passed back to the figure. :slight_smile:


Edit: made it so deselecting will reset the graph colors.

Also note, the selectedData will only change if there has been no interaction on the top chart.

2 Likes

Hello @jinnyzor ,
Thank you very much. I made a few changes to allow multi color brushing just as the documentation example. But this way of selecting colors is exactly what i was looking for. Thank you very much.

If this is ok with you, i would like to share this piece of code in some unaswered questions in stackoverflow, i will give due credit obviously.

Again, thank you for the quick response.

1 Like

Hello @alejohenao,

Sure thing, happy to help. :slight_smile:

1 Like