【问题标题】:Plotly Dash Graph With Multiple Dropdown Inputs Not Working使用多个下拉输入不工作的 Plotly Dash 图
【发布时间】:2019-05-21 11:56:25
【问题描述】:

我正在尝试创建一个具有多个交互式下拉用户输入变量的时间序列 Dash 折线图。理想情况下,我希望每个下拉输入都允许进行多项选择。

虽然我能够成功创建下拉菜单,但图表并没有像我希望的那样更新。当我允许下拉菜单有多个选择时,我收到一个错误,即数组长度不同。当我将下拉菜单限制为一个选项时,我收到一个错误,指出 ['Vendor_Name'] 不在索引中。所以这可能是两个不同的问题。

无效的图表:

导入DF的Excel数据片段

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import pandas as pd
#import plotly.graph_objs as go 
df = pd.read_csv("Data.csv", sep = "\t")
df['YearMonth'] = pd.to_datetime(df['YearMonth'], format = '%Y-%m')
cols = ['Product_1', 'Product_2', 'Product_3']
vendor = df['Vendor'].unique()

app = dash.Dash('Data')

app.layout = html.Div([
    html.Div([
        html.Div([

            html.Label('Product'),
            dcc.Dropdown(
                 id = 'product',
                 options = [{
                         'label' : i, 
                         'value' : i
                 } for i in cols],
                multi = True,
                value = 'Product_1'

                 ),
                ]),

        html.Div([

            html.Label('Vendor'),
            dcc.Dropdown(
             id = 'vendor',
             options = [{
                     'label' : i, 
                     'value' : i
             } for i in vendor],
            multi = True,
             value = 'ABC')
             ,
        ]),
            ]),

    dcc.Graph(id = 'feature-graphic')
    ])


@app.callback(Output('feature-graphic', 'figure'),
    [Input('product', 'value'),
     Input('vendor', 'value')])


def update_graph(input_vendor, input_column):


    df_filtered = df[df['Vendor'] == input_vendor]

##also tried setting an index because of the error I was getting. Not sure if necessary
    df_filtered = df_filtered.set_index(['Vendor']) 

    traces = []

    df_by_col = df_filtered[[input_column, 'YearMonth']]

    traces.append({

        'x' :pd.Series(df_by_col['YearMonth']),
        'y' : df_by_col[input_column],
        'mode' : 'lines',
        'type' : 'scatter',
        'name' :'XYZ'}
        )

    fig = {
                    'data': traces,
                    'layout': {'title': 'Title of Chart'}
                    }
    return fig


if __name__ == '__main__':
    app.run_server(debug=False)

提前感谢您的帮助!对 Python 来说还是个新手,但对 Dash 的功能感到非常兴奋。我已经能够使用单个输入创建其他图表,并且通读了文档。

