Hello! I would like to build an app that filters a datatable based on the coordinates chosen by the user. Any tips on how to capture the event of different coordinates being chosen by user as shown in the gif below? We have tried dcc.Graph’s clickData
and the figure
property as the Input of the callback but nothing comes up.
Example code:
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
df = px.data.iris()
fig = px.parallel_coordinates(df, color="species_id",
dimensions=['sepal_width', 'sepal_length', 'petal_width',
'petal_length'],
color_continuous_scale=px.colors.diverging.Tealrose,
color_continuous_midpoint=2)
app.layout = dbc.Container([
dcc.Graph(id="my-graph", figure=fig),
html.Div(id='placeholder')
])
@app.callback(
Output('placeholder', 'children'),
Input("my-graph", "figure")
)
def udpate_table(figure):
print(figure['data'][0])
return ""
if __name__ == "__main__":
app.run_server(debug=True, port=4003)
you can use
from dash import ctx
in the callback
print(ctx.triggered_id)
to evaluate what are the various properties that are getting adjusted on our graph. could be more than the click property
then, generically, you can build an intermediate via dcc.store
and have your datatable receive filter information from the dcc.store
rictuar:
print(ctx.triggered_id)
hi @rictuar
Thank you for the suggestion. I was also trying that but to no avail.
@app.callback(
Output('placeholder', 'children'),
Input("my-graph", "figure")
)
def udpate_table(c):
print(ctx.triggered_id)
print(c)
return ""
Any other ideas on how to capture that event displayed in the gif?
Hello @adamschroeder /@jennalenoble ,
You guys are looking for the restyleData
prop.
Here is a working example.
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
dims = ['sepal_width', 'sepal_length', 'petal_width', 'petal_length']
df = px.data.iris()
fig = px.parallel_coordinates(df, color="species_id",
dimensions=dims,
color_continuous_scale=px.colors.diverging.Tealrose,
color_continuous_midpoint=2)
app.layout = dbc.Container([
dcc.Graph(id="my-graph", figure=fig),
html.Div(id='placeholder')
])
@app.callback(
Output('placeholder', 'children'),
Input("my-graph", "restyleData")
)
def udpate_table(data):
if data:
key = list(data[0].keys())[0]
col = dims[int(key.split('[')[1].split(']')[0])]
rng = data[0][key][0]
if isinstance(rng[0], list):
# if multiple choices combine df
dff = pd.DataFrame(columns=df.columns)
for i in rng:
dff2 = df[df[col].between(i[0], i[1])]
dff = pd.concat([dff,dff2])
else:
# if one choice
dff = df[df[col].between(rng[0], rng[1])]
print(dff)
return ""
if __name__ == "__main__":
app.run_server(debug=True, port=4003)
edited for supporting multiple choices on the same section, as well as allowing migrating to different columns.
Now… to have the data table showing properly. I’m sure you can figure out something.
1 Like
Ok… so, I just wanted to figure this out.
Here is the result:
import dash_bootstrap_components as dbc
from dash import Dash, dcc, html, Input, Output, dash_table, State, Patch
import plotly.express as px
import pandas as pd
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
dims = ['sepal_width', 'sepal_length', 'petal_width', 'petal_length']
df = px.data.iris()
fig = px.parallel_coordinates(df, color="species_id",
dimensions=dims,
color_continuous_scale=px.colors.diverging.Tealrose,
color_continuous_midpoint=2)
app.layout = dbc.Container([
dcc.Graph(id="my-graph", figure=fig),
dash_table.DataTable(id='table', columns=[{'id': i, 'name': i} for i in df.columns]),
dcc.Store(id='activefilters', data={})
])
@app.callback(
Output('table', 'data'),
Input("activefilters", "data")
)
def udpate_table(data):
if data:
dff = df.copy()
for col in data:
if data[col]:
rng = data[col][0]
if isinstance(rng[0], list):
# if multiple choices combine df
dff3 = pd.DataFrame(columns=df.columns)
for i in rng:
dff2 = dff[dff[col].between(i[0], i[1])]
dff3 = pd.concat([dff3, dff2])
dff = dff3
else:
# if one choice
dff = dff[dff[col].between(rng[0], rng[1])]
return dff.to_dict('records')
return df.to_dict('records')
@app.callback(
Output('activefilters', 'data'),
Input("my-graph", "restyleData")
)
def updateFilters(data):
if data:
key = list(data[0].keys())[0]
col = dims[int(key.split('[')[1].split(']')[0])]
newData = Patch()
newData[col] = data[0][key]
return newData
return {}
if __name__ == "__main__":
app.run_server(debug=True, port=4003)
This builds the filters as you go along, this is because the restyleData
doesnt have all of the options, but what currently changed.
5 Likes
Thank you so much Bryan
@jennalenoble Let me know if you need help with the implementation.
2 Likes
@jinnyzor wow that’s excellent, thank you so much!
2 Likes