so today, I'm trying to create a simple dahsboard with data from multiple dataframes. I use the dropdown to select the dataframe to show in the plot. However, when I run this code, I got callback error in the web page says
Callback error updating loglog.figure
Traceback (most recent call last):
File "C:\Users\nahar\AppData\Local\Temp/ipykernel_1556/982666652.py", line 63, in build_graph
TypeError: string indices must be integers
I don't understand why this occurs, here is the code
logs = ['CALI','RDEP','GR','RHOB','NPHI','SP','DTC']
colors = ['black','firebrick','green','mediumaquamarine','royalblue','goldenrod','lightcoral']
log_cols = np.arange(1,8)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.SANDSTONE], meta_tags=[
{"name": "viewport", "content": "width=device-width, initial-scale=1"}
])
server = app.server
app.config.suppress_callback_exceptions = True
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.H1('FORCE 2020 Well Log Challange Dashboard',
className='text-center mb-4'),
width=12)
]),
dbc.Row([
dbc.Col([
dcc.Dropdown(id='droplog',
options=[
{'label':'15/9-13','value':'well1'},
{'label':'15/9-15','value':'well2'},
{'label':'15/9-17','value':'well3'},
{'label':'16/1-2','value':'well4'},
{'label':'16/1-6 A','value':'well5'},
{'label':'16/10-1','value':'well6'},
{'label':'16/10-2','value':'well7'},
{'label':'16/10-3','value':'well8'},
{'label':'16/10-5','value':'well9'},
{'label':'16/11-1 ST3','value':'well10'},
{'label':'16/2-11 A','value':'well11'},
{'label':'16/2-16','value':'well12'}
], multi=False, value='well1'),
dcc.Graph(id='loglog',figure={})
])
]),
dbc.Row([
])
])
#app.callback(
Output(component_id='loglog',component_property='figure'),
[Input(component_id='droplog',component_property='value')]
)
def build_graph(plot_chosen):
logplot = make_subplots(rows=1, cols=len(logs), shared_yaxes=True)
for i in range (len(logs)):
if i == 1:
logplot.add_trace(go.Scatter(x=plot_chosen[logs[i]], y=plot_chosen['DEPTH_MD'],
name=logs[i], line_color=colors[i]),row=1, col=log_cols[i])
logplot.update_xaxes(type='log',row=1, col=log_cols[i], title_text=logs[i],
tickfont_size=12, linecolor='#585858')
else:
logplot.add_trace(go.Scatter(x=plot_chosen[logs[i]], y=plot_chosen['DEPTH_MD'],
name=logs[i], line_color=colors[i]),row=1, col=log_cols[i])
logplot.update_xaxes(col=log_cols[i], title_text=logs[i], linecolor='#585858')
logplot.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='inside', tickangle= 0)
logplot.update_yaxes(tickmode='linear', tick0=0,dtick=250, showline=True, linewidth=2, linecolor='black',
mirror=True, ticks='outside')
logplot.update_yaxes(row=1, col=1, autorange='reversed')
logplot.update_layout(height=750, width=1200, showlegend=False)
logplot.update_layout(plot_bgcolor = "rgba(0,0,0,0)", paper_bgcolor = "rgba(0,0,0,0)")
return logplot
if __name__=='__main__':
app.run_server(debug=True,use_reloader=False)
and the input data used is in the form of many dataframes, one of which is like this
for well 1
I tried to run defined function only, which build_graph, and it worked like a charm. The result of figure is shown here
I think I know what the problem is: the variable plot_chosen which is the argument in the function build_graph is a string. As a result you can not type y=plot_chosen['DEPTH_MD']. Although after choosing 15/9-13 on the Dropdown menu it has a value of well1, it does not represent the dataframe but a simple string. Try creating for example a dictionary with the dataframes
dataframes = {'well1': well1, 'well2': well2, ...}
and then choose the appropriate DataFrame from the dictionary.
So for example when the user chooses 15/9-13 on the dropdown you will get plot_chosen='well1' and you can simply get the dataframe well1 by using dataframes[plot_chosen].
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 the following problem:
I try to plot a plotly.scatter plot combined with dash which should be changeable with sliders using callback.
I searched through the documentation of dash and plotly and found some examples for basic and advanced callbacks and also for Sliders
https://dash.plotly.com/basic-callbacks
https://dash.plotly.com/advanced-callbacks
https://dash.plotly.com/dash-core-components/slider
The Sliders are working and the shown data is also changing but the old plot doesnt vanish even if i reload the page.
I dont know if the problem is in the html component or in the callback.
from math import cos,sin,radians
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from dash import Dash, dcc, html, Input, Output, callback
import pandas as pd
I skip the calculation which is not called in the callback because im quite sure the problem isnt there but if you want i can provide it aswell.
def s_out(XTvar, YTvar, ZTvar, XCvar, YCvar, ZCvar):
for a in np.linspace(0,360,3600):
s1i=cos(radians(a))
s2i=sin(radians(a))
fe=fE_tsaiwu((s1i,s2i,0,0,0,0),getm(XTvar, YTvar, ZTvar, XCvar, YCvar,ZCvar))
s1.append(s1i/fe)
s2.append(s2i/fe)
return s1, s2
s1, s2 = s_out(XTvar, YTvar, ZTvar, XCvar, YCvar, ZCvar)
Html component with callback:
(Initialising the figure in the Div is not even necessary i figured out)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(
id='Tsai',
# figure=fig
figure = {}
),
html.H3(children='XTvar'),
html.H5(children='Der Wert des Sliders wird hier direkt unter dem momentanen Punkt angezeigt'),
dcc.Slider(
id= "XTvar",
min = 1,
max = 1500,
value = 1000,
tooltip={"placement": "bottom", "always_visible": True}
),
html.H3(children='YTvar'),
dcc.Slider(
id= "YTvar",
min = 1,
max = 50,
value = 40,
),
html.H3(children='ZTvar'),
dcc.Slider(
id= "ZTvar",
min = 1,
max = 50,
value = 40,
),
html.H3(children='XCvar'),
dcc.Slider(
id= "XCvar",
min = 500,
max = 1000,
value = 700,
),
html.H3(children='YCvar'),
dcc.Slider(
id= "YCvar",
min = 100,
max = 150,
value = 120,
),
html.H3(children='ZCvar'),
dcc.Slider(
id= "ZCvar",
min = 100,
max = 150,
value = 120,
)
])
#app.callback(
Output(component_id='Tsai', component_property='figure'),
Input(component_id='XTvar', component_property='value'),
Input(component_id='YTvar', component_property='value'),
Input(component_id='ZTvar', component_property='value'),
Input(component_id='XCvar', component_property='value'),
Input(component_id='YCvar', component_property='value'),
Input(component_id='ZCvar', component_property='value')
)
def updatefigure(XTvar, YTvar, ZTvar, XCvar, YCvar, ZCvar):
df = zip(s_out(XTvar, YTvar, ZTvar, XCvar, YCvar, ZCvar))
fig = px.scatter(df,s1, s2)
return fig
if __name__ == '__main__':
app.run_server(debug=True)
Here you can see what is happening if i move one of the sliders.
I appreciate every kind of help and i hope the problem is well described.
Your logic in your dash callback is incomplete.
If you take a look at s1, s2 after you move your slider, you can find their length is increasing by 3600 every time you move your slider, since you never clear s1, s2 after each call to the callback.
Therefore everytime you move your slider, you are drawing a new eclipse by appending new values to s1, s2!
Here are some advises to your code, to avoid this behaviour.
Avoid defining global variables like s1, s2 = s_out(XTvar, YTvar, ZTvar, XCvar, YCvar, ZCvar) in your code. Global variables is a cache in your Dash app, and its value persist until you kill your debugging session. We have little control to them in Dash. See Dash Documentation for details.
If you need to cache some values between callbacks, use dcc.Store
Use Dash built-in debugger to see what is inside the callbacks. It could be useful to trace more complex callbacks.
I am relatively new to Dash, and thought I understood the callbacks pretty well. However, I am now in a situation where it seems I need to have all callbacks within one callback, as my program works fine when the one is called.
When I have multiple callbacks, they work fine individually; meaning that if I comment one out the callback works as desired. I am able to get the correct functionality using context.triggered, but I do not want to have everything in one massive callback, and I feel like this is an issue with my understanding. I have tried limiting to one input on the callback, but that still does not work. Am I passing the whole app layout to the callback?
If I am, how do I limit what is being passed beyond the ids? How
can callbacks?
An adapted working example is below:
import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import dash_table
from dash.dependencies import Input, Output, State
import plotly.express as px
from dash import callback_context, no_update
import webbrowser
from flask import request
chrome_path = 'C:/Program Files (x86)/Google/Chrome/Application/chrome.exe %s'
df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/Mining-BTC-180.csv").drop('Unnamed: 0',axis=1)
selected_cols = ['Number-transactions', 'Output-volume(BTC)',
'Market-price', 'Hash-rate', 'Cost-per-trans-USD', 'Mining-revenue-USD',
'Transaction-fees-BTC']
class Parameter_Viewer():
def __init__(self, df, **kwargs):
revenue_plot = px.scatter(df, x="Date", y="Mining-revenue-USD",title='Mining-revenue-USD')
Hash_plot = px.scatter(df, x="Date", y="Hash-rate",title='Hash-rate')
parameter_table = dash_table.DataTable(
id='db-table',
columns=[{"name": i, "id": i} for i in selected_cols],
data=df.to_dict('records'),
#page_action="native",
page_action='none',
style_table={'height': '300px', 'overflowY': 'auto'},
editable=True
)
app = dash.Dash()
app.layout = html.Div(
children=[
#first row
html.Div(
children=[
html.Div(children=[
html.H1("Parameter", className="menu-title"),
dcc.Dropdown(
id="parameter-filter",
options=[
{"label": parameter, "value": parameter} for parameter in df.columns
],
value="Mining-revenue-USD",
clearable=False,
className="dropdown",
),]),
html.Div(children=[
html.H1("Data Type", className="menu-title"),
dcc.Dropdown(
id="data-selector",
options=[
{"label": data_col, "value": data_col} for data_col in selected_cols
],
value="Hash-rate",
clearable=False,
className="dropdown",
),]),
html.Button("Close Viewer", id="Close_Btn")
]
),
html.Div(children=[
html.H1(children="Database Analytics",),
parameter_table]),
html.Div(children=[
html.Div(
dcc.Graph(id='param-plot',figure=revenue_plot),
style={"width":'50%', "margin": 0, 'display': 'inline-block'},className="six columns"),
html.Div(
dcc.Graph(id='param-plot2',figure=Hash_plot),
style={"width":'50%', "margin": 0, 'display': 'inline-block'},className="six columns")],className="row")
])
#app.callback(
[Output("db-table", "data"), Output("param-plot", "figure"), Output("param-plot2", "figure")],
[Input("parameter-filter", "value"),Input("data-selector", "value"),Input('db-table', 'data')])#,prevent_initial_call=True)
def update_charts(parameter,data_type,table_data):
changed_inputs = [x["prop_id"]for x in callback_context.triggered]
if changed_inputs[0] == 'parameter-filter.value':
df = pd.DataFrame.from_dict(table_data)
ua_plot = px.scatter(df, x="Date", y=data_type)
aa_plot = px.scatter(df, x="Date", y=parameter)
return table_data, ua_plot, aa_plot
elif changed_inputs[0] == 'db-table.data':
df = pd.DataFrame.from_dict(table_data)
ua_plot = px.scatter(df, x="Date", y=data_type)
aa_plot = px.scatter(df, x="Date", y=parameter)
return no_update, ua_plot, aa_plot
else:
return no_update,no_update,no_update
#app.callback(Output("db-table", "data"),Input("Close_Btn", "n_clicks"),prevent_initial_call=True)
def close_browser(n_clicks):
print('In close callback\n')
if n_clicks>0:
self.shutdown()
return no_update
host='127.0.0.1'
port='8050'
url = 'http://{}:{}/'.format(host,port)
webbrowser.get(chrome_path).open(url)
app.run_server(debug=False)
def shutdown(self):
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()
Parameter_Viewer(df)```
It's easier to consider base cases of what your callbacks may look like and then extend upon them. From my experience, the main reason I typically structure a callback is to handle a particular output caused by a particular input.
E.g.
If output O1 is an effect of input I1, then I make a callback C1(I1 → O1).
If output O1 is an effect of Inputs I1 and I2, then I make a callback C1([I1, I2] → O1)
If you happen to have multiple outputs that are effects of an input, then you combine those outputs in the same callback.
E.g.
If output O1 is an effect of input I1 and output O2 is an effect of input I1, then I make a callback C1(I1 → [O1, O2])
Also, keep in mind that Dash doesn't allow multiple callbacks to have the same output component (e.g., a particular component property can be associated with one and only one callback). In which case, if you have a particular output that requires to be updated simultaneously with another, but is not necessarily an effect of that same input, then you should still combine the outputs into the single callback.
E.g.
If output O1 is an effect of input I1 and output O2 must be updated with O1, then I make a callback C1(I1 → [O1, O2])
I'm not sure why in the comment you tried using State() instead of Input(), as State() is used in the event a property must still be read as input, but doesn't necessarily have to be triggered.
So using the above logic, e.g.,
if output O1 that is an effect of input I1 and is not an effect of Input (which we now call State) S1, but still requires S1, then I make a callback C1([I1, S1] → [O1])
It's normal to have large callbacks that have many inputs and outputs as long as the callback is not trying to do too many disjointed things at once (otherwise when you go back to look at your code you'll spend a lot of time trying to remember what you did believe me :)).
If you're a beginner like me. You're probably looking for the most valuable information, in my case, in Daniel Al Mouiee's answer:
Dash doesn't allow multiple callbacks to have the same output component
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 ran the below code and I got the error:
TypeError: Unexpected keyword argument `n_intervals`
Allowed arguments: disabled, fireEvent, id, interval, setProps
When I ran it. I have all the particular libraries installed on my python 2.7.
Please find the code which I am running:
app.layout = html.Div
[
dcc.Tabs(
tabs=[
{'label': seclist.keys()[i],'value': i} for i in range(0, 5)
],
value=0,
id='tabs'
),
html.Div([
html.H4('TERRA Satellite Live Feed'),
html.Div(id='live-update-text'),
dcc.Graph(id='live-update-graph'),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)
]),
html.Div(id='tab-output')
],
style={
'width': '80%',
'fontFamily': 'Sans-Serif',
'margin-left': 'auto',
'margin-right': 'auto'
})
print 'HI'
#app.callback(Output('tabs','value'), [Input('interval-component','n_intervals')])
def updateData(n):
print 'yo man'
extractData(df,axis_points)
Another query I have is, I see that most of the real time examples use a single graph or subplots, but is it possible to have different tabs where each tab is updated every 100 milliseconds?
I suspect this is a version change within Dash, but I (like you) am stuck on the older version.
The fix with the older version (found here):
#*In Layout*
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
)
#*Callback*
#app.callback(
output=Output(component_id='tabs', component_property='value'),
events=[Event('interval-component', 'interval')]
)
def updateData():