破折号:每列创建一个下拉列表,而不是每个表创建一个下拉列表

时间:2019-04-29 12:50:20

标签: python plotly-dash plotly-python

我有一个看起来像这样的数据集:

cat_id  author  year    publisher   country value (dollars)
name1   kunga   1998    D and D Australia   10
name2   siba    2001    D and D UK  20
name3   siba    2001    D and D US  20
name3   shevara 2001    D and D UK  10
name3   dougherty   1992    D and D Australia   20
name4   ken 2011    S and K Australia   10

目标是生成“每列”而不是所有列的“多选”下拉菜单(因为我目前无法执行的“每列”过滤器,我需要能够按一列中的每列过滤多个项目去)。

使用上面的示例,这将在cat_id下添加一个名称为1,2,3,4的单元格;作者将有一个关于kunga,siba,shevara和发酵的下拉列表;年份将与1992、1998、2001、2011等一起下降。

我编写了以下代码:

import dash
from dash.dependencies import Input, Output, State
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

app = dash.Dash(__name__)
df = pd.read_excel('dash_test_doc.xlsx')


app.layout = html.Div([
    html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10},),html.Button('Add Column', id='adding-rows-button', n_clicks=0)], style={'height': 50}),

#Need to move this to being within each column?
        html.Label('Multi-Select Dropdown'),
            dcc.Dropdown(options=[{'label':i,'value':i} for i in df.columns],
            value = [i for i in df.columns],
            multi=True),


        dash_table.DataTable(
         id='adding-rows-table',
         columns=[{"name": i, "id": i,"deletable":True} for i in df.columns],
#        column_conditional_dropdowns=[{'id':i,'dropdowns':df[i]} for i in df.columns],
         data = df.to_dict('rows'),
         editable=True,
         filtering=True,
         sorting=True,
         sorting_type="multi",
         row_selectable="multi",
         row_deletable=True,
         selected_rows=[],
         pagination_mode="fe",
         style_cell_conditional=[
         {
             'if': {'row_index': 'odd'},
             'backgroundColor': 'rgb(230, 255, 230)'
         }
     ] + [
         {
             'if': {'column_id': c},
             'textAlign': 'left'
        } for c in ['Date', 'Region']
    ],
    style_header={
        'backgroundColor': 'white',
        'fontWeight': 'bold'
    }
    ),



    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    dcc.Graph(id='adding-rows-graph')
])


@app.callback(
    Output('adding-rows-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('adding-rows-table', 'data'),
     State('adding-rows-table', 'columns')])
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows


@app.callback(
    Output('adding-rows-table', 'columns'),
    [Input('adding-rows-button', 'n_clicks')],
    [State('adding-rows-name', 'value'),
     State('adding-rows-table', 'columns')])
def update_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'id': value, 'name': value,
            'editable_name': True, 'deletable': True
        })
    return existing_columns


@app.callback(
    Output('adding-rows-graph', 'figure'),
    [Input('adding-rows-table', 'data'),
     Input('adding-rows-table', 'columns')])
def display_output(rows, columns):
    return {
        'data': [{
            'type': 'heatmap',
            'z': [[row.get(c['id'], None) for c in columns] for row in rows],
            'x': [c['name'] for c in columns]
        }]
    }



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

此方法存在两个特定问题:

  1. 下拉列表是在表级别而不是在每个列级别进行的。
  2. 然后我想过滤表格以仅显示下拉菜单中的内容,而目前尚未完成。

我知道问题所在的代码部分是:

#Need to move this to being within each column?
        html.Label('Multi-Select Dropdown'),
            dcc.Dropdown(options=[{'label':i,'value':i} for i in df.columns],
            value = [i for i in df.columns],
            multi=True),

并且没有回调,但是我是破折号的新手,而且我正在努力了解我应该具体做什么。如果有人可以向我展示如何在此代码中解决这两个问题,我将不胜感激。

作为旁注/免责声明,我最初是作为一个更大的问题here的一部分来问这个问题的,但是我意识到我需要让我的问题更具体地针对代码,并且更一般而言,因此我将逐一处理每个问题。

1 个答案:

答案 0 :(得分:0)

感谢凯拉,使这个问题更加具体。这仍然是一个不错的选择,所以我看看是否能帮上所有忙。

第一件事是您需要更改表中的列定义,以使字典中要显示为下拉列表的每一列都具有'presentation': 'dropdown'。当前下拉列表在表内不起作用,因为您使用了dcc.Dropdown组件,该组件是与表分离的组件。如果您希望它们在表中工作,则需要使用column_static_dropdowncolumn_conditional_dropdowns(我看到您已将其注释掉)。

不过,我不确定表中的下拉列表是否可以用作回调的一部分。我试图将其连接起来,但下拉菜单的ID未识别为布局的一部分。要执行您想要的操作-我想-需要使用dcc.Dropdown设置多个下拉菜单,并将这些值作为更新表data属性的回调的输入。

这是一个我已经证实有效的小例子,但有一些限制:

app = dash.Dash(__name__)
df = pd.DataFrame(np.arange(30).reshape(5, 6))

app.layout = html.Div([
    html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10}, ),
        html.Button('Add Column', id='adding-rows-button', n_clicks=0)], style={'height': 50}),

    # Need to move this to being within each column?
    html.Label('Multi-Select Dropdown'),
    dcc.Dropdown(
        id='test-dropdown',
        options=[{'label': i, 'value': i} for i in df[0]],
        value=[i for i in df[0]],
        multi=True),

    dash_table.DataTable(
        id='adding-rows-table',
        columns=[{"name": i, "id": i, "deletable": True} for i in df.columns],
        data=df.to_dict('rows'),
        editable=True,
        filtering=True,
        sorting=True,
        sorting_type="multi",
        row_selectable="multi",
        row_deletable=True,
        selected_rows=[],
        pagination_mode="fe",
        style_cell_conditional=[
               {
                   'if': {'row_index': 'odd'},
                   'backgroundColor': 'rgb(230, 255, 230)'
               }
           ] + [
               {
                   'if': {'column_id': c},
                   'textAlign': 'left'
               } for c in ['Date', 'Region']
           ],
        style_header={
            'backgroundColor': 'white',
            'fontWeight': 'bold'
        }
    ),

    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    dcc.Graph(id='adding-rows-graph'),
])


@app.callback(
    Output('adding-rows-table', 'data'),
    [Input('editing-rows-button', 'n_clicks'),
     Input('test-dropdown', 'value')],
    [State('adding-rows-table', 'data'),
     State('adding-rows-table', 'columns')])
def add_row(n_clicks, dropdown_value, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})

    df = pd.DataFrame.from_dict(rows)

    try:
        df_filtered = df[df['0'].isin(dropdown_value)]
    except TypeError:
        df_filtered = df[df['0'].eq(dropdown_value)]

    return df_filtered.to_dict(orient='records')

现在只有一个下拉列表,并且可以使用第一列中的值。您将需要添加更多,并且很快就会变得复杂。您可能可以设置下拉菜单的样式,以便它们与相关列更清晰地关联。

过滤掉行后,还存在如何处理行的问题。回调从表中提取State data,但是,如果过滤出一行,则该状态不在状态,因此无法通过再次在下拉菜单中进行选择来重新添加。您将需要一种不同的方式来处理表数据的存储,以免这种方式删除行。