Modify Dash Plotly dropdown menu to manipulate multiple plots - python

Ok so I'm trying to make simple webapp with dash plotly. I have a couple of plots on my dashboard that I need to be modified by the same dropdown. One plot is a lineplot that shows how the data fluctuates in a 5 year period and the other is an animated barchart that shows the selected country's move for certain year.
The data is in shape:
Country name
Score
year
Finland
12
2015
Finland
11
2016
Denmark
3
2015
And so on for 150 countries and a period from 2015-2020
Here is my code:
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/":
return [
html.P("This is the content of the home page!"),
#This is the code for the plots
dcc.Graph(id='scattergraph', figure=fig),
html.Div([
html.Div([
dcc.Graph(id='linechart'),
],className='six columns'),
html.Div([
dcc.Graph(id='barchart'),
],className='six columns'),
],className='row'),
html.Div([
dcc.Dropdown(id='dropdown_id', options=name_options,multi=True)],style={'width':'40%'})
]
elif pathname == "/page-1":
return html.P("This is the content of page 1. Yay!")
elif pathname == "/page-2":
return html.P("Oh cool, this is page 2!")
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
And below is the function to update the output, along with a screenshot of the result
#app.callback(
[Output('piechart', 'figure'),
Output('barchart', 'figure')],
[Input('dropdown_id', 'value')]
)
#This updates the charts on the bottom of the home page
def update_data(chosen_country):
# if len(chosen_country)==0:
# df_filterd = dff[dff['Country name'].isin(['Netherlands','Iran','Spain','Italy'])]
# else:
# print(chosen_country)
# df_filterd = dff[dff.index.isin(chosen_country)]
dff=df.copy()
df_filterd = dff[dff['Country name']== chosen_country]
bar_chart=px.bar(data_frame=df_filterd, x="Country name", y="Score", color="Country name",animation_frame="year")
list_chosen_countries=df_filterd['Country name'].tolist()
df_line = dfcon[dfcon['Country name'].isin(list_chosen_countries)]
line_chart = px.line(
data_frame=df_line,
x='year',
y='Score',
color='Country name',
labels={'Country name':'Countries', 'year':'year'},
)
line_chart.update_layout(uirevision='foo')
return (bar_chart,line_chart)

Some questions I can't really see in your code:
Where is the DataFrame dfcon defined?
Where is the variable name_options defined? Have you assigned the labels and values for the drop down menu? You could do something like:
dcc.Dropdown(id='dropdown_id',
options={'label': name, 'value': name} for name in name_options,
multi=True)
Then the value of the dropdown will come into the callback! Be careful with your multi being True, if the user is allowed to pick more than one country then it could have issues when you are filtering the information, so you would need a new for loop.

Related

Dash dropdown wont reset values once x clicked

I created 2 Dash dropdowns where one dropdown (the lower) is based on the selection in the first dropdown (the upper)
The selection and everything work fine.
However, when I click the X button to remove all the options from the area-dropdown, it does remove all the options but still, city-dropdown values are the same as when I clicked the X button.
why won't it reset?
This is the code I'm using:
#app.callback(
Output("city-dropdown", "options"),
Input("area-dropdown", "value"),
)
def update_city_dropdown(areas):
if areas is None or None in areas or areas == []:
return []
_area_codes = area_codes['area'][area_codes['name'].isin(areas)]
cities = city_codes['name'][city_codes['area'].isin(_area_codes)]
return [{'label': city, 'value': city} for city in cities]
where:
area_dropdown = dcc.Dropdown(
options=area_codes['name'],
placeholder="Select an area",
multi=True,
style=DROPDOWN_STYLE,
id='area-dropdown'
)
city_dropdown = dcc.Dropdown(
placeholder="Select a city",
options=[],
multi=True,
style=DROPDOWN_STYLE,
id='city-dropdown'
)
Thank you
The city_dropdown.value isn't updated, so upon re-selection of an area, the cities show-up again (since those values are now again in the refreshed options).
You can add the following callback:
#app.callback(
Output("city-dropdown", "value"),
Input("area-dropdown", "value")
)
def clear_city_dropdown(value):
return []
Note that the above will reset your cities when switching areas.

Passing a value from a page to another in dash plotly

