input text in plotly dash based on a RadioItem picker - python

I have a code where i use plotly dash radio item to pick from different tests ('weekness', 'std', etc.) The main code is shown below.
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([ # this Div contains our output graph
dcc.Graph(id='side-plot', )], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
])
Now, i want to display an explanation to the tests whenever one is picked using the radio item button. I am using the following #app.callback code: (note i am just displaying one text for the 'weakness' test)
#app.callback(
Output('est-explain', 'children'), [Input("test-picker", "value")])
def update_chart_info(picker_test):
if picker_test == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
return [text]
however, i am not getting anything on the dashboard when i run the code. I mean, the code works for other callbacks (that are not shown here) however it is not working for the callback for displaying the text. I tried different dcc components, such as html and markdown, but still can't see the explanation displayed.
any suggestions?
Thanks

There's a few issues that are probably preventing your callback from working as intended:
you have a typo in your callback arguments and Output('est-explain', 'children') should be Output('test-explain', 'children')
you mentioned you want the Dash app to update based on the selection from the Radioitem component, but Input("test-picker", "value") refers to the id for your Dropdown component, so you probably want to change it to Input("plot-picker", "value")
you'll also need a condition in your callback for when the Radioitem selection isn't "weakness" – otherwise you'll get something like UnboundLocalError: local variable 'text' referenced before assignment whenever someone selects another radioitem because update_chart_info won't know what text is supposed to be (for simplicity, I am returning an empty string whenever anything option besides "weakness" is selected, but you can modify this as needed)
The following should work:
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
And here is a small working example:
import plotly.graph_objects as go
from dash import Dash, dcc, html, Input, Output
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_mean', 'p_mean_average', 'p_median', 'p_median_average','range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
## sample names
names = ['weakness','other']
app = Dash(__name__)
app.layout = html.Div(
[
html.H4('Dieharder Tests Plots'),
dcc.Graph(id="bar-plot", style={'width':'80%', 'height': '70vh','display':'inline-block'}),
html.Div([
# this Div contains our output graph
dcc.Graph(id='side-plot', )
], style={'width':'20%', 'height':'70vh','display':'inline-block'}),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
]
)
#app.callback(
Output('test-explain', 'children'),
Input('plot-picker', 'value')
)
def update_chart_info(radio_selection):
if radio_selection == 'weakness':
text = "This test counts how many weak results are there for each test for all rounds and return the number divided by the number of rounds"
else:
text = ""
return [text]
app.run_server(debug=True)

Related

only list-like objects are allowed to be passed to isin(), you passed a [str]

I am trying to create dashboard by using the plotly python. Require to create dropdown for date selection for the pie chart. All the data are come from a .csv file.
Expectation: The data displayed in the pie chart are based on the date selected.
Data:
enter image description here
Code:
date_category = list(df['Date'].unique())
app.layout = ...,
dcc.Dropdown(id='date_drdn', multi=False, value= ['02/01/2022'],
options = [{'label':x, 'value':x}
for x in date_category]
),
dcc.Graph(id='pie-fig', figure={})
#app.callback(
Output('pie-fig', 'figure'),
Input('date_drdn', 'value'))
def update_graph(selection):
dff = df[df['Date'].isin(selection)]
fig = px.pie(dff, values='Transactions', names='Product', color_discrete_sequence=px.colors.sequential.RdBu)
fig.update_traces(textinfo= "label+value+percent").update_layout(title_x=0.5)
return fig
However, it keep on showing the error message when select the date.
Error message:"only list-like objects are allowed to be passed to isin(), you passed a [str]"
And the data is not display based on the date selected.
Does anyone know why and how to solve it?
If the return value of the dropdown allows multiple selections, it will be in list format and isin effect. Since the expected pie chart is a single selection of date and time, the list format is not needed as an initial value. At the same time, the return value of the callback will be a single date and time data for conditional extraction.
date_category = list(df['Date'].unique())
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
#from jupyter_dash import JupyterDash
app = Dash(__name__)
#app = JupyterDash(__name__)
app.layout = html.Div([
html.H3('Daily Graph'),
dcc.Dropdown(id='date_drdn',
multi=False,
value= '02/01/2022',
options = [{'label':x, 'value':x} for x in date_category]
),
dcc.Graph(id='pie-fig', figure={})
])
#app.callback(
Output('pie-fig', 'figure'),
Input('date_drdn', 'value'))
def update_graph(selection):
# if selection:
dff = df[df['Date'] == selection]
#print(dff)
fig = px.pie(dff, values='Transactions', names='Product', color_discrete_sequence=px.colors.sequential.RdBu)
fig.update_traces(textinfo="label+value+percent").update_layout(title_x=0.5)
return fig
if __name__ == '__main__':
app.run_server(debug=True)#, mode='inline'

