我有一段冗长的代码,用于源自多个嵌套字典的多个链接回调。我有所有想要提供的必要下拉菜单和选项。但是,每当我在此示例中将“ crop”下拉菜单更改为原始选项(即玉米)以外的其他内容时,都会将下面的“ Weighting”下拉菜单重置。同样,如果我更改“加权”下拉菜单,则会将“预测变量”下拉菜单重置为原始选项。我该如何预防?链接回调的目的是使更改一个选项将更改绘制的数据,因为它们都已链接。
我认为这里的数据不重要?但是它的功能是这样的:
final_dict[init_date][model][weight][crop]
然后,上述确切的词典将输出一个数据帧。数据框中的列将成为“预测变量”,并将最终绘制出来。如果我确实需要添加数据,我可以尝试做,但是字典非常大。
这是我到目前为止的代码。请注意,该图是空的,因为我还没走那么远。
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
from pandas import Timestamp
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
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'crop2':my_dict[2]},
'weight2':{'crop1':my_dict[3], 'crop2':my_dict[4]}},
'model2':{'weight1':{'crop1':my_dict[5], 'crop2':my_dict[6]},
'weight2':{'crop1':my_dict[7], 'crop2':my_dict[8]}}},
'day2':{'model1':{'weight1':{'crop1':my_dict[9], 'crop2':my_dict[10]},
'weight2':{'crop1':my_dict[11], 'crop2':my_dict[12]}},
'model2':{'weight1':{'crop1':my_dict[13], 'crop2':my_dict[14]},
'weight2':{'crop1':my_dict[15], 'crop2':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
controls = dbc.Card(
[ dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options=[{'label': k, 'value': k} for k in final_dict.keys()],
value=list(final_dict.keys())[0],
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
clearable=False,
),
]
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row([
dbc.Col([html.Div(id = 'plot_title')],)
]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
@app.callback(
Output('model_dd', 'options'),
[Input('init_dd', 'value')])
def set_model_options(model):
return [{'label': i.replace('_',' '), 'value': i} for i in final_dict[model]]
@app.callback(
Output('model_dd', 'value'),
[Input('model_dd', 'options')])
def set_model_options_value(available_model):
return available_model[0]['value']
#set the weight
@app.callback(
Output('weight_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value')])
def set_weight_options(selected_init, selected_model):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model]]
@app.callback(
Output('weight_dd', 'value'),
[Input('weight_dd', 'options')])
def set_weight_value(available_weight):
return available_weight[0]['value']
#set the crop
@app.callback(
Output('crop_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value')])
def set_crop_options(selected_init, selected_model, selected_weight):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight]]
@app.callback(
Output('crop_dd', 'value'),
[Input('crop_dd', 'options')])
def set_crop_value(available_crop):
return available_crop[0]['value']
#set the variable
@app.callback(
Output('columns_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value')])
def set_column_options(selected_init, selected_model, selected_weight, selected_crop):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight][selected_crop].columns[1:]]
@app.callback(
Output('columns_dd', 'value'),
[Input('columns_dd', 'options')])
def set_column_value(available_column):
return available_column[1]['value']
app.run_server(mode='external', port = 8099)
编辑:在示例虚拟数据中添加。请注意,当更改选项的某些组合时,其他选项如何切换回原始值。希望防止这种情况发生。
答案 0 :(得分:2)
特定数据示例有所帮助。我看到了
关于更好地控制链接回调的问题,我认为这是在正确的位置使用Input()
和State()
的问题。
尝试一下(我已将您的final_dict
重命名,以便更轻松地监视正在发生的事情):
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
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State
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
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'cropA':my_dict[2]},
'weight2':{'crop2':my_dict[3], 'cropB':my_dict[4]}},
'model2':{'weight3':{'crop3':my_dict[5], 'cropC':my_dict[6]},
'weight4':{'crop4':my_dict[7], 'cropD':my_dict[8]}}},
'day2':{'model3':{'weight5':{'crop5':my_dict[9], 'cropE':my_dict[10]},
'weight6':{'crop6':my_dict[11], 'cropF':my_dict[12]}},
'model4':{'weight7':{'crop7':my_dict[13], 'cropG':my_dict[14]},
'weight8':{'crop8':my_dict[15], 'cropH':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
controls = dbc.Card(
[ dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options=[{'label': k, 'value': k} for k in final_dict.keys()],
value=list(final_dict.keys())[0],
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
clearable=False,
),
]
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row(html.Div(id='selected_data')),
# dbc.Row([
# dbc.Col([html.Div(id = 'plot_title')],)
# ]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
@app.callback(
Output('model_dd', 'options'),
[Input('init_dd', 'value')])
def set_model_options(model):
return [{'label': i.replace('_',' '), 'value': i} for i in final_dict.get(model).keys()]
@app.callback(
Output('model_dd', 'value'),
[Input('model_dd', 'options')])
def set_model_options_value(available_model):
return available_model[0]['value']
#set the weight
@app.callback(
Output('weight_dd', 'options'),
[Input('model_dd', 'value')],
[State('init_dd', 'value')])
def set_weight_options(selected_model, selected_init):
if selected_model is None: return None
print('selected_model(): ', selected_init, selected_model)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).keys()]
@app.callback(
Output('weight_dd', 'value'),
[Input('weight_dd', 'options')])
def set_weight_value(available_weight):
return available_weight[0]['value']
#set the crop
@app.callback(
Output('crop_dd', 'options'),
[Input('weight_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value')])
def set_crop_options(selected_weight, selected_init, selected_model):
if selected_model is None or selected_weight is None: return None
print('set_crop_options(): ',selected_init, selected_model, selected_weight)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).get(selected_weight).keys()]
@app.callback(
Output('crop_dd', 'value'),
[Input('crop_dd', 'options')])
def set_crop_value(available_crop):
return available_crop[0]['value']
#set the variable
@app.callback(
Output('columns_dd', 'options'),
[Input('crop_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value'),
State('weight_dd', 'value')])
def set_column_options(selected_crop, selected_init, selected_model, selected_weight):
if selected_crop is None or selected_weight is None or selected_model is None: return None
print('set_column_options(): ', selected_init, selected_model, selected_weight, selected_crop)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).get(selected_weight).get(selected_crop).columns[1:]]
@app.callback(
Output('columns_dd', 'value'),
[Input('columns_dd', 'options')])
def set_column_value(available_column):
if available_column is None: return None
return available_column[1]['value']
@app.callback(
Output('selected_data', 'children'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def show_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('show_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd][columns_dd].to_json(orient='split')
except:
return
return data
def make_plot(df, var):
fig = go.Figure(
data=[go.Scatter(x=df['time'], y=df[var], name=var)],
layout={
'yaxis': {'title': f'Plot of <b>{var}</b>'}
}
)
return fig
no_data_fig = {"layout": {
"xaxis": { "visible": False},
"yaxis": {"visible": False},
"annotations": [
{ "text": "",
"xref": "paper",
"yref": "paper",
"showarrow": False,
"font": {"size": 20 }
}]
}
}
@app.callback(
Output('crop-graph', 'figure'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def plot_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('plot_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd]
data_col = data[columns_dd]
except:
return no_data_fig
return make_plot(data, columns_dd)
app.run_server(mode='external', port = 8098, debug=True)
答案 1 :(得分:0)
这是另一个版本。
column_dd
固定而不被更新(可能假设最终数据集中不同版本的数据列相同)。因此,我注释掉了用于更新column_dd
的回调。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
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State
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
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'cropA':my_dict[2]},
'weight2':{'crop2':my_dict[3], 'cropB':my_dict[4]}},
'model2':{'weight3':{'crop3':my_dict[5], 'cropC':my_dict[6]},
'weight4':{'crop4':my_dict[7], 'cropD':my_dict[8]}}},
'day2':{'model3':{'weight5':{'crop5':my_dict[9], 'cropE':my_dict[10]},
'weight6':{'crop6':my_dict[11], 'cropF':my_dict[12]}},
'model4':{'weight7':{'crop7':my_dict[13], 'cropG':my_dict[14]},
'weight8':{'crop8':my_dict[15], 'cropH':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# Helpers #####################################################################
def get_dict_keys(varlist):
dic = final_dict
for var in varlist:
dic = dic.get(var)
return dic
def make_options(option_iter):
return [{'label': i, 'value': i} for i in option_iter]
class InputContainer:
def __init__(self, input_dd='day1', model_dd='model1', weight_dd='weight1',
crop_dd='crop1', columns_dd='Precip'):
self.container = [
dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options= make_options(final_dict.keys()),
value=input_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
value = model_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
value = weight_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
value = crop_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
value = columns_dd,
options = make_options(['time', 'Temp','Precip']),
clearable=False,
),
]
),
]
self.assign_index()
def assign_index(self): # unused: just for an idea
self.idx = {}
for i, item in enumerate(self.container):
if hasattr(item.children[1], 'id'): # skip the label at 0
self.idx[item.children[1].id] = i
def update_options(self, varname, options, selected=0): # unused: just for an idea
pos = self.idx[varname]
print(self.container[pos].children[1])
if hasattr(self.container[pos].children[1],'id'):
setattr(self.container[pos].children[1],'options', options)
setattr(self.container[pos].children[1],'value', options[selected].get('value'))
# UI #####################################################################
controls = dbc.Card(
html.Div(
id='dynamic-input-container',
children = InputContainer().container),
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row(html.Div(id='selected_data')),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
@app.callback(
[Output('model_dd', 'options'),
Output('model_dd', 'value')],
[Input('init_dd', 'value')],
)
def update_model_options(input_dd):
print('update_model_options():')
options = make_options(get_dict_keys([input_dd]).keys())
return options, options[0].get('value')
#set the weight
@app.callback(
[Output('weight_dd', 'options'),
Output('weight_dd', 'value')],
[Input('model_dd', 'value')],
[State('init_dd', 'value')])
def update_weight_options(model_dd, input_dd):
print('update_weight_options():')
options = make_options(get_dict_keys([input_dd, model_dd]).keys())
return options, options[0].get('value')
#set the crop
@app.callback(
[Output('crop_dd', 'options'),
Output('crop_dd', 'value')],
[Input('weight_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value')])
def update_crop_options(weight_dd, input_dd, model_dd):
print('update_crop_options():')
options = make_options(get_dict_keys([input_dd, model_dd, weight_dd]).keys())
return options, options[0].get('value')
# #set the variable
# @app.callback(
# [Output('columns_dd', 'options'),
# Output('columns_dd','value')],
# [Input('crop_dd', 'value')],
# [State('init_dd', 'value'),
# State('model_dd', 'value'),
# State('weight_dd', 'value')])
# def set_column_options(crop_dd, input_dd, model_dd, weight_dd):
# print('update_column_options():')
# options = make_options(get_dict_keys([input_dd, model_dd, weight_dd, crop_dd]).columns[1:])
# return options, options[0].get('value')
def make_plot(df, var):
fig = go.Figure(
data=[go.Scatter(x=df['time'], y=df[var], name=var)],
layout={
'yaxis': {'title': f'Plot of <b>{var}</b>'}
}
)
return fig
no_data_fig = {"layout": {
"xaxis": { "visible": False},
"yaxis": {"visible": False},
"annotations": [
{ "text": "",
"xref": "paper",
"yref": "paper",
"showarrow": False,
"font": {"size": 20 }
}]
}
}
@app.callback(
Output('crop-graph', 'figure'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def plot_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('plot_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd]
data_col = data[columns_dd]
except:
return no_data_fig
return make_plot(data, columns_dd)
app.run_server(mode='external', port = 8098, debug=True)