I have two pages in dash plotly, and an index file who run the pages :
Index.py
Page 1
Page 2
I have a variable in page 1 which I want to pass it in page 2 and work with it. ( example of code)
Is it possible in Dash plotly ?
I saw somewhere that I can use Dcc.store, but how can I stock this variable in it ?
First_page = html.Div([
html.Div([dcc.Input(id='date_start', type='text', value='2021')]),
...
...
])
#app.callback(
Output('someoutput', 'children'),
[Input('someinput', 'value')])
#Uploading a certain table
#app.callback(
Output('datatable', 'data'),
Input('someinput', 'value'))
def function(somedata):
#Get the VARIABLE
...
...
variable= 12 (result of the operation in funtion)
How can I get the VARIABLE value (12) and inject it in page 2 please ?
Second_page = html.Div([
html.Div([dcc.Input(id='', type='text', value='')]),
...
...
])
#app.callback ()
def function_page2(somedata_page2):
#Work with the VARIABLE in first page
...
Thank you .

Persistence problem for dependent dropdowns in Plotly Dash

I have two dependent dropdowns that I want to persist in user session. I noticed that the persistence doesn't work for the second dropdown. It get reset with no possible value.
Here is a code sample :
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
app = Dash(
prevent_initial_callbacks=True,
suppress_callback_exceptions=True,
)
#app.callback(
Output("dd-client-code", "options"),
Input("dd-clients-seg-1", "value")
)
def dd_client_code(client_seg_1):
#any function would do for generate_options
return generate_options(selected_segment=client_seg_1)
dd1 = dcc.Dropdown(
id="dd-clients-seg-1",
options=["record_1", "record_2", "record_3"],
persistence="true",
persistence_type="session",
)
dd2 = dcc.Dropdown(
id="dd-client-code",
persistence="true",
persistence_type="session",
)
app.layout = html.Div(children=[dd1, dd2])
app.run_server(debug=True)
Can anyone help me ?
The persistent values for both dropdowns are stored as expected...
but the value for dd2 is updated to null when the page is reloaded because dd2 has no options at that time.
The callback to update dd2 is not called when the page is reloaded. Even if the callback was called it would be too late because of the first issue.
The following modified code uses dcc.Store to store the value of dd1 each time it is changed. dcc.Interval is used to ensure that the callback is called after the page reload. dd2 is turned into a function that takes the value of dd1 and is called by a new callback that triggers on the interval to update the layout.
import dash.exceptions
from dash import Dash, dcc, html
from dash.dependencies import Input, Output, State
app = Dash(
prevent_initial_callbacks=True,
suppress_callback_exceptions=True,
)
def generate_options(selected_segment):
if selected_segment == "record_1":
return ["A", "B", "C"]
elif selected_segment == "record_2":
return ["One", "Two", "Three"]
else:
return ["Small", "Medium", "Large"]
dd1 = dcc.Dropdown(
id="dd-clients-seg-1",
options=["record_1", "record_2", "record_3"],
persistence="true",
persistence_type="session",
)
def dd2(dd1_value):
"""Return a dd2 dropdown with the appropriate options based on dd1 value"""
options = [] if not dd1_value else generate_options(dd1_value)
return dcc.Dropdown(
id="dd-client-code",
options=options,
persistence="true",
persistence_type="session",
)
#app.callback(
Output("dd-client-code", "options"),
# Store the value of dd1 dropdown when it changes
Output("dd-clients-seg-1-value", "data"),
Input("dd-clients-seg-1", "value")
)
def dd_client_code(client_seg_1):
if not client_seg_1:
raise dash.exceptions.PreventUpdate
return generate_options(client_seg_1), client_seg_1
#app.callback(
Output("dd2-div", "children"),
Input("interval-timer", "n_intervals"),
State("dd-clients-seg-1-value", "data"),
)
def dd2_div_handler(unused, dd1_value):
"""Update the dd2 menu when triggered by dcc.Interval"""
return dd2(dd1_value)
app.layout = html.Div([
# store the latest value of dd-clients-seg-1 dropdown
dcc.Store("dd-clients-seg-1-value", storage_type="session"),
# fires 1ms after page load
dcc.Interval(id="interval-timer", interval=1, max_intervals=1),
# static menu
dd1,
# dynamic menu: don't put dd2 here to avoid persistent value going null
html.Div(id="dd2-div")
])
app.run_server(debug=True)
This was tested with Dash 2.4.1

