I want to design the Dash app layout in such a way, that two trend/line charts are arranged in the same container/figure and I want to switch it with a button to display one chart at a time, the x-axis is the same for both charts.
I have tried with the below code but it doesn't update the label of the y-axis and hover text according to charts.
import pandas as pd
import plotly.express as px
dfx = pd.DataFrame({'months':['apr','may','jun','jul','aug','sep','oct','nov','dec','jan','feb','mar']
,'imported_qty':[25,35,45,30,35,45,50,25,30,35,45,40]
,'unit_price':[1.80,1.75,2.10,2.08,2.25,2.20,2.35,2.38,2.28,2.32,2.38,2.51]})
fig = px.line(dfx, x='months', y=dfx['imported_qty'])
fig.update_traces(mode="lines")
fig.update_layout(yaxis={'showgrid': False}
,xaxis={'showgrid': False}
,template='plotly_dark'
,hovermode="x"
,legend=dict(y=1, x=1, font=dict(size=8))
,height=350
,font=dict(size=10, color='gray')
,title={'text': "Import Qty. Trend"
,'y':0.95
,'x':0.5
,'xanchor': 'center'
,'yanchor': 'top'
,'font_size':15
,'font_color':'white'}
,updatemenus=[
dict(
type="buttons",
direction="right",
x=0.7,
y=1,
showactive=True,
buttons=list(
[
dict(
label="Qty",
method="update",
args=[{"y": [dfx['imported_qty']]}
,{'title':'Import Qty. Trend'}
,{'y':'Import qty.'}],
),
dict(
label="Price",
method="update",
args=[{"y": [dfx['unit_price']]}
,{'title':'Unit Price Trend'}
,{'y':'Unit Price'}],
),
]
),
)
]
)
Thank You.
Why not put you fig as dash Graph as a children of a html Div, create two buttons in the layout, and then create a callback that listens on these two buttons and that updates the children of the html Div depending on which button has been clicked ?
Related
How can I process when I have 2 callbacks functions with the same output (in that case : a graph) knowing that the first callback function returns a graph and a slider, whereas the input of 2nd callback function is the value of the new slider (created thanks to the first slider ) and returns also a graph (by adding new traces on the old traces)
The code is like that :
# Create Div to place a conditionally visible element inside
myslider = html.Div(
id='slider-container',
children=[
# Create a slider to hide/show
dcc.Slider(0,5,
step=1,
value=0,
id="newslider"
)
],
hidden=True,
)
mygraph = html.Div(dcc.Graph(id="graph", figure=go.Figure(go.Scattergeo())))
mydropdown = html.Div(dcc.Dropdown(['NYC', 'MTL', 'SF'], id="dropdown",
multi=True,
clearable=False))
layout = dbc.Container([mydropdown, mygraph, myslider,html.Div(id='slider-output-container',hidden=True)])
#app.callback(Output(component_id='graph', component_property='figure'),
Output(component_id='newslider', component_property='max'),
Output("newslider", "marks"),
Output(component_id='slider-container', component_property='hidden'),
Input(component_id='dropdown', component_property='value'),
Input(component_id='timeslider', component_property='value'),
State(component_id='newslider', component_property='max'),
State("newslider", "marks"),
prevent_initial_call=True
)
def create_graph_and_slider(dropdown,value,maxi,marks):
#created the initial traces of the graph associated to the newslider thanks to the value of dropdown
return fig, maxi, marks, False
#app.callback(Output(component_id='slider-output-container', component_property='children'),
Output(component_id='slider-output-container', component_property='hidden'),
Output(component_id='graph', component_property='figure'),
Input(component_id='newslider', component_property='value'),
prevent_initiall_call=True
)
def update_graph_from_extrapolation_slider(value):
#add new traces in the graph thanks to the value of new slider
return 'You have selected "{}"'.format(value), False, fig
I have a 2D plotly graph with a hover feature. When you hover over each point, the label (e.g. 'image 2, cluster 1') associated with that point appears. I'd like for label to be appended onto an existing list if I were to click on the point (rather than just hover over it). The reason why is that I'd later like to use the data of this point to perform another task. Is there an example online that demonstrates how to do this-- have looked through the documentation but haven't found something for this yet. Thanks!
The hoverData that is available to you by default, with some sample data, is this:
{
"points": [
{
"curveNumber": 1,
"pointNumber": 7,
"pointIndex": 7,
"x": 1987,
"y": 74.32,
"bbox": {
"x0": 420.25,
"x1": 426.25,
"y0": 256,
"y1": 262
}
}
]
}
I'm not quite sure what you mean by 'label', so I can only assume that it would be the name of a trace or something similar, like in this example from the Plotly docs:
But as you can see, that's not readily available in the hoverData dict. This means that you'll have to use this information to reference your figure structure as well, so that you end up with something like this:
[['New Zealand', 2002, 79.11]]
And that's not a problem as long as you're willing to use Plotly Dash. I've made a complete setup for you that should meet your requirements. In the app in the image below you'll find a figure along with two output fields for strings. The first field shows the info from that last point you've clicked in the figure. On every click, a new element is added to a list named store. The last fields shows the complete information from the same click.
The answer to your question is, yes, there is a way to save the data of a clicked point in a list. And one way to do so is through the following callback that uses clickdata to reference your figure object, store those references in a list, and append new elements every time you click a new element.
App
Complete code:
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)
You need to use callbacks to perform this type of action (register on_click()). Have defined clicked as a list of clicked points. Demonstrated how this can be achieved with ipwidgets or dash
ipwidgets
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import ipywidgets as widgets
from pathlib import Path
import json
x = np.random.uniform(-10, 10, size=50)
y = np.sin(x)
clicked = []
# construct figure that has holders for points, interpolated line and final lines
fig = go.FigureWidget(
[
go.Scatter(x=x, y=y, mode="markers", name="base_points"),
]
)
fig.update_layout(template="simple_white")
out = widgets.Output(layout={"border": "1px solid black"})
out.append_stdout("Output appended with append_stdout\n")
# create our callback function
#out.capture()
def base_click(trace, points, selector):
global clicked
clicked.append(points.__dict__)
fig.data[0].on_click(base_click)
widgets.HBox([fig, out])
dash
from jupyter_dash import JupyterDash
import dash
from dash.dependencies import Input, Output, State
import numpy as np
import json
clicked = []
# Build App
app = JupyterDash(__name__)
app.layout = dash.html.Div(
[
dash.dcc.Graph(
id="fig",
figure=go.Figure(go.Scatter(x=x, y=y, mode="markers", name="base_points")),
),
dash.html.Div(id="debug"),
]
)
#app.callback(
Output("debug", "children"),
Input("fig", "clickData"),
)
def point_clicked(clickData):
global clicked
clicked.append(clickData)
return json.dumps(clickData)
# Run app and display result inline in the notebook
app.run_server(mode="inline")
I have a plot that I've created with plotly that's interactive with clicks and widgets within Jupyter. I'm happy with how my plot works and I want to export it to use outside of Jupyter using dash. When I take my figure and try to output it with dash all the interactive functions I've created through plotly in Jupyter don't work. When I run the code the figure and layout are present, but the interactive part is gone. If I comment out running this on the app.run_server everything works great. All my functions in plotly use #out.capture() then xx.on_click(function) and I use widgets.Button or widgets.FloatText. Are there changes that I need to make to my capture functions to work in dash? Any suggestions are appreciated!
import pandas as pd
import numpy as np
import io
import os
import plotly.graph_objects as go
import json
import ipywidgets as widgets
from dash import Dash, dcc, html
app=Dash(__name__)
x=np.random.uniform(-10,10,size=50)
y=np.sin(x)
# Initialize the figure using plotly
fig=go.FigureWidget([go.Scatter(x=x,
y=y,
mode='markers',
opacity=1,
marker=dict(
color=['#a3a7e4']*100,
size=[10]*100)
),
go.Scatter(x=[],
y=[],
mode='lines',
marker=dict(
color='DarkSlateGrey')
)])
fig.update_layout(
template='simple_white',
showlegend=False,
title='Title',
xaxis=dict(title="X Axis",
mirror=True,
),
yaxis=dict(title='Y Axis',
mirror=True,
)
)
scatter=fig.data[0]
line = fig.data[1]
# Create the box on the bottom to append the data in the output
out = widgets.Output(layout={'border': '1px solid black'})
out.append_stdout('\n')
reset = widgets.Button(description="Reset")
export = widgets.Button(description="Export")
enter_text=widgets.FloatText(description='Text:')
# Create our callback function
#out.capture()
def update_point(trace, points, selector):
x = list(line.x) + points.xs
y = list(line.y) + points.ys
line.update(x=x, y=y)
c = list(scatter.marker.color)
s = list(scatter.marker.size)
for i in points.point_inds:
c[i] = '#bae2be'
s[i] = 20
with fig.batch_update():
scatter.marker.color = c
scatter.marker.size = s
# Function to clear the output when 'Reset' button is clicked
#out.capture()
def on_reset_clicked(b):
line.update(x=[], y=[])
scatter.marker.color=['#a3a7e4']*100
scatter.marker.size=[10]*100
out.clear_output()
# Function for exporting the points clicked
#out.capture()
def on_export_clicked(b):
line.update(x=[],y=[])
scatter.marker.color=['#a3a7e4']*100
scatter.marker.size=[10]*100
out.clear_output
reset.on_click(on_reset_clicked)
export.on_click(on_export_clicked)
scatter.on_click(update_point)
widgets.VBox([widgets.HBox([reset, export,enter_text]), widgets.VBox([fig, out])])
app.layout=html.Div([
dcc.Graph(
id='Title',
figure=fig
)
])
if __name__=='__main__':
app.run_server(debug=False)
I'm trying to create a dashboard in Pycharm using dash. Here is the error I keep receiving,
html.Div(dcc.Graph(id='line-plot')),
TypeError: Graph() takes no arguments
And below is a snippet of my code where the error is being found (bottom of code). This code ran fine and I was about to populate the dashboard without receiving any errors inside IBM's python environment. I'm assuming I have to tweak something
# TASK 3 - UPDATE LAYOUT COMPONENETS
# html.H1 tag for title , style, and overall font size
# html.Div & dcc.Input() tag to set inputs of the dashboard
# Update output componenent 2nd html.Div to layout the graph dcc.Graph()
app.layout = html.Div(children=[html.H1('Airline Performance Dashboard',
style={'textAlign': 'center', 'color': '#503D36',
'font-size': 40}),
html.Div(["Input Year: ", dcc.Input(id='input-year', value='2010',
type='number',
style={'height': '50px', 'font-size': 35}), ],
style={'font-size': 40}),
html.Br(),
html.Br(),
html.Div(dcc.Graph(id='line-plot')),
])
Here is the rest of the code,
# TASK 4 - ADD APPLICATION CALL BACK FUNCTION and outputs / inputs
# add callback decorator
#app.callback(Output(component_id='line-plot', component_property='figure'),
Input(component_id='input-year', component_property='value'))
# Add computation to callback function and return graph
def get_graph(entered_year):
# Select 2019 data
df = airline_data[airline_data['Year'] == int(entered_year)]
# Group the data by Month and compute average over arrival delay time.
line_data = df.groupby('Month')['ArrDelay'].mean().reset_index()
# TASK 5 - UPDATE CALL BACK FUNCTION go.Figure(data=) and update fig.update_layout()
fig = go.Figure(
data=go.Scatter(x=line_data['Month'], y=line_data['ArrDelay'], mode='lines', marker=dict(color='green')))
fig.update_layout(title='Month vs Average Flight Delay Time', xaxis_title='Month', yaxis_title='ArrDelay')
return fig
# Run the app
if __name__ == '__main__':
app.run_server()
Its safe to say I need an adult.
have added import and simulation of data frame
all other code runs without issue on plotly 5.1.0
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
from jupyter_dash import JupyterDash
import numpy as np
app = JupyterDash(__name__)
# simulate data...
dr = pd.date_range("1-jan-2010", freq="W", periods=200)
airline_data = pd.DataFrame({"Year":dr.year, "Month":dr.month, "ArrDelay":np.random.uniform(2,5,len(dr))})
# TASK 3 - UPDATE LAYOUT COMPONENETS
# html.H1 tag for title , style, and overall font size
# html.Div & dcc.Input() tag to set inputs of the dashboard
# Update output componenent 2nd html.Div to layout the graph dcc.Graph()
app.layout = html.Div(children=[html.H1('Airline Performance Dashboard',
style={'textAlign': 'center', 'color': '#503D36',
'font-size': 40}),
html.Div(["Input Year: ", dcc.Input(id='input-year', value='2010',
type='number',
style={'height': '50px', 'font-size': 35}), ],
style={'font-size': 40}),
html.Br(),
html.Br(),
html.Div(dcc.Graph(id='line-plot')),
])
# TASK 4 - ADD APPLICATION CALL BACK FUNCTION and outputs / inputs
# add callback decorator
#app.callback(Output(component_id='line-plot', component_property='figure'),
Input(component_id='input-year', component_property='value'))
# Add computation to callback function and return graph
def get_graph(entered_year):
# Select 2019 data
df = airline_data[airline_data['Year'] == int(entered_year)]
# Group the data by Month and compute average over arrival delay time.
line_data = df.groupby('Month')['ArrDelay'].mean().reset_index()
# TASK 5 - UPDATE CALL BACK FUNCTION go.Figure(data=) and update fig.update_layout()
fig = go.Figure(
data=go.Scatter(x=line_data['Month'], y=line_data['ArrDelay'], mode='lines', marker=dict(color='green')))
fig.update_layout(title='Month vs Average Flight Delay Time', xaxis_title='Month', yaxis_title='ArrDelay')
return fig
app.run_server(mode="inline")
I've done a bit of research and found that plotly does not natively support click and hover events like the scatter plot does. Can someone find a way around this? I need to be able to click on a Task and use it in a callback to feed to another component in Dash.
fig = ff.create_gantt(df, height=y * 15, bar_width=0.2, index_col='Resource', show_colorbar=True,
group_tasks=True, title='Tags Timeline')
fig.update_layout(
xaxis=dict(
rangeselector=dict(
),
rangeslider=dict(
visible=True
),
type="category",
autorange=False,
range=(0,30)
)
)
#app.callback(
Output("video-display", "currentTime"),
[Input("tags-timeline", "clickData")],
)
def sync_timeline_video(clickData):
if clickData:
print(clickData)
else:
print("none yet")
Thank you