Callback with navbar plotly dash python

I have this code below. I’ve created 3 navigation links: Inputs, Calculations and About.
Now on the Inputs sheet, I would like to add several dropdowns and input fields for the user. How can I include those input fields and give them ids as I want to use them for calculations later (i.e I create a dropdown and I give it an id of “my-dropdown” to later use for the calculations I want to make in Calculations).
So far this is what I have in the callbacks but if I want to continue my function with an elif pathname == “/inputs” and then I start including my input fields, I will not be able to store their ids to call them later once I want to do the calculations. Can you please advise?
navbar = dbc.NavbarSimple(
children=[
dbc.NavItem(dbc.NavLink("INPUTS", href="/inputs", external_link=True, active="exact")),
dbc.NavItem(dbc.NavLink("CALCULATIONS", href="/calculations", external_link=True, active="exact")),
dbc.NavItem(dbc.NavLink("ABOUT", href="/about", external_link=True, active="exact")),
dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("Comments", href="/comments", external_link=True),
dbc.DropdownMenuItem("Version Updates", href="/updates", external_link=True),
],
nav=True,
in_navbar=True,
label="More",
),
],
brand="Main Page Title",
color="#015151",
dark=True
)
content = html.Div(id="page-content", children=[])
app.layout = html.Div([
dcc.Location(id="url"),
navbar,
content
])
# ---------------------------------------------------------------------------
## APP CALLBACKS ##
# Add info in about section
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/about":
return [
html.Br(),
html.Br(),
html.P('About', style={'fontWeight': 'bold', 'font-size': 32, 'margin-left': '20px'}),
I suggest you have some global hidden temporary divs or use the dcc.Store component that can hold the inputs that are inputted on the inputs sheet, then you'll just have to refer to these hidden divs that hold these inputs in the calculations sheet, rather than referring to the inputs sheet components.

Use a button to set values in drop down using dash

I was wondering is it possible to set multiple values to a drop down value using a button. What I want to be able to do is filter the drop down and then if the user presses the submit button, all options that contain the search_value are added as the drop down value. For example in my code, if you type in 'a' into the drop down search bar then 'Montreal' and 'San Francisco' appear. If the user were to then press the submit button both these options should be added as values to the drop down at once. At the moment I am able to save the most recent search value to user put in but I am having trouble dynamically altering the drop down value. Any help would be greatly appreciated!! Here is my code: FYI: I am running this in a Colab Notebook
**Correct answer
from jupyter_dash import JupyterDash # pip install dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Output, Input, State
import pandas as pd # pip install pandas
import plotly.express as px
import dash_table
import math
from dash import no_update
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
options = [
{"label": "New York City", "value": "NYC"},
{"label": "Montreal", "value": "MTL"},
{"label": "San Francisco", "value": "SF"},
]
app.layout = html.Div([
html.Div(id='output_data'),
html.Div(id="hidden-search-value", style={"visibility":"hidden"}),
html.Div(id="value-test", style={'color':'white'}),
html.Div(html.Button('Submit', id = 'button', n_clicks=0), style={'width':'49%', 'display':'inline-block'}),
html.Div(
dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True, clearable = True, options = options, style={'width':'49%', 'display':'inline-block'})),
])
#app.callback(
[Output("hidden-search-value", "children"),
Output("value-test", "children")],
Input(component_id="my-multi-dynamic-dropdown", component_property="search_value"),
)
# save previous search value in hidden variable
def update_hidden_value(value):
if value == "":
raise PreventUpdate
return value, "Select all values containing '{}'".format(value)
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
Input(component_id="button", component_property="n_clicks"),
[
State(component_id="output_data", component_property="children"),
State(component_id="hidden-search-value", component_property="children")
]
)
# submit button logic: use saved search value to update drop_down value
def update_multi_options(n_clicks, children, search_value):
# submit button pressed
if int(n_clicks) > 0:
values = []
for o in options:
# all values containing search value
if search_value.lower() in o["label"].lower():
values.append(o["value"])
else:
continue
return values
else:
return no_update
app.run_server(mode='inline', port=8030)
I believe you have the wrong ordering of your input arguments to your callback update_multi_options. It should be:
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
Input(component_id="button", component_property="n_clicks"),
State(component_id="output_data", component_property="children")
)
def update_multi_options(n_clicks, children):
Now, regarding the dynamic updating using the search feature, it turns out that whenever you click out of the search bar, the search_value is cleared. So I suggest perhaps tracking the search_value in a temporary hidden variable, if this is a low risk option for your application. An approach to do this would be the following:
app.layout = html.Div([
html.Div(id='output_data'),
html.Div(html.Button('Submit', id = 'button', n_clicks=0)),
html.Div(id="hidden-search-value", style={"visibility":"hidden"}),
html.Div([
"Multi dynamic Dropdown",
dcc.Dropdown(id="my-multi-dynamic-dropdown", multi=True, clearable = True, options = options),
]),
])
.
.
.
#app.callback(
Output("my-multi-dynamic-dropdown", "value"),
[
Input(component_id="button", component_property="n_clicks"),
],
[
State(component_id="output_data", component_property="children"),
State(component_id="hidden-search-value", component_property="children")
]
)
def update_multi_options(n_clicks, children, search_value):
# Make sure that the set values are in the option list, else they will disappear
# from the shown select list, but still part of the `value`.
if int(n_clicks) > 0:
values = []
for o in options:
if search_value in o["label"]:
values.append(o)
else:
continue
return values
else:
return no_update
#app.callback(
Output("hidden-search-value", "children"),
Input(component_id="my-multi-dynamic-dropdown", component_property="search_value"),
)
def update_hidden_value(value):
print(value, type(value))
if value == "":
raise PreventUpdate
return value
The only limitation with this approach would be that when you type something into the search and click submit, then you manually erase the search value and re-click on submit, it'll be stuck on the last typed in value. But, I don't see this being an issue, since you can overwrite this with another callback if this is undesirable.
In summary, you'll need to track the value you are typing in (eg. dcc.Dropdown.search_value) then trigger the filtering.