Plotly: Dash button with live graph not working

i developed python dash based application for monitoring. as a part of the project i want to display live graph and change the live graph value based on user input. i am stuck in this part. the live graph getting update when i start typing in input box (eg:temp) the graph keep on updating for each letter like t,te,tem,temp. so i created a button to submit the input value. still the graph updating for each letter.
the code for the same:
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Input(id='input-value', value='example', type='text'),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
]
)
call back function is like below
#app.callback(Output('live-graph', 'figure'),
[Input('submit-button','n_clicks')],
state=[State(component_id='sentiment_term', component_property='value')],
events=[Event('graph-update', 'interval')])
def update_graph_scatter(n_clicks, input_value):
conn = sqlite3.connect('database.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM table WHERE colume LIKE ? ORDER BY unix DESC LIMIT 1000", conn ,params=('%' + input_value+ '%',))
df.sort_values('unix', inplace=True)
df['date'] = pd.to_datetime(df['unix'],unit='ms')
df.set_index('date', inplace=True)
X = df.index
Y = df.column
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range= [min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)])}
Note: If i removed the interval the button start working.but i want to be live update as well as button
You could use an intermediate component like a div to save the input from the text field and update that div only on button click.
So you would have
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(id='input-value', value='example', type='text'),
html.Div(['example'], id='input-div', style={'display': 'none'}),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
])
Now you update the div only on button click:
#app.callback(Output('input-div', 'children'),
[Input('submit-button', 'n_clicks')],
state=[State(component_id='input-value', component_property='value')])
def update_div(n_clicks, input_value):
return input_value
And the Graph always uses the the div content to query your database (either when the interval triggers or the div changes):
#app.callback(Output('live-graph', 'figure'),
[Input('graph-update', 'interval'),
Input('input-div', 'children')])
def update_graph_scatter(n, input_value):
...
Are you sure the updates on each letter are because of the input? Sounds like the interval updates the graph with the not finished input you are typing.
P.S.: I guess you have a name discrepancy between you Input and State id.

passing value from 1 page to another in dash plotly

I am trying to pass input value to another with Dash Plot.
The main page has a list of strategies :
First_page = html.Div([
html.Div([dcc.Dropdown(id='strategy', options=[{'label': i, 'value': i} for i in [strat1, start2]], multi=True)], value = [strat1]),
html.Div([dcc.Input(id='date_start', type='text', value='1990-01-01')]),
html.Button('Start Calculation', id='button'),
])
And my pages 1 and 2 are the following :
for i in range(2) :
layout = html.Div([
html.Div([dcc.Dropdown(id='strategy'+str(i), options=[{'label': i, 'value': i} for i in [strat1, start2]]])], value = strat+str(i)),
html.Div([dcc.Input(id='date_start'+str(i), type='text', value='1990-01-01')]),
html.Button('Start Calculation', id='button'+str(i)),
+ other options not listed in main page
])
The goal is the following : when the user is on the main page he can choose the list of strategies he want to see, as well as the starting date.
Then when he press the button, it should open pages for each strategy selected (In the example we have 2 strategies so I should have 2 pages : localhost:port/page-strat1 and localhost:port/page-strat2) where the graph shown on each page starts with the selected date.
I succeed to open the pages localhost:port/strat1 and/or the page localhost:port/strat2 depending on the list of selected strategies.
list_event = [Event('button', 'click')]
list_state = [State('strategy', 'value')]
#app.callback(Output('hidden-div', 'children'),events=list_event, state=list_state)
def create_callback_share(datestart, list_strategies_used):
for _strat in list_strategies_used:
time.sleep(1)
webbrowser.open('http://localhost:port/page-'+_strat)
But I do not succeed to send the selected date. I try the following callback :
for i in range(2) :
#app.callback(Output('date_start'+str(i), 'value'),inputs=[Input('date_start', 'value')])
def set_display_children(selected_element):
return "{}".format(selected_element)
But the page localhost:port/strat1 and localhost:port/strat2 allow open with the default value which is '1990-01-01' even if I enter another value in the main page.
Is it possible to send value from one page to the other ?
Use dcc.Store() as in the answer here:
Plotly Dash Share Callback Input in another page with dcc.Store
And here is the Dash documentation:
https://dash.plotly.com/dash-core-components/store

Categories

Resources