首页 > 解决方案 > 在 plotly 上创建具有多个回调的过滤器

问题描述

我最近开始使用 plotly 来创建交互式仪表板。我仍在学习它,所以我想知道最佳实践,以便创建影响我的应用程序中多个数字的过滤器。

这就是我的app.py样子:

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd
from datetime import datetime
from pandas import Timestamp

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

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

colors = {
    'background': '#111111',
    'text': '#7FDBFF'
}

data = {'Id Incidencia': {0: 'INC000006722157',
  1: 'INC000006722000',
  2: 'INC000006721939',
  3: 'INC000006708347',
  4: 'INC000006723090',
  5: 'INC000006736601',
  6: 'INC000006736721',
  7: 'INC000006724926',
  8: 'INC000006725331',
  9: 'INC000006725229',
  10: 'INC000006722542',
  11: 'INC000006722729',
  12: 'INC000006723246',
  13: 'INC000006722574',
  14: 'INC000006741563',
  15: 'INC000006722571',
  16: 'INC000006741632',
  17: 'INC000006741568',
  18: 'INC000006741636',
  19: 'INC000006741640'},
 'Fecha Apertura': {0: Timestamp('2020-12-07 12:28:30'),
  1: Timestamp('2020-12-07 09:52:06'),
  2: Timestamp('2020-12-07 10:13:06'),
  3: Timestamp('2020-12-02 09:02:45'),
  4: Timestamp('2020-12-07 20:37:53'),
  5: Timestamp('2020-12-12 00:35:16'),
  6: Timestamp('2020-12-12 00:46:48'),
  7: Timestamp('2020-12-08 15:21:15'),
  8: Timestamp('2020-12-08 20:04:14'),
  9: Timestamp('2020-12-08 18:33:54'),
  10: Timestamp('2020-12-07 15:52:59'),
  11: Timestamp('2020-12-07 18:33:22'),
  12: Timestamp('2020-12-07 23:56:08'),
  13: Timestamp('2020-12-07 17:11:05'),
  14: Timestamp('2020-12-14 13:31:05'),
  15: Timestamp('2020-12-07 17:06:55'),
  16: Timestamp('2020-12-14 13:44:35'),
  17: Timestamp('2020-12-14 13:33:40'),
  18: Timestamp('2020-12-14 13:46:38'),
  19: Timestamp('2020-12-14 13:51:34')}}
df = pd.DataFrame(data)
df["Fecha Apertura"] = pd.to_datetime(df["Fecha Apertura"])
df = df.set_index('Fecha Apertura')

periods = [('D', '%Y-%m-%d'), ('M', '%Y-%m'), ('Y', '%Y')]
grouped_data = df.groupby(df.index.to_period(periods[0][0]))["Id Incidencia"].count()
fig2 = px.line(
    x=grouped_data.index.strftime(periods[0][1]),
    y=grouped_data.values,
    title='Distribución temporal de las incidencias',
    labels={
        'x': 'Fecha',
        'y': 'Nº incidencias'
    }
)

app.layout = html.Div(children=[
    dcc.Dropdown(
        id='groupby-period',
        options=[
            {'label': 'Día', 'value': 0},
            {'label': 'Mes', 'value': 1},
            {'label': 'Año', 'value': 2}
        ],
        value=0,
        clearable=False,
        searchable=False
    ),

    dcc.DatePickerRange(
        id='date-picker-range',
        start_date_placeholder_text="Fecha Inicio",
        end_date_placeholder_text="Fecha Fin",
        calendar_orientation='horizontal',
    ),

    dcc.Graph(
        id='line-chart-apertura',
        figure=fig2
    )
])

@app.callback(
    Output('line-chart-apertura', 'figure'),
    Input('groupby-period', 'value')
)
def update_graphs_by_period(period):
    periods = [('D', '%Y-%m-%d'), ('M', '%Y-%m'), ('Y', '%Y')]
    grouped_data = df.groupby(df.index.to_period(periods[period][0]))["Id Incidencia"].count()

    fig = px.line(
        x=grouped_data.index.strftime(periods[period][1]),
        y=grouped_data.values,
        title='Distribución temporal de las incidencias',
        labels={
            'x': 'Fecha',
            'y': 'Nº incidencias'
        }
    )

    fig.update_layout(transition_duration=500)

    return fig

@app.callback(
    Output('line-chart-apertura', 'figure'),
    [
        Input('date-picker-range', 'start_date'),
        Input('date-picker-range', 'end_date')
    ],
    prevent_initial_call=True
)
def filter_by_date_range(start_date, end_date):
    if start_date is None or end_date is None:
        return dash.no_update

    start_date_object = datetime.fromisoformat(start_date)
    end_date_object = datetime.fromisoformat(end_date)

    mask = (df.index >= start_date_object) & (df.index <= end_date_object)
    df_range = df.loc[mask]

    periods = [('D', '%Y-%m-%d'), ('M', '%Y-%m'), ('Y', '%Y')]
    grouped_data = df_range.groupby(df_range.index.to_period(periods[0][0]))["Id Incidencia"].count()
    fig = px.line(
        x=grouped_data.index.strftime(periods[0][1]),
        y=grouped_data.values,
        title='Distribución temporal de las incidencias',
        labels={
            'x': 'Fecha',
            'y': 'Nº incidencias'
        }
    )

    fig.update_layout(transition_duration=500)

    return fig

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

我稍微解释一下:

到目前为止,我已经实现了让两个过滤器独立工作,但我正在努力让它们同时工作。我知道我的实际方法是不可能的,因为一个数字只能受到一个回调的影响,所以我需要将它们组合成一个。但是,我不知道最干净和最佳实践方法是什么。

  1. 将两种回调方法合并为一个的最干净的方法是什么?
  2. 我的按日期范围过滤df_range = df.loc[mask]的方法是我应该做的,还是有一个像全局 df 对象df_to_show = df.copy()并使用它来代替它会更好?
  3. 关于#2:如果回调函数修改了全局变量,我怎么能做到这一点df_to_show,所有使用它的数字都会注意到它,以便他们重绘自己?

标签: pythonpandasplotlyplotly-dashplotly-python

解决方案


推荐阅读