How to assign multiple inputs and outputs to app.callback with hover_feature or click_feature in dash-leaflet?

I'm having trouble getting multiple inputs and outputs to work in Dash-leaflet. Essentially, I want to do the following: on a mouse click place a marker on the map, and when hovering over a country highlight an area of the map with a polygon and display some information about the country being hovered over in a different pane. I implemented each of those functions in a separate app.callback, as in the below code.
app = dash.Dash(__name__)
app.layout = html.Div(children=[
dl.Map(children=[dl.TileLayer(),
dl.LayerGroup(id="layer"),
dl.GeoJSON(url='https://pkgstore.datahub.io/core/geo-countries/countries/archive/23f420f929e0e09c39d916b8aaa166fb/countries.geojson', id="countries",
options=dict(style=dict(opacity=0,fillOpacity=0)),
hoverStyle=arrow_function(dict(weight=2, color='red', dashArray='', opacity=1)))
],
style={'width': '1000px', 'height': '500px'}, zoom=3, id='map'),
html.Div(id='country'),
html.Div(id='Country info pane')
])
#app.callback(Output("layer", "children"), [Input("countries", "click_feature")])
def map_click(feature):
return [dl.Marker(position=[np.random.randint(-90,90),np.random.randint(-185,185)])]
#app.callback(Output('Country info pane', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
country_id = feature['properties']['ISO_A3']
return country_id
#app.callback(Output('layer', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
return(dl.Polygon(positions=[[30,40],[50,60]], color='#ff002d', opacity=0.2))
if __name__ == '__main__':
app.run_server(debug=False)
While each of those callbacks works in isolation (i.e. if I comment out the other callbacks), none of them work if I run the code with two or more of them active.
If I combine the two hover_feature functions like the below, they seem to work together.
#app.callback(Output('Country info pane', 'children'),
Output('layer', 'children'),
Input('countries', 'hover_feature'))
def country_hover(feature):
if feature is not None:
country_id = feature['properties']['ISO_A3']
return country_id, dl.Polygon(positions=[[30,40],[50,60]], color='#ff002d', opacity=0.2)
However, the "map_click" function still does not work along with the "country_hover" function. How can I get all three callbacks to work?
I found out how to do this, I had to add another dl.LayerGroup to the dl.Map object with a different id, then assigned each function to output to a different layer.

