I am trying to create a Dash dashboard where dropdown options in one box are dependent on the previous dropdown selection.
The data consists of two dictionaries, with two keys each. Each key contains a dataframe with a couple of columns. The exact data:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
from pandas import Timestamp
import numpy as np
df_vals_prod={'corn':pd.DataFrame({'time': {1: Timestamp('2020-09-23 06:00:00'),
2: Timestamp('2020-09-23 12:00:00'),
3: Timestamp('2020-09-23 18:00:00'),
4: Timestamp('2020-09-24 00:00:00')},
'2m_temp_prod': {1: 0.020584322444347606,
2: 0.08973907730395358,
3: 2.3866310395722463,
4: 3.065472457668321},
'total_precip_prod': {1: 1.372708470272411,
2: 2.135683294556938,
3: 1.9811172016307312,
4: 2.1082116841869323}}),
'soybeans':pd.DataFrame({'time': {1: Timestamp('2020-09-23 06:00:00'),
2: Timestamp('2020-09-23 12:00:00'),
3: Timestamp('2020-09-23 18:00:00'),
4: Timestamp('2020-09-24 00:00:00')},
'2m_temp_prod': {1: 0.6989001827317545,
2: -0.8699121426411993,
3: -0.9484359259520706,
4: 0.7391299158393124},
'total_precip_prod': {1: -0.07639291299336869,
2: 0.19182892415959496,
3: 0.8719339093510236,
4: 0.90586956349059}})}
df_vals_area={'corn':pd.DataFrame({'time': {1: Timestamp('2020-09-23 06:00:00'),
2: Timestamp('2020-09-23 12:00:00'),
3: Timestamp('2020-09-23 18:00:00'),
4: Timestamp('2020-09-24 00:00:00')},
'2m_temp_area': {1: -1.6820417878457192,
2: -0.2856437053872421,
3: 0.3864022581278122,
4: 0.5873739667356371},
'total_precip_area': {1: 1.3703311242708185,
2: 0.25528434511264525,
3: 0.5007488191835624,
4: -0.16292114222272375}}),
'soybeans':pd.DataFrame({'time': {1: Timestamp('2020-09-23 06:00:00'),
2: Timestamp('2020-09-23 12:00:00'),
3: Timestamp('2020-09-23 18:00:00'),
4: Timestamp('2020-09-24 00:00:00')},
'2m_temp_area': {1: 1.3789989862086967,
2: -0.7797086923820608,
3: 1.0695635889750523,
4: 1.136561500804678},
'total_precip_area': {1: -0.6035111830104833,
2: -0.18237330469451313,
3: -0.7820158376898607,
4: -0.6117188028872137}})}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
weight_opts=['Production','Area']
controls = dbc.Card(
[ dbc.FormGroup(
[
dbc.Label("Crop"),
dcc.Dropdown(
id="Crop",
options=[
{"label": col, "value": col} for col in list(df_vals_prod.keys())
],
value=list(df_vals_prod.keys())[0],
clearable=False,
),
]
),
dbc.FormGroup(
[
dbc.Label("Weighting"),
dcc.Dropdown(
id="weights",
options=[
{"label": i, "value": i} for i in weight_opts
],
value=weight_opts[0],
clearable=False,
),
]
),
dbc.FormGroup(
[
dbc.Label("Forecast Variable"),
dcc.Dropdown(
id="forecast_v",
),
]
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
dbc.Row([
dbc.Col([
html.Br(),
dbc.Row([
dbc.Col([html.Div(id = 'txt1')
])
]),
html.Br(),
dbc.Row([
dbc.Col([html.Div(id = 'txt2')])
])
])
])
],xs = 2)
,
dbc.Col([
dbc.Row([
dbc.Col([html.Div(id = 'plot_title')],)
]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
#dbc.Col(dcc.Graph(id="cluster-graph"))
])
])
],),
],
fluid=True,
)
@app.callback(
Output('forecast_v','options'),
[Input('weights', 'value')]
)
def update_var_dropdown(weight):
if weight=='Production':
return [{'label': i, 'value': i} for i in df_vals_prod['corn'].columns[1:]]
elif weight=='Area':
return [{'label': i, 'value': i} for i in df_vals_area['corn'].columns[1:]]
@app.callback(
Output("crop-graph", "figure"),
[ Input("Crop", "value"),
Input("weights", "value"),
Input("forecast_v", "value"),
],
)
def crop_graph(Crop, val, weight):
# plotly figure setup
fig = make_subplots(specs=[[{"secondary_y": True}]])
if weight:
fig.add_trace(go.Scatter(name=val, x=df_vals_prod[Crop]['time'], y=((df_vals_prod[Crop][val]-273)*(9/5))+32, mode = 'lines', line=dict(color='red', width=4),
hovertemplate='Date: %{x|%d %b %H%M} UTC<br>Temp: %{y:.2f} F<extra></extra>'), secondary_y=False,
)
fig.update_yaxes(title_text="<b>Temp (F)<b>", color='red', secondary_y=False,)
fig.update_yaxes(title_text="<b>24hr Forecast Change (F)</b>", secondary_y=True)
return(fig)
app.run_server(mode='external', port = 8099)
As you can see, this 6 hourly data and is meant to be plotted as a time series. Now I want to add a couple of dropdowns. The first dropdown (Crop) selects which crop to choose (corn or soybeans), which are the two keys from each dictionary.
The second dropdown (Weighting) now selects which dataframe we want to use. What the user selects in this second dropdown will determine the options to select in the third dropdown.
The third dropdown will select the actual variable (Forecast Variable), which is one of two columns available in each dataframe. So if in dropdown 2, "Production" is selected, the options for dropdown 3 would consist of '2m_temp_prod' or 'total_precip_prod'. For "Area" in dropdown 2, dropdown 3 options would be '2m_temp_area' or 'total_precip_area'.
Here is the code I have so far. I am able to setup the callback properly for the dropdowns, but I don't think my second callback is working properly. I understand how to create the dynamic dropdown, but I am not sure how to translate that to actually plotting the data.
That produces this plot. Notice that the dropdowns are what I want, but it doesn't plot. How do I add in the 'weight' to make the desired plot? What I would expect is just a simple line graph where the data depends on all of the dropdowns chosen.
Edit: as suggested by vestland, I am including a much smaller data sample. The specific values in this case don't matter, just the structure of the data. See above for more concise data.
I haven't been able to figure out why your code fails. But I've been putting together an example that I think will come close to what you're looking for here. It builds on an example from the plotly docs, and so has a bit different layout from what you've got in your question. The main take-away is that three sets of radio buttons will let you:
['prod', 'area']
,['2m_temp_prod', 'total_precip_prod']
or['2m_temp_area', 'total_precip_area']
.['corn', 'soybeans']
It's very possible that I've misunderstood the logic of what you want to achieve here. But just give me some feedback along the way and we can work out the details.
Dash app for selection
DF: prod | Crops: corn | Column: 2m_temp_prod
Dash app for selection
DF: area | Crops: soybeans | Column: total_precip_area
Complete code:
Edit 1 - Dropdown menus.
All you have to do to get the desired dropdown buttons is to change each
to
Now you'll get:
Complete code: