Dash: создание раскрывающегося списка для столбца вместо раскрывающегося списка для таблицы - PullRequest
1 голос
/ 29 апреля 2019

У меня есть набор данных, который выглядит следующим образом:

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

Цель - создать выпадающее меню с несколькими вариантами выбора на столбец, а не на все столбцы (потому что фильтр на столбец, который я в данный моментdo не достаточно, мне нужно иметь возможность фильтровать по нескольким элементам на столбец за один раз).

Используя приведенный выше пример, это добавило бы ячейку сразу под cat_id с именем1,2,3,4;у автора будет выпадающий список с кунга, сиба, шевара и душерти;год будет иметь выпадение с 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),

и что нет обратного вызова, но я новичок в тире, и яЯ изо всех сил пытаюсь понять, что конкретно я должен делать.Если бы кто-то мог показать мне, как исправить эти две проблемы в этом коде, я был бы признателен.

В качестве примечания / отказа от ответственности я изначально задавал этот вопрос как часть гораздо большего вопроса здесь, но я понял, что мне нужно сделать свои вопросы более специфичными для кода и более конкретными в целом, поэтому я собираюсь проработать каждую проблему в отдельности.

1 Ответ

0 голосов
/ 01 мая 2019

Спасибо, Кела, за то, что сделали этот вопрос немного более конкретным.Это все еще хороший размер, поэтому я посмотрю, смогу ли я помочь со всем этим.

Во-первых, вам нужно изменить определение столбца в таблице, чтобы в словаре было 'presentation': 'dropdown' для каждогостолбец, который вы хотите показать как выпадающий список.Текущий выпадающий список не работает внутри таблицы, потому что вы использовали компонент dcc.Dropdown, который является отдельным компонентом таблицы.Вам нужно будет использовать column_static_dropdown или column_conditional_dropdowns (который, я вижу, вы прокомментировали), если вы хотите, чтобы они работали внутри таблицы.

Я не уверен, что выпадающие списки внутри таблицыможет быть использован как часть обратного вызова, однако.Я попытался подключить один, и идентификатор раскрывающегося списка не был распознан как часть макета.Чтобы сделать то, что вы хотите, - я думаю, - потребуется настроить несколько раскрывающихся списков с помощью dcc.Dropdown и подключить эти значения в качестве входных данных для обратного вызова, который обновляет таблицу data prop.

Вот небольшой пример того, чтоЯ подтвердил работы с некоторыми ограничениями:

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 из таблицы, но, если строка была отфильтрована, она не находится в состоянии и не может быть добавлена ​​обратно, выполнив этот выбор снова в раскрывающемся списке.Вам потребуется другой способ хранения данных для таблицы, чтобы строки не удалялись таким образом.

...