【问题标题】:Plotly-Dash: Update a trace on a graph that doesn't have its own dropdownPlotly-Dash:更新没有自己下拉列表的图表上的跟踪
【发布时间】:2023-03-31 01:28:01
【问题描述】:

这个问题基本上是对这个先前提出的问题的补充:

Properly setting up callbacks for dynamic dropdowns plotly dash

现在,我想在我的绘图中添加第二条轨迹,它位于辅助 y 轴上。辅助 y 轴上的绘图数据将来自类似结构的字典和数据框,也具有类似的命名约定。这是我所拥有的。

app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])

index1= [1,2,3,4]
columns1 =['time', '2m_temp_prod' , 'total_precip_prod']

index2= [1,2,3,4]
columns2 = ['time', '2m_temp_area', 'total_precip_area']

df_vals_prod = {'corn': pd.DataFrame(index=index1, columns = columns1,
                                data= np.random.randn(len(index1),len(columns1))).cumsum(),
                'soybeans' : pd.DataFrame(index=index1, columns = columns1,
                                     data= np.random.randn(len(index1),len(columns1))).cumsum()}

df_vals_area= {'corn': pd.DataFrame(index=index2, columns = columns2,
                                data= np.random.randn(len(index2),len(columns2))).cumsum(),
               'soybeans' : pd.DataFrame(index=index2, columns = columns2,
                                     data= np.random.randn(len(index2),len(columns2))).cumsum()}