【问题讨论】:

    标签: python-3.x plotly-dash


    【解决方案1】:

    这是我遵循的方法:(使用我的方法编辑谷歌中可用的常见示例):

    import dash
    from dash.dependencies import Input, Output
    import dash_core_components as dcc
    import dash_html_components as html
    
    app = dash.Dash(__name__)
    
    all_options = {
        'America': ['New York City', 'San Francisco', 'Cincinnati'],
        'Canada': [u'Montréal', 'Toronto', 'Ottawa']
    }
    app.layout = html.Div([
        dcc.Dropdown(
            id='countries-dropdown',
            options=[{'label': k, 'value': k} for k in all_options.keys()],
            value='America',  #default value to show
            multi=True,
            searchable=False
        ),
    
        dcc.Dropdown(id='cities-dropdown', multi=True, searchable=False, placeholder="Select a city"),
    
        html.Div(id='display-selected-values')
    ])
    
    @app.callback(
        dash.dependencies.Output('cities-dropdown', 'options'),
        [dash.dependencies.Input('countries-dropdown', 'value')])
    def set_cities_options(selected_country):
        if type(selected_country) == 'str':
            return [{'label': i, 'value': i} for i in all_options[selected_country]]
        else:
            return [{'label': i, 'value': i} for country in selected_country for i in all_options[country]]
    
    if __name__ == '__main__':
        app.run_server(debug=True)
    

    这里的解决方法是:当父下拉列表中存在单个输入时,该值是字符串格式。但是对于多个值,它以列表格式出现。
    即使您单击交叉选项以删除任何选定的选项,此代码也可以完美运行并自动更新。

    注意:我使用了“占位符”属性而不是为其定义默认值,因为在这种情况下它没有任何意义。但是您也可以以类似的方式动态更新值。

    【讨论】:

      【解决方案2】:

      1 个输入数据

      csv 中的数据很难循环。 我认为这是您的代码不起作用的主要原因, 因为您似乎了解基本的代码结构。 戴上我的 SQL 眼镜后,我想你应该试着让它像这样

      Date, Vendor, ProductName, Value
      

      2个回调输入类型改变

      multi 很棘手,因为它会在仅选择一项时返回 str 和选择多于一项时返回 list 之间切换切换

      3回调返回类型

      您的代码返回一个dict,但回调将figure 声明为返回类型

      但这是带有print()sleep() 的调试痕迹的代码

      import pandas as pd
      import dash
      import dash_core_components as dcc
      import dash_html_components as html
      from dash.dependencies import Input, Output
      import plotly.graph_objs as go
      import time
      
      df = pd.read_csv("Data.csv", sep="\t")
      df['YearMonth'] = pd.to_datetime(df['YearMonth'], format='%Y-%m')
      products = ['Product_1', 'Product_2', 'Product_3']
      vendors = df['Vendor'].unique()
      
      app = dash.Dash('Data')
      
      app.layout = html.Div([
          html.Div([
              html.Div([
                  html.Label('Product'),
                  dcc.Dropdown(
                      id='product',
                      options=[{'label' : p, 'value' : p} for p in products],
                      multi=True,
                      value='Product_1'
                  ),
              ]),
              html.Div([
                  html.Label('Vendor'),
                  dcc.Dropdown(
                      id='vendor',
                      options=[{'label': v, 'value': v} for v in vendors],
                      multi=True,
                      value='ABC'
                  ),
              ]),
          ]),
          dcc.Graph(id='feature-graphic', figure=go.Figure())
      ])
      
      
      @app.callback(
          Output('feature-graphic', 'figure'),
          [Input('product', 'value'),
           Input('vendor', 'value')])
      def update_graph(input_product, input_vendor):
          # df_filtered[['Product_1', 'YearMonth']]
          if type(input_product) == str:
              input_product = [input_product]
          if type(input_vendor) == str:
              input_vendor= [input_vendor]
      
          datasets = ['']
          i = 1
          for vendor in input_vendor:
              df_filtered = df[df['Vendor'] == vendor]
              for product in input_product:
                  datasets.append((df_filtered[['YearMonth', 'Vendor', product]]).copy())
                  datasets[i]['ProductName'] = product
                  datasets[i].rename(columns={product: 'Value'}, inplace=True)
                  i += 1
          datasets.pop(0)
          print(datasets)
      
          traces = ['']
          for dataset in datasets:
              print(dataset)
              time.sleep(1)
              traces.append(
                  go.Scatter({
                      'x': dataset['YearMonth'],
                      'y': dataset['Value'],
                      'mode': 'lines',
                      'name': f"Vendor: {dataset['Vendor'].iloc[0]} Product: {dataset['ProductName'].iloc[0]}"
              }))
          traces.pop(0)
          layout = {'title': 'Title of Chart'}
      
          fig = {'data': traces, 'layout': go.Layout(layout)}
          return go.Figure(fig)
      
      
      if __name__ == '__main__':
          app.run_server()
      

      快速而肮脏的披露:

      如果您处理 1.问题它将大大简化一切。 所以我会尝试将pd.DataFrame()从回调中分离出来并进入上层I/O部分。

      1) 不要在 for 循环中使用计数器

      2) 我的变量名也不是最好的

      3)以下样式是穴居人的蟒蛇和there must be a better way

      traces = ['']
      traces.append(this_and_that)
      traces.pop(0)
      

      一般情况:

      使用print(input_variable)print(type(input_variable)) 大部分时间让我的轮子摆脱泥泞。

      毕竟

      您应该注意到每个trace 都有其单独的名称,该名称将显示在图例中。单击图例中的名称将添加或删除trace,而不需要@app.callback()

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-23
        • 1970-01-01
        • 2021-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-08
        • 2021-10-28
        相关资源
        最近更新 更多