Periodically updating figures in plotly

Below is a simple script which retrieves live population data. It updates periodically and updates the plotly figure:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
# Retrieve data
link = requests.get("https://countrymeters.info/en/World").text
table = pd.read_html(link)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
# Dash app
app = dash.Dash(external_stylesheets=[dbc.themes.LUX])
# Bootstrap CSS
app.css.append_css({"external_url": "https://codepen.io/amyoshino/pen/jzXypZ.css"})
link = session.get("https://countrymeters.info/en/World").text
app.layout = html.Div(children=[
html.H1("Population Data Scrape"),
html.Div(children=
'''
A summary of the latest population data across the globe.
''' ),
# Graph 1
html.Div([
dcc.Tabs([ #Tab 1
dcc.Tab(label="Population Data", children=[
html.Div([
dcc.Graph(
id = "Data",
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
),
dcc.Interval(
id="4secinterval",
interval="4000",
n_intervals=0
)], className = "six columns"),
]),
])
])
])
# Update graph
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
def draw_figure(n):
test = session.get("https://countrymeters.info/en/World").text
table = pd.read_html(test)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
return figure
if __name__ == "__main__":
app.run_server(debug=False)
In the "update graph" section of my code, for the graph to update I have to call the web scrape again to retrieve the latest data and define it in a whole function. I've tried defining the function before and using:
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
draw_figure(n)
which I was hoping would just return the figure, however, this doesn't work. Is there a way in plotly/Dash to update the figure in a shorter way (i.e without having to scrape and format the data all over again)?
The catch here is in the dcc.Graph section. You are calling a global variable table.iplot() where table is defined as a global variable in your retrieve section.
Try to put all the functions in a separate file say `useful_functions.py'
def draw_graph():
link = requests.get("https://countrymeters.info/en/World").text
table = pd.read_html(link)[0]
table = table
figure = table.iplot(asFigure=True, kind='bar',xTitle='Source: World',yTitle='Live Pop',title='Population')
return figure
the_graph = draw_graph()
Now, in your main file as above, remove the global declaration of table and figure. To display the graph, in your graph section call the draw_graph() function as:
import useful_functions as uf
<rest of the html app code>
dcc.Graph(
id = "graph_id",
figure = uf.the_graph
),
This will call the graph for the first time on load. Now for the refresh bit, the callback would look like:
#app.callback(Output("Data", "figure"),
[Input("4secinterval", "n_intervals")])
def draw_figure(n):
fig = uf.draw_graph()
return fig

Categories