Включение функции выбора всех в обратный вызов Dash Plotly – Python

У меня есть гистограмма, подключенная к обратному вызову для фильтрации. Ниже приведена фильтрация по столбцу «Тип».

Проблема: реальные данные, которые я фильтрую, содержат тысячи элементов. Сначала я хочу показать все данные, но не хочу визуализировать все отдельные элементы в раскрывающемся списке (потому что их слишком много). Поэтому я планирую включить функцию «Выбрать все».

Я не думаю, что нынешним подходом можно манипулировать, но я открыт для новых идей.

Вопрос: Если в раскрывающемся меню выбран вариант «Выбрать все», это должен быть единственный видимый значок. Вы не можете иметь все предметы и что-то еще. Если изначально выбран отдельный тип(ы), то если впоследствии будет выбран вариант «Выбрать все», отдельный элемент должен быть удален из раскрывающейся панели.

Пример ниже. Начните с B, затем выбирается Select All, поэтому B следует удалить из раскрывающегося списка.

Кроме того, я вообще сомневаюсь, что это функция, но если Select All стоит, то отдельные типы не могут быть добавлены в раскрывающийся список.

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import plotly.graph_objs as go
import pandas as pd

df = pd.DataFrame({
       'Type': ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'],
       })

N = 300
df = pd.concat([df] * N, ignore_index=True)

df['TIMESTAMP'] = pd.date_range(start='2024/01/01 07:36', end='2024/01/30 08:38', periods=len(df))

df['DATE'], df['TIME'] = zip(*[(d.date(), d.time()) for d in df['TIMESTAMP']])
df['DATE'] = pd.to_datetime(df['DATE'], format='%Y-%m-%d')


external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]

app = dash.Dash(__name__, external_stylesheets = external_stylesheets)


filter_box = html.Div(children=[
    html.Div(children=[
        dcc.Dropdown(
            id = 'Type',
            options = [
                {'label': x, 'value': x} for x in df['Type'].unique()
            ] + [
                {'label': 'Select All', 'value': 'all'}
            ],
            value = 'all',
            multi = True,
            clearable = True,
            style = {'display': 'inline-block','margin':'0.1rem'}
        ),       
    ], className = "vstack gap-1 h-100",            
    )
])

app.layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            dbc.Row([
                dbc.Col(html.Div(filter_box), 
                        ),
            ]),
        ]),
        dbc.Col([
            dbc.Row([
                dcc.Graph(id = 'date-bar-chart'),
            ]),
        ])
    ])
], fluid = True)

@app.callback(
    Output('date-bar-chart', 'figure'),
    [Input('Type', 'value'), 
    ])     

def chart(value_type):

    if 'all' in value_type:
          value_type = ['all']
    else:
          value_type = value_type

    if value_type == 'all':
        dff = df

    elif value_type == ['all']:
        dff = df

    else:
        dff = df[df['Type'].isin(value_type)]

    df_count = dff.groupby(['DATE','Type'])['DATE'].count().reset_index(name = 'counts')

    if df_count.empty == True:
        type_fig = go.Figure()

    else:
        df_count = df_count

        type_fig = px.bar(x = df_count['DATE'], 
                      y = df_count['counts'],
                      color = df_count['Type']
                      )

    return type_fig

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
0
144
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не очень разбираюсь в Dash, но вы можете попробовать внести пару изменений в объявление dcc.Dropdown, чтобы убедиться, что вы видите «Выбрать все». Во-первых, словарь «Выбрать все» добавлен в конец списка параметра options, а не в начало. Возможно, вы захотите переместить его в начало, чтобы оно появилось. Согласно документации:

Параметры, представленные в виде одного словаря, отображаются в браузере в произвольном порядке. Предоставление списка, содержащего словарь для каждого параметра, гарантирует отображение параметров в указанном порядке.

Во-вторых, у вас есть параметр dcc.Dropdownvalue, настроенный как value = 'Select All', но пример в документации указывает, что для него должно быть установлено значение соответствующего словаря, а не ключа:

dcc.Dropdown(
   options=[
       {'label': 'New York City', 'value': 'NYC'},
       {'label': 'Montreal', 'value': 'MTL'},
       {'label': 'San Francisco', 'value': 'SF'},
   ],
   value='MTL'
)

