I am trying to add interactivity to a plotly 3d scatter plot which I am hosting using dash. My question contains two related parts:
(i) I would like to manually select points in my 3d scatter and change the selected points' colour to red. Selection should include click event and selection event.
(ii) I would like to be able to delete these points from the plot on pressing of a key e.g. 'delete' key.
Part (i) is similar to an example in the plotly guide https://plot.ly/python/click-events/, however, on_click is not available as a method for 3d scatter charts.
I have been attempting to use FigureWidget as that apparently provides methods to capture clicks and selections, but after 2 days I am struggling to make headway.
Example data (copy below to clipboard and run df = pd.read_clipboard(sep=','))
id,x_val,z_val,y_val
a,11.313449728149418,0.13039110880256777,0.5386387766748618
b,11.321463427315383,0.2360697833061771,1.32441455152796
c,10.127132005050942,0.23085014016641864,1.0961116175427044
d,11.639819269465233,0.0958798324712593,0.6506370305953094
e,8.892696370438149,0.08223988244819926,0.6440321391968353
f,6.711586646011124,0.3657515974938044,
g,7.095030650760687,,0.5723062047617504
h,6.4523124528415,,1.293852184258803
i,7.165105300812886,0.4151365420301895,-0.5920674079031845
j,7.480703395137295,0.14284429977557123,1.0600936940126982
k,5.570775744372319,,
l,4.358946555449826,,
I have example code below which I hope is almost there (but not quite). The idea is that 'handle_click' should capture click events. I should also handle 'selection' events, although I am not sure how to do this as 3d scatter plot does not provide a selection box or lassoo tool. With the callback, I'm not even sure how to initiate, as there is no clickData/selectionData event with 3d scatterplot that I can utilize (so [Input('subindustry-dropdown', 'value')]) is incorrect and note that subindustry-dropdown is not something that I have provided in the example, but I select my ids from a dropdown which returns a subindustry value in my development version. )
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
app.layout = html.Div([html.Div(id = '3d-scatter'),
])
# Calculate and store data
#app.callback(Output('3d-scatter', 'children'),
[Input('subindustry-dropdown', 'value')])
def chart_3d():
f = go.FigureWidget(px.scatter_3d(df, x = 'x_val', y = 'y_val', z = 'z_val', hover_name = 'company_nm'))
f.layout.clickmode = 'event+select'
f.data[0].on_click(handle_click) # if click, then update point/df.
return dcc.Graph(id = '3d_scat', figure=f)
def handle_click(trace, points, selector):
c = list(f.data[0].marker.color)
s = list(f.data[0].marker.size)
for i in points.point_inds:
c[i] = '#bae2be'
s[i] = 20
with f.batch_update():
f.data[0].marker.color = c
f.data[0].marker.size = s
return f.data[0]
Here is a solution which allows:
selecting points by clicking individually
delete selected points by pressing html button
clear selection by pressing html button
according to this issue selecting multiple points in a 3d plot is currently not supported
(The use of FigureWidget does not seem to make a difference, so I removed it)
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import pandas as pd
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
import pandas as pd
df = pd.DataFrame(columns=['x_val','z_val','y_val'])
df.loc['a'] = [11.313449728149418,0.13039110880256777,0.5386387766748618]
df.loc['b'] = [11.321463427315383,0.2360697833061771,1.32441455152796]
df.loc['c'] = [10.127132005050942,0.23085014016641864,1.0961116175427044]
df.loc['d'] = [11.639819269465233,0.0958798324712593,0.6506370305953094]
df.loc['e'] = [8.892696370438149,0.08223988244819926,0.6440321391968353]
df.loc['f'] = [6.711586646011124,0.3657515974938044,0]
df.loc['g'] = [7.095030650760687,0,0.5723062047617504]
df.loc['h'] = [6.4523124528415,0,1.293852184258803]
df.loc['i'] = [7.165105300812886,0.4151365420301895,-0.5920674079031845]
df.loc['j'] = [7.480703395137295,0.14284429977557123,1.0600936940126982]
df.loc['k'] = [5.570775744372319,0,0]
df.loc['l'] = [4.358946555449826,0,0]
def create_figure(skip_points=[]):
dfs = df.drop(skip_points)
return px.scatter_3d(dfs, x = 'x_val', y = 'y_val', z = 'z_val')
f= create_figure()
app.layout = html.Div([html.Button('Delete', id='delete'),
html.Button('Clear Selection', id='clear'),
dcc.Graph(id = '3d_scat', figure=f),
html.Div('selected:'),
html.Div(id='selected_points'), #, style={'display': 'none'})),
html.Div('deleted:'),
html.Div(id='deleted_points') #, style={'display': 'none'}))
])
#app.callback(Output('deleted_points', 'children'),
[Input('delete', 'n_clicks')],
[State('selected_points', 'children'),
State('deleted_points', 'children')])
def delete_points(n_clicks, selected_points, delete_points):
print('n_clicks:',n_clicks)
if selected_points:
selected_points = json.loads(selected_points)
else:
selected_points = []
if delete_points:
deleted_points = json.loads(delete_points)
else:
deleted_points = []
ns = [p['pointNumber'] for p in selected_points]
new_indices = [df.index[n] for n in ns if df.index[n] not in deleted_points]
print('new',new_indices)
deleted_points.extend(new_indices)
return json.dumps(deleted_points)
#app.callback(Output('selected_points', 'children'),
[Input('3d_scat', 'clickData'),
Input('deleted_points', 'children'),
Input('clear', 'n_clicks')],
[State('selected_points', 'children')])
def select_point(clickData, deleted_points, clear_clicked, selected_points):
ctx = dash.callback_context
ids = [c['prop_id'] for c in ctx.triggered]
if selected_points:
results = json.loads(selected_points)
else:
results = []
if '3d_scat.clickData' in ids:
if clickData:
for p in clickData['points']:
if p not in results:
results.append(p)
if 'deleted_points.children' in ids or 'clear.n_clicks' in ids:
results = []
results = json.dumps(results)
return results
#app.callback(Output('3d_scat', 'figure'),
[Input('selected_points', 'children'),
Input('deleted_points', 'children')],
[State('deleted_points', 'children')])
def chart_3d( selected_points, deleted_points_input, deleted_points_state):
global f
deleted_points = json.loads(deleted_points_state) if deleted_points_state else []
f = create_figure(deleted_points)
selected_points = json.loads(selected_points) if selected_points else []
if selected_points:
f.add_trace(
go.Scatter3d(
mode='markers',
x=[p['x'] for p in selected_points],
y=[p['y'] for p in selected_points],
z=[p['z'] for p in selected_points],
marker=dict(
color='red',
size=5,
line=dict(
color='red',
width=2
)
),
showlegend=False
)
)
return f
if __name__ == '__main__':
app.run_server(debug=True)
Related
I have written following code which updates Plotly Chart with some random values every 5 seconds, however after few seconds the new data is located outside the chart and is not visible. Is there an easy way to reset the axes everytime it's needed?
Also how can I make this responsive so it will auto-scale to full window?
import requests
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
from datetime import datetime
import json
import plotly.graph_objs as go
import random
# Create empty lists to store x and y values
x_values = []
y_values = []
# Initialize the dash app
app = dash.Dash()
app.layout = html.Div([
dcc.Graph(id='live-graph', animate=True,responsive=True),
dcc.Interval(
id='graph-update',
interval=5000,
n_intervals=0
),
])
# Define the callback function
#app.callback(Output('live-graph', 'figure'), [Input('graph-update', 'n_intervals')])
def update_graph(n):
current_value= random.randint(2000, 8000)
# Get the current datetime
x = datetime.now()
x_values.append(x)
# Get the response value and append it to the y_values list
y = current_value
y_values.append(y)
# Create the line chart
trace = go.Scatter(x=x_values, y=y_values)
data = [trace]
layout = go.Layout(title='Real-time Data',
xaxis=dict(title='Datetime'),
yaxis=dict(title='Value'))
return go.Figure(data=data, layout=layout).update_layout(
autosize=True,
margin=dict(l=0, r=0, t=0, b=0)
)
if __name__ == '__main__':
app.run_server(debug=False)
Many thanks!
Using this method.
Fixed y axis range from 2000 to 8000
And x axis range find min and max every time.
layout = go.Layout(title='Real-time Data',
yaxis_range=[2000,8000],
xaxis_range=[min(x_values),max(x_values)],
xaxis=dict(title='Datetime'),
yaxis=dict(title='Value'))
return go.Figure(data=data, layout=layout).update_layout(
autosize=True,
margin=dict(l=0, r=0, t=50, b=50)
)
The first segment of code below (code # 1) generates a graph for which 1) when you hover over each point, the data associated with each point is displayed and 2) when you click on each point, the data associated with each point is saved to a list. For this code, I would also like to display the image associated with each point. Assume the dataframe df has a column 'image' which contains the image pixel/array data of each point. I found code online (code #2) that implements this image hover feature but without the click feature. I'm having a hard time combining the image hover feature with the click feature. So, basically, I'm trying to combine the click feature (click on point, it's data is saved to a list) of code # 2 into code # 1.
CODE # 1 (with click feature):
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
from dash import dcc
import dash_html_components as html
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# app info
app = JupyterDash(__name__)
styles = {
'pre': {
'border': 'thin lightgrey solid',
'overflowX': 'scroll'
}
}
# data
df = px.data.gapminder().query("continent=='Oceania'")
# plotly figure
fig = px.line(df, x="year", y="lifeExp", color="country", title="No label selected")
fig.update_traces(mode="markers+lines")
app.layout = html.Div([
dcc.Graph(
id='figure1',
figure=fig,
),
html.Div(className
='row', children=[
html.Div([
dcc.Markdown(d("""Hoverdata using figure references""")),
html.Pre(id='hoverdata2', style=styles['pre']),
], className='three columns'),
html.Div([
dcc.Markdown(d("""
Full hoverdata
""")),
html.Pre(id='hoverdata1', style=styles['pre']),
], className='three columns')
]),
])
# container for clicked points in callbacks
store = []
#app.callback(
Output('figure1', 'figure'),
Output('hoverdata1', 'children'),
Output('hoverdata2', 'children'),
[Input('figure1', 'clickData')])
def display_hover_data(hoverData):
if hoverData is not None:
traceref = hoverData['points'][0]['curveNumber']
pointref = hoverData['points'][0]['pointNumber']
store.append([fig.data[traceref]['name'],
fig.data[traceref]['x'][pointref],
fig.data[traceref]['y'][pointref]])
fig.update_layout(title = 'Last label was ' + fig.data[traceref]['name'])
return fig, json.dumps(hoverData, indent=2), str(store)
else:
return fig, 'None selected', 'None selected'
app.run_server(mode='external', port = 7077, dev_tools_ui=True,
dev_tools_hot_reload =True, threaded=True)
CODE # 2 (includes image hover feature):
from jupyter_dash import JupyterDash
from dash import Dash, dcc, html, Input, Output, no_update
import plotly.graph_objects as go
import pandas as pd
## create sample random data
df = pd.DataFrame({
'x': [1,2,3],
'y': [2,3,4],
'z': [3,4,5],
'color': ['red','green','blue'],
'img_url': [
"https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2880px-Stack_Overflow_logo.svg.png",
"https://upload.wikimedia.org/wikipedia/commons/3/37/Plotly-logo-01-square.png",
"https://upload.wikimedia.org/wikipedia/commons/thumb/e/ed/Pandas_logo.svg/2880px-Pandas_logo.svg.png"
]
})
fig = go.Figure(data=[
go.Scatter3d(
x=df['x'],
y=df['y'],
z=df['z'],
mode='markers',
marker=dict(color=df['color'])
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
scene = dict(
xaxis = dict(range=[-1,8],),
yaxis = dict(range=[-1,8],),
zaxis = dict(range=[-1,8],),
),
)
app = JupyterDash(__name__)
server = app.server
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
#app.callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['img_url']
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
], style={'width': '100px', 'white-space': 'normal'})
]
return True, bbox, children
app.run_server(mode="inline")
you want a callback that does hover and click
on hover display image associated with point and full hover info
on click update list of clicked points and figure title
Assume the dataframe df has a column 'image' have created one that is a b64 encoded image
have inserted this into the figure by using customdata (hover_data parameter in px)
have added an additional div image
have changed callback to behave as it did before and also contents on new div. This uses b64 encoded image, extending with necessary "data:image/png;base64,"
need to take note of this https://dash.plotly.com/vtk/click-hover and https://dash.plotly.com/advanced-callbacks
import json
from textwrap import dedent as d
import pandas as pd
import plotly.graph_objects as go
import numpy as np
import dash
import plotly.express as px
from dash.dependencies import Input, Output
from jupyter_dash import JupyterDash
import warnings
import base64, io, requests
from PIL import Image
from pathlib import Path
warnings.simplefilter(action="ignore", category=FutureWarning)
# app info
app = JupyterDash(__name__)
styles = {"pre": {"border": "thin lightgrey solid", "overflowX": "scroll"}}
# data for whare images can be found
df_flag = pd.read_csv(
io.StringIO(
"""country,Alpha-2 code,Alpha-3 code,URL
Australia,AU,AUS,https://www.worldometers.info//img/flags/small/tn_as-flag.gif
New Zealand,NZ,NZL,https://www.worldometers.info//img/flags/small/tn_nz-flag.gif"""
)
)
# ensure that images exist on your file system...
f = Path.cwd().joinpath("flags")
if not f.exists():
f.mkdir()
# download some images and use easy to use filenames...
for r in df_flag.iterrows():
flag_file = f.joinpath(f'{r[1]["Alpha-3 code"]}.gif')
if not flag_file.exists():
r = requests.get(r[1]["URL"], stream=True, headers={"User-Agent": "XY"})
with open(flag_file, "wb") as fd:
for chunk in r.iter_content(chunk_size=128):
fd.write(chunk)
# encode
def b64image(country):
b = io.BytesIO()
im = Image.open(Path.cwd().joinpath("flags").joinpath(f"{country}.gif"))
im.save(b, format="PNG")
b64 = base64.b64encode(b.getvalue())
return b64.decode("utf-8")
df_flag["image"] = df_flag["Alpha-3 code"].apply(b64image)
# data
df = px.data.gapminder().query("continent=='Oceania'")
df = df.merge(df_flag, on="country") # include URL and b64 encoded image
# plotly figure. Include URL and image columns in customdata by using hover_data
fig = px.line(
df,
x="year",
y="lifeExp",
color="country",
title="No label selected",
hover_data={"URL": True, "image": False},
)
fig.update_traces(mode="markers+lines")
app.layout = dash.html.Div(
[
dash.dcc.Graph(
id="figure1",
figure=fig,
),
dash.html.Div(
className="row",
children=[
dash.html.Div(id="image"),
dash.html.Div(
[
dash.dcc.Markdown(d("""Hoverdata using figure references""")),
dash.html.Pre(id="hoverdata2", style=styles["pre"]),
],
className="three columns",
),
dash.html.Div(
[
dash.dcc.Markdown(
d(
"""
Full hoverdata
"""
)
),
dash.html.Pre(id="hoverdata1", style=styles["pre"]),
],
className="three columns",
),
],
),
]
)
# container for clicked points in callbacks
store = []
#app.callback(
Output("figure1", "figure"),
Output("hoverdata1", "children"),
Output("hoverdata2", "children"),
Output("image", "children"),
[Input("figure1", "clickData"), Input("figure1", "hoverData")],
)
def display_hover_data(clickData, hoverData):
# is it a click or hover event?
ctx = dash.callback_context
if ctx.triggered[0]["prop_id"] == "figure1.clickData":
traceref = clickData["points"][0]["curveNumber"]
pointref = clickData["points"][0]["pointNumber"]
store.append(
[
fig.data[traceref]["name"],
fig.data[traceref]["x"][pointref],
fig.data[traceref]["y"][pointref],
]
)
fig.update_layout(title="Last label was " + fig.data[traceref]["name"])
return fig, dash.no_update, str(store), dash.no_update
elif ctx.triggered[0]["prop_id"] == "figure1.hoverData":
# simpler case of just use a URL...
# dimg = dash.html.Img(src=hoverData["points"][0]["customdata"][0], style={"width": "30%"})
# question wanted image encoded in dataframe....
dimg = dash.html.Img(
src="data:image/png;base64," + hoverData["points"][0]["customdata"][1],
style={"width": "30%"},
)
return fig, json.dumps(hoverData, indent=2), dash.no_update, dimg
else:
return fig, "None selected", "None selected", "no image"
# app.run_server(mode='external', port = 7077, dev_tools_ui=True,
# dev_tools_hot_reload =True, threaded=True)
app.run_server(mode="inline")
I am creating a 3D scatter plot based off a pandas dataframe, and then I want to re-draw it with slightly updated data whenever the user presses a button in my program. I almost have this functionality working, except the updated figure is drawn via a new opened tab, when really I just want my origin existing figure to be updated.
Here is my code. First I initialize the plot with 'version 1' of the data, then I set up a simple while loop to wait for the user to request an update. Then ideally once they enter input to ask for the update, I just re-draw everything in the same tab that is open. But instead a new tab is opened (which redraws the data correctly at least).
fig = go.Figure(data=[go.Scatter3d(x=df['x'],y=df['y'],z=df['z'],mode='markers', marker=dict(
size=4,
color=df['y'], # set color to an array/list of desired values
colorscale='Viridis', # choose a colorscale
opacity=0.3
))])
# Column max and mins for plotting:
xmax = df_1.max(axis=0)['x']; xmin = df_1.min(axis=0)['x']
ymax = df_1.max(axis=0)['y']; ymin = df_1.min(axis=0)['y']
zmax = df_1.max(axis=0)['z']; zmin = df_1.min(axis=0)['z']
fig.update_layout(
scene = dict(xaxis = dict(nticks=4, range=[xmin,xmax],),
yaxis = dict(nticks=4, range=[ymin,ymax],),
zaxis = dict(nticks=4, range=[zmin,zmax],),))
f2 = go.FigureWidget(fig)
f2.show()
#fig.show()
while True:
choice = input("> ")
choice = choice.lower() #Convert input to "lowercase"
if choice == 'exit':
print("Good bye.")
break
if choice == 'w':
print("W, moving forward")
cube_origin = cube_origin + np.array([0.1,0,0])
df_cube = createCubeMesh(cube_size, cube_density, cube_origin)
new_df = df_scene_orig.copy()
new_df = new_df.append(df_cube)
fig = go.Figure(data=[go.Scatter3d(x=new_df['x'],y=new_df['y'],z=new_df['z'],mode='markers', marker=dict(
size=4,
color=new_df['y'], # set color to an array/list of desired values
colorscale='Viridis', # choose a colorscale
opacity=0.3
))])
f2 = go.FigureWidget(fig)
f2.show()
I based my code on another answer that said to use go.FigureWidget(fig), but it doesn't seem to work as intended.
Edit
Instead of me using f2.show() at the end, I just want a simple thing analogous to f2.update() that redraws.
This is the case you want.
Everywhere in this page that you see fig.show(), you can display the same figure in a Dash application by passing it to the figure argument of the Graph component from the built-in dash_core_components package like this:
import plotly.graph_objects as go
fig = go.Figure(
data=[go.Scatter(
mode="markers+text",
x=[10, 20],
y=[20, 25],
text=["Point A", "Point B"]
)],
layout=dict(height=400, width=400, template="none")
)
import dash
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash()
app.layout = html.Div([
dcc.Graph(figure=fig)
])
app.run_server(debug=True, use_reloader=False)
reference: https://plotly.com/python/figure-introspection/
Help you write a code that is closest to your needs:
import plotly as py
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import pandas as pd
import numpy as np
py.offline.init_notebook_mode(connected=True)
app = JupyterDash('SimpleExample')
app.layout = html.Div([
dcc.Dropdown(id='dropdown', options=[
{'label': 'W', 'value': 'W'},
{'label': 'exit', 'value': 'exit'}],
value='exit'),
dcc.Graph(id='graph-court')
])
def random_data():
# sample dataframe of a wide format
np.random.seed(4)
cols = list('xyz')
X = np.random.randint(50, size=(3, len(cols)))
df = pd.DataFrame(X, columns=cols)
df.iloc[0] = 0
return df
df = random_data()
def create_figure(df):
fig = go.Figure(data=[go.Scatter3d(x=df['x'], y=df['y'], z=df['z'], mode='markers', marker=dict(
size=10,
color=df['y'],
colorscale='Viridis',
opacity=0.3
))])
# Column max and mins for plotting:
xmax = df.max(axis=0)['x']
xmin = df.min(axis=0)['x']
ymax = df.max(axis=0)['y']
ymin = df.min(axis=0)['y']
zmax = df.max(axis=0)['z']
zmin = df.min(axis=0)['z']
fig.update_layout(
scene=dict(xaxis=dict(nticks=4, range=[xmin, xmax], ),
yaxis=dict(nticks=4, range=[ymin, ymax], ),
zaxis=dict(nticks=4, range=[zmin, zmax], ), ))
fig = go.FigureWidget(fig)
return fig
#app.callback(Output('graph-court', 'figure'),
[Input('dropdown', 'value')])
def update_figure(selected_value):
selected_value = selected_value.lower() # Convert input to "lowercase"
if selected_value == 'exit':
print("Good bye.")
new_x, new_y, new_z = [], [], []
else:
print("W, moving forward")
# new data
new_x, new_y, new_z = np.random.randint(10, size=(3, 1))
# ploy
fig = create_figure(df) # Set as global variable or local variable as required
fig.add_trace(go.Scatter3d(x=new_x, y=new_y, z=new_z, marker=dict(size=10, color='green'), mode='markers'))
return fig
app.run_server(debug=False, use_reloader=False)
Estimated that your "tab" is referring to "browser tab" it is basically not possible with the standard renderer.
With the renderer browser it serves a one-shot server on a random port, which is shutting down immediately after the rendering is done. You can check that by reloading the graph in browser.
You can:
generate a static image and serve that yourself in a webapp (e.g. with flask) with f2.write_image("test.svg")
generate a dynamic html content by f2.show(renderer = "iframe") and serve that with e.g. flask
simply use plotly dash, look here for impressions
Try using Plotly for plotting, it has a functionality (Visibility), using that you can update your plot on button click or drop down.
The below example is for dropdown.
import pandas as pd
import numpy as np
import plotly.offline as py_offline
import plotly.graph_objs as go
from plotly import tools
py_offline.init_notebook_mode()
trace = go.Scatter(
x=[1, 2, 3],
y=[4, 5, 6]
)
fig = tools.make_subplots(rows=10, cols=1)
for k in range(10):
fig.append_trace(trace, k+1, 1)
updatemenus=list([
dict(
buttons=[],
direction = 'down',
pad = {'r': 10, 't': 10},
showactive = True,
x = 0,
xanchor = 'left',
y = 1.2,
yanchor = 'top'
),
])
lister = []
for k in range(11):
lister.append(dict(
args=['visible', [True for k in range(10)] if k == 0 else [True if (i+1) == k else False for i in range(10)]],
label='Show Trace ' + str( 'All' if k == 0 else k),
method='restyle'
))
updatemenus[0]['buttons'] = lister
fig['layout']['updatemenus'] = updatemenus
fig['layout'].update(title='subplots')
py_offline.iplot(fig, filename='simple-subplot')
I would like to add a select all for my dropdown, and make it the default when the app opens up, with the ability to then one by one remove capsule and also to unselect the select all button and select a group of capsule. I managed to find several ways that other people have use the select all option but none seems to work for my situation. Thank you!
Data looks this this.
Dash look like this.
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import dash # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
server = app.server
df = pd.read_csv("tcd vs rh.csv")
print(df)
capsuleID = df['Capsule_ID'].unique()
print(capsuleID)
capsuleID_names = list(capsuleID)
print(capsuleID_names)
capsuleID_names_1 = [{'label': k, 'value': k } for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2
app.layout = html.Div([
html.H1("Web Application Dashboards with Dash", style={'text-align': 'center'}),
dcc.Dropdown(id="capsule_select",
options=capsuleID_names_all,
optionHeight=25,
multi=True,
searchable=True,
placeholder='Please select...',
clearable=True,
value=[''],
style={'width': "40%"}
),
html.Div([
dcc.Graph(id="the_graph")
]),
])
# -----------------------------------------------------------
#app.callback(
Output('the_graph', 'figure'),
[Input('capsule_select', 'value')]
)
def update_graph(capsule_chosen):
if capsule_chosen == 'all_values':
dff = df['Capsule_ID']
else:
dff = df[df['Capsule_ID'].isin(capsule_chosen)] # filter all rows where capsule ID is the capsule ID selected
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition='top center')
return scatterplot
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
If by default you want to select everything from df['Capsule_ID'] you can simply pass it to the value of your dropdown.
Then you can change your callback to something like this for the 'select all' functionality:
#app.callback(
Output("the_graph", "figure"),
Output("capsule_select", "value"),
Input("capsule_select", "value"),
)
def update_graph(capsules_chosen):
dropdown_values = capsules_chosen
if "All" in capsules_chosen:
dropdown_values = df["Capsule_ID"]
dff = df
else:
dff = df[df["Capsule_ID"].isin(capsules_chosen)]
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
)
scatterplot.update_traces(textposition="top center")
return scatterplot, dropdown_values
In addition to your check not working with the default dropdown value, you were doing this:
dff = df['Capsule_ID']
which means you were setting dff to a single column. This is not what want since 'tcd' and 'humidity' columns don't exist on df['Capsule_ID'].
I am trying to make a live updating dash app but the live update only shows when I manually press the refresh button on the web page until then the graph doesn't update , I am trying to generate a progressively updating heat map from some data derived from sensor reading to show how it would look like if the data is read in real time.
Data Source is available at : Data source used in code below 1MB in size only
import dash
from dash.dependencies import Output, Input
import dash_core_components as dcc
import dash_html_components as html
import plotly
import pandas as pd
import random
import plotly.graph_objs as go
# The csv file for this dataset used here can be downloaded from:
# https://easyupload.io/0ni29p (Hyperlinked in post as well)
cols = [ 'shot' , 'chan' , 'peak_amplitude', 'onset_time_(ms)', 'bubble_period_(ms)']
cols_bubble_period = [ 'shot' , 'chan' , 'bubble_period_(ms)']
source_df = pd.read_csv('seq-4bubbleQC.txt').drop(['sequence', 'file'], axis=1)
source_df.columns = cols
source_df.loc[source_df['shot'] % 2 == 0, 'chan'] += 18
all_results = pd.DataFrame(columns = cols)
i=0
def df_to_plotly(df):
return {'z': df.values.tolist(),
'x': df.columns.tolist(),
'y': df.index.tolist()}
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Graph(id = 'live-graph', animate = True),
html.Button('START_PLOT', id='plot_start',n_clicks=0),
html.H2(id='hidden-div', style= {'display': 'none'} ),
dcc.Interval(
id = 'graph-update',
interval = 5*1000,
n_intervals = 0
),
]
)
#app.callback(
Output('hidden-div', 'children'),
[ Input('graph-update', 'n_intervals') ],
[Input('plot_start', 'n_clicks')]
)
def update_frame(n_clicks, n_interval):
global source_df
global i
global all_results
if n_clicks == 0:
raise dash.exceptions.PreventUpdate()
all_results = all_results.append((source_df[i: i+36]))
i+=36
#print(i)
#app.callback(
Output('live-graph', 'figure'),
[ Input('graph-update', 'n_intervals') ],
[Input('plot_start', 'n_clicks')]
)
def update_graph_heat(n_clicks, n_interval):
global all_results
all_results_1 = all_results.apply(pd.to_numeric)
#all_results_1 = all_results_1.drop_duplicates()
#all_results_1.to_csv('all_results.csv')
df_s1 = all_results_1.loc[all_results_1['chan'] <=18]
df_s1_bp = df_s1[cols_bubble_period]
#print(df_s1_bp)
#df_s1_bp.to_csv('test_data.csv')
df_s1_pivoted = df_s1_bp.pivot( 'chan', 'shot', 'bubble_period_(ms)')
data = go.Heatmap(df_to_plotly(df_s1_pivoted) , colorscale='rainbow', zmin=30.0, zmax=210.0)
return {'data': [data],
'layout' : go.Layout(xaxis_nticks=20, yaxis_nticks=20)}
if __name__ == '__main__':
app.run_server()
The graph only updates when a manual page refresh is done , how can I make the updated graph appear automatically?
Your callbacks are constructed incorrectly. All inputs should be within the same list. Here is what your first one should look like:
#app.callback(
Output('hidden-div', 'children'),
[Input('graph-update', 'n_intervals'),
Input('plot_start', 'n_clicks')]
)
def update_frame(n_clicks, n_interval):
global source_df...
# etc.