# mimic data properties of your real world data
df_vals_prod['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'), 
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['corn'].set_index('time', inplace = True)
df_vals_prod['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['soybeans'].set_index('time', inplace = True)

df_vals_area['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['corn'].set_index('time', inplace = True)
df_vals_area['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['soybeans'].set_index('time', inplace = True)

index3= [1,2,3,4]
columns3 =['time', '2m_temp_24hdelta_prod' , 'total_precip_24hdelta_prod']

index4= [1,2,3,4]
columns4 = ['time', '2m_temp_24hdelta_area', 'total_precip_24hdelta_area']

df_deltas_prod = {'corn': pd.DataFrame(index=index3, columns = columns3,
                                data= np.random.randn(len(index3),len(columns3))).cumsum(),
                'soybeans' : pd.DataFrame(index=index3, columns = columns3,
                                     data= np.random.randn(len(index3),len(columns3))).cumsum()}

df_deltas_area= {'corn': pd.DataFrame(index=index4, columns = columns4,
                                data= np.random.randn(len(index4),len(columns4))).cumsum(),
               'soybeans' : pd.DataFrame(index=index4, columns = columns4,
                                     data= np.random.randn(len(index4),len(columns4))).cumsum()}

# mimic data properties of your real world data
df_deltas_prod['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'), 
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_deltas_prod['corn'].set_index('time', inplace = True)
df_deltas_prod['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_deltas_prod['soybeans'].set_index('time', inplace = True)

df_deltas_area['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_deltas_area['corn'].set_index('time', inplace = True)
df_deltas_area['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_deltas_area['soybeans'].set_index('time', inplace = True)

# weighting
all_options = {
    'Production': list(df_vals_prod[list(df_vals_prod.keys())[0]].columns[1:]),
    'Area': list(df_vals_area[list(df_vals_prod.keys())[0]].columns[1:])
}


controls = dbc.Card(
    [    dbc.FormGroup(
            [
                dbc.Label("Crop"),
                dcc.Dropdown(
                    id='crop_dd',
                    options=[{'label': k.title(), 'value': k} for k in list(df_vals_prod.keys())],
                    value=list(df_vals_prod.keys())[0],
                    clearable=False,
                ),
            ]
        ),    
        
        
        
        dbc.FormGroup(
            [
                dbc.Label("Weighting"),
                dcc.Dropdown(
                    id='weight_dd',
                    options=[{'label': k, 'value': k} for k in all_options.keys()],
                    value='Area',
                    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"), 
                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,
)
    



# Callbacks #####################################################################

# Weighting selection.
@app.callback( # Dataframe PROD or AREA
    Output('columns_dd', 'options'),
    # layout element: dcc.RadioItems(id='weight_dd'...)
    [Input('weight_dd', 'value')])
def set_columns_options(weight):
    varz =  [{'label': i, 'value': i} for i in all_options[weight]]
    return [{'label': i, 'value': i} for i in all_options[weight]]

# Columns selection
@app.callback( 
    Output('columns_dd', 'value'),
    [Input('columns_dd', 'options')])
def set_columns(available_options):
    return available_options[1]['value']

# Crop selection
@app.callback( 
    Output('crop_dd', 'value'),
    [Input('crop_dd', 'options')])
def set_crops(available_crops):
    return available_crops[0]['value']




# Make a figure based on the selections
@app.callback( # Columns 2m_temp_prod, or....
    Output('crop-graph', 'figure'),
    [Input('weight_dd', 'value'),
     Input('crop_dd', 'value'),
     Input('columns_dd', 'value')])
def make_graph(weight, available_crops, vals):
    
    # data source / weighting
    if weight == 'Production':
        dfv = df_vals_prod
        #dfd = df_deltas_prod
    if weight == 'Area':
        dfv = df_vals_area
        #dfd= df_deltas_area
    
    # plotly figure
    fig = make_subplots(specs=[[{"secondary_y": True}]])
    if 'precip' in vals:
        fig.add_trace(go.Scatter(x=df_vals_prod[available_crops]['time'], y=round((dfv[available_crops][vals].cumsum()/25.4),2),
                                mode = 'lines', line=dict(color='lime', width=4),
                                hovertemplate='Date: %{x|%d %b %H%M} UTC<br>Precip: %{y:.2f} in<extra></extra>'), secondary_y=False)
    else:
        fig.add_trace(go.Scatter(x=df_vals_prod[available_crops]['time'], y=round(((dfv[available_crops][vals]-273.15)*(9/5))+32,2),
                                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.add_trace(go.Bar(x=dfd[available_crops].index, y=dfd[available_crops][deltas]), secondary_y=True)
    fig.update_layout(title=dict(text='Crop: ' + available_crops.title() + ', Weight: ' +weight+ ', Variable: '+ vals))
    fig.update_layout(yaxis2_showgrid=False,showlegend=False, 
                      width=1500,height=800,yaxis_zeroline=False, yaxis2_zeroline=False)
    fig.update_layout(template="plotly_dark", plot_bgcolor='#272B30', paper_bgcolor='#272B30')
    fig.update_yaxes(automargin=True)
    return(fig)

app.run_server(mode='external', port = 8099)

注意字典名称和字典中的数据框列如何具有相似的名称。我希望他们在情节上保持在一起。

例如,用户选择权重:生产,作物:玉米,预测变量:2m_temp_prod。这应该绘制一个线图。现在,我想添加一个辅助 y 轴,其中 2m_temp_24hdelta_prod 被绘制(来自 df_deltas_prod['corn']['2m_temp_24hdelta_prod']。请注意,虽然我不想要一个下拉菜单,但我只是希望它是根据其他下拉选择绘制。最后,如果用户切换到权重:面积,作物:玉米,预测变量:2m_temp_area,次要 y 轴将绘制 df_deltas_area['corn']['2m_temp_24hdelta_area']。希望这个很清楚。

【问题讨论】:

  • 你能在你的代码 sn-p 中包含你的导入吗?

标签: python plotly plotly-dash plotly-python


【解决方案1】:

只是为了看看我在这里是否正确理解了您的逻辑,我将在链接问题的答案中的 sn-p 基础上进行构建,而不是您的 sn-p'在这里提供。如果事实证明由下面的 sn-p 生成的应用程序的结构实际上是您正在寻找的,我会看看我是否可以将其应用于 this中的代码 sn-p > 问题。 (但首先,请提供一个 完全 工作的 sn-p with 必要的导入)。

逻辑:

正如您通过研究下面的 sn-p 所看到的,此建议仅限于在 另一个 dict,但对于 同一类别,例如 'corn''soybeans'。因此,如果主轴在主轴上显示''corn''corn'2m_temp_prod',则辅助y 轴显示'2m_temp_area''corn''df_vals_prod''corn'

为了遵循所描述的逻辑,更新后的代码 sn-p 中的一个关键附录是:

# secondary yaxis
column_implied_lst = [e for e in dfd2[available_crops].columns if e[:4]==selected_column[:4]]
column_implied = column_implied_lst[0]

fig.add_trace(go.Bar(x=dfd2[available_crops].index,
                     y=dfd2[available_crops][column_implied],
                     marker_color = "rgba(255,0,0,0.4)"),
              secondary_y=True)
fig.update_layout(yaxis2=dict(title=dict(text='DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ column_implied)))

编辑:字符串匹配

我对@9​​87654336@ 使用了一种非常基本的字符串匹配方法。在为您的答案构建解决方案时,我提出了问题How to retrieve partial matches from a list of strings,结果得到了一些非常好的答案。如果我提供的方法不适合您的需求,请查看那些。

Dash 应用程序:

完整代码(第一个音高)

from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

# 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

# data ##########################################################################
index1= [1,2,3,4]
columns1 =['time', '2m_temp_prod' , 'total_precip_prod']

index2= [1,2,3,4]
columns2 = ['time', '2m_temp_area', 'total_precip_area']

df_vals_prod = {'corn': pd.DataFrame(index=index1, columns = columns1,
                                data= np.random.randn(len(index1),len(columns1))).cumsum(),
                'soybeans' : pd.DataFrame(index=index1, columns = columns1,
                                     data= np.random.randn(len(index1),len(columns1))).cumsum()}

df_vals_area= {'corn': pd.DataFrame(index=index2, columns = columns2,
                                data= np.random.randn(len(index2),len(columns2))).cumsum(),
               'soybeans' : pd.DataFrame(index=index2, columns = columns2,
                                     data= np.random.randn(len(index2),len(columns2))).cumsum()}

# mimic data properties of your real world data
df_vals_prod['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'), 
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['corn'].set_index('time', inplace = True)
df_vals_prod['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['soybeans'].set_index('time', inplace = True)

df_vals_area['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['corn'].set_index('time', inplace = True)
df_vals_area['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['soybeans'].set_index('time', inplace = True)

# dash ##########################################################################
app = JupyterDash(__name__)

# weighting
all_options = {
    'prod': list(df_vals_prod[list(df_vals_prod.keys())[0]].columns),
    'area': list(df_vals_area[list(df_vals_prod.keys())[0]].columns)
}

app.layout = html.Div([
    dcc.Dropdown(
        id='produce-radio',
        options=[{'label': k, 'value': k} for k in all_options.keys()],
        value='area'
    ),
#     dcc.Dropdown(
#     id='produce-radio',
#     options=[
#         {'label': k, 'value': k} for k in all_options.keys()
#     ],
#     value='prod',
#     clearable=False),
    

    html.Hr(),
    
    dcc.Dropdown(
        id='crop-radio',
        options=[{'label': k, 'value': k} for k in list(df_vals_prod.keys())],
        value=list(df_vals_prod.keys())[0]
    ),

    html.Hr(),

    dcc.Dropdown(id='columns-radio'),

    html.Hr(),

    html.Div(id='display-selected-values'),
    
    dcc.Graph(id="crop-graph")
])

# Callbacks #####################################################################

# Weighting selection.
@app.callback( # Dataframe PROD or AREA
    Output('columns-radio', 'options'),
    # layout element: dcc.RadioItems(id='produce-radio'...)
    [Input('produce-radio', 'value')])
def set_columns_options(selected_produce):
    varz =  [{'label': i, 'value': i} for i in all_options[selected_produce]]
    print('cb1 output: ')
    print(varz)
    return [{'label': i, 'value': i} for i in all_options[selected_produce]]

# Columns selection
@app.callback( 
    Output('columns-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('columns-radio', 'options')])
def set_columns(available_options):
    return available_options[0]['value']

# Crop selection
@app.callback( 
    Output('crop-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('crop-radio', 'options')])
def set_crops(available_crops):
    return available_crops[0]['value']

# Display selections in its own div
@app.callback( # Columns 2m_temp_prod, or....
    Output('display-selected-values', 'children'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def set_display_children(selected_produce, available_crops, selected_column):
    return('DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column)

# Make a figure based on the selections
@app.callback( # Columns 2m_temp_prod, or....
    Output('crop-graph', 'figure'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def make_graph(selected_produce, available_crops, selected_column):
    #global selected_column
    
    # data source / weighting
    if selected_produce == 'prod':
        dfd = df_vals_prod
        dfd2 = df_vals_area
    if selected_produce == 'area':
        dfd = df_vals_area
        dfd2 = df_vals_prod
    
    # plotly figure
    
    # primary yaxis
    fig = make_subplots(specs=[[{"secondary_y": True}]])
    fig.add_trace(go.Scatter(x=dfd[available_crops].index, y=dfd[available_crops][selected_column]), secondary_y=False)
    fig.update_layout(yaxis1=dict(title=dict(text='DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column)))
    
    # secondary yaxis
    
    column_implied_lst = [e for e in dfd2[available_crops].columns if e[:4]==selected_column[:4]]
    column_implied = column_implied_lst[0]
    
    fig.add_trace(go.Bar(x=dfd2[available_crops].index,
                         y=dfd2[available_crops][column_implied],
                         marker_color = "rgba(255,0,0,0.4)"),
                  secondary_y=True)
    fig.update_layout(yaxis2=dict(title=dict(text='DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ column_implied)))
    
    # layout makeover
    fig.update_layout(title=dict(text='Column to match: '+ selected_column + '| Implied match: ' +column_implied))
    fig['layout']['yaxis2']['showgrid'] = False
    
    return(fig)

app.run_server(mode='inline', port = 8077, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)

【讨论】:

  • 太棒了。这正是我所希望的。谢谢!很想分享我几乎完成的产品。有没有办法可以访问我创建的仪表板?
  • @EliTurasky 这是个好问题!也许是谷歌 colab?不确定,实际上...
  • 不幸。无论如何。我将从这里添加更多的下拉菜单,但基本上已经完成。这是一张静态图片:imgur.com/a/LQbl7Qk
  • @EliTurasky 干得好!我也很喜欢你选择的颜色!
猜你喜欢
  • 2019-01-23
  • 1970-01-01
  • 2022-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多