Следовательно, вы также можете попробовать изменить это на value = 'all'. В итоге:

    dcc.Dropdown(
        id = 'Type',
        options = [
            {'label': 'Select All', 'value': 'all'}
        ] + [
            {'label': x, 'value': x} for x in df['Type'].unique()
        ],
        value = 'all',
        multi = True,
        clearable = True,
        style = {'display': 'inline-block','margin':'0.1rem'}
    ),

Спасибо, я внес некоторые поправки. Главный вопрос все еще остается в силе, но он стал немного чище.

tonydanza123 16.04.2024 01:37

Хорошо, это проясняет ситуацию. Если у меня будет время позже, я напишу пример кода; Между тем, эта документация может помочь, если вы хотите попробовать ее.

Bill Horvath 16.04.2024 20:36
Ответ принят как подходящий

Обработка выбора в порядке, но вам не хватает обновления компонента Dropdown.

Кроме того, в случае добавления чего-то еще к опции «Выбрать все» вы, вероятно, захотите очистить только выделение, но сохранить график без изменений. Можно отметить, что существуют противоположные случаи, которые заканчиваются одинаковым значением выбора, например. ['A', 'all']. Чтобы отличить их, вам необходимо отслеживать предыдущее состояние, что можно реализовать с помощью компонента dcc.Store.

Они суммируются следующими изменениями:

Дополнительный импорт
from dash import no_update
from dash.dependencies import State
Некоторые константы и вспомогательный метод создания диаграммы:
SELECT_ALL = 'all'  # to avoid string literals in the code

def make_figure(*, select_all=True, selected_values=None):

    if select_all:
        dff = df  # use source data frame
    
    elif selected_values is None:
        return go.Figure()  # result is empty without filter values

    else:
        dff = df[df['Type'].isin(selected_values)]  # filtered

    df_count = dff.groupby(['DATE', 'Type'])['DATE'].count().reset_index(
        name='counts')

    if df_count.empty:
        return go.Figure()

    return px.bar(x=df_count['DATE'],
                  y=df_count['counts'],
                  color=df_count['Type'])
Обновление макета:
filter_box = html.Div(children=[
    dcc.Store(id='session_select_all_types', data=True, storage_type='session'),
    html.Div(children=[
        dcc.Dropdown(
...

app.layout = dbc.Container([
    ...
    # Initialize with all types selected
    dcc.Graph(id='date-bar-chart',
              figure=make_figure(select_all=True)),
Перезвонить:
@app.callback(
    Output('date-bar-chart', 'figure'),
    Output('Type', 'value'),
    Output('session_select_all_types', 'data'),
    Input('Type', 'value'),
    State('session_select_all_types', 'data'))
def chart(value_type, select_all_types_previous):

    select_all_types = SELECT_ALL in value_type

    if select_all_types:
        if select_all_types_previous is True:
            return (no_update,     # you don't want to recreate a valid figure
                    [SELECT_ALL],  # force it to have an only Select all value
                    no_update)     # already True

        value_type = [SELECT_ALL]

    figure = make_figure(select_all=select_all_types,
                         selected_values=value_type)

    return (figure,
            value_type,        # update the selection
            select_all_types)  # update the flag in the session storage

Спасибо. Работает хорошо. Однако есть несколько вопросов. Select All не отображает никаких данных при первом открытии или при обновлении *ЕСЛИ Select All было последним значением, выбранным перед обновлением. all возвращается, но данные на диаграмме не отображаются. Отдельно, насколько сложно было бы отбросить Select All, если бы было выбрано индивидуальное значение? Вместо того, чтобы очищать Select All перед выбором отдельного значения.

tonydanza123 17.04.2024 03:11

О, вы правы, я пропустил значение по умолчанию «Выбрать все». Обновил ответ: переключите значение хранилища по умолчанию на True, сделайте вспомогательный метод для генерации фигур, сразу инициализируйте макет полной фигурой вместо первого обратного вызова.

Dmitry 17.04.2024 10:17

Что касается второй части, то это лучше другой вопрос. Но это не сложно. Вам необходимо реализовать эту логику: если было выбрано все, а стало выбрано все+, затем нажмите «Выбрать все», если не было «Выбрать все», а стало «Выбрать все+», тогда сделайте так, чтобы оно только выбрало все, в противном случае работайте с выбранными значениями.

Dmitry 17.04.2024 10:34

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

tonydanza123 22.04.2024 03:14

Другие вопросы по теме