如何在 Plotly 中正确使用按钮过滤数据

Posted

技术标签:

【中文标题】如何在 Plotly 中正确使用按钮过滤数据【英文标题】:How to correctly use the buttons for filtering data in Plotly 【发布时间】:2021-05-19 01:14:01 【问题描述】:

我有一个看起来像这样的数据框,其中“dni”是每一行的唯一 ID。我想在每个单独的 dni 的日期列中使用 plotly 堆叠条形图。

dni date col1 col2 col3
unique ids Datetime 5 7 1

我目前使用的代码如下所示:

fig = go.Figure()
buttons = []

for i,dni in enumerate(sorted(df_merged.dni.unique())):
  df = df_merged[df_merged['dni']==dni]
  for column in df.columns[3:-1]:
      fig.add_trace(go.Bar(
                        name = column,
                        x = pd.to_datetime(df.date.astype('str')),
                        y = df[column], 
                        visible = (i==0)
                      ))
  args = [False] * df_merged.dni.nunique()
  args[i] = True
    
  button = dict(label = dni,
                method = "update",
                args=["visible": args])
      
  buttons.append(button)
        
fig.update_layout(
    updatemenus=[
        dict(
        type="dropdown",
        direction="down",
        buttons = buttons)
    ],
    barmode = "stack",)
fig.show()

这确实给了我一个带有过滤器的图,但它显示的数据总是不正确的。我正在努力理解我哪里出错了。它显示的唯一正确数据是第一个 dni,当我实际按下按钮时它也会改变。

谢谢!

编辑: 这是实际数据:

           id      date  bills  goalTrans  incomes  payments  savings
0  12345678901  2020-12    1.0        2.0      1.0       0.0      0.0
1  23456789012  2021-02    6.0        0.0      2.0       0.0      0.0
2  34567890123  2020-12    4.0        0.0      2.0       0.0      0.0
3  45678901234  2020-12    9.0        1.0      1.0       0.0      0.0
4  56789012345  2021-01    3.0        0.0      2.0       1.0      0.0

'bills': 0: 1.0, 1: 6.0, 2: 4.0, 3: 9.0, 4: 3.0,
 'date': 0: '2020-12',
  1: '2021-02',
  2: '2020-12',
  3: '2020-12',
  4: '2021-01',
 'id': 0: '12345678901',
  1: '23456789012',
  2: '34567890123',
  3: '45678901234',
  4: '56789012345',
 'goalTrans': 0: 2.0, 1: 0.0, 2: 0.0, 3: 1.0, 4: 0.0,
 'incomes': 0: 1.0, 1: 2.0, 2: 2.0, 3: 1.0, 4: 2.0,
 'payments': 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 1.0,
 'savings': 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0

【问题讨论】:

请按照here的描述分享您的数据集样本 更好。但这不是链接中描述的方法。 完美。我加了一个赞成票 =) 在字典中的示例数据框中,id 不应该是dni吗? 我的建议对你有什么效果? 【参考方案1】:

言归正传:

这可能是一个非常困难的问题,但是您错误地指定了 visible 属性,因为您的四个子集有三个跟踪,因此每个 arg 需要十二个规范,而不是您看起来的四个在您的设置中。

解决方案:

答案末尾的完整代码sn-p会产生下图,其中第一个按钮选项已经启动:

子集 1:

# 00820054194
   bills     date          dni  goalTrans  incomes  payments  savings
0    1.0  2020-12  00820054194        2.0      1.0       0.0      0.0

情节1:

如果您将Subset 1Plot 1 进行比较,您会发现到目前为止我们都很好。我将由您来验证所有子集,但这是最后一个来完成的:

子集 4:

# 04902852446
   bills     date          dni  goalTrans  incomes  payments  savings
3    9.0  2020-12  04902852446        1.0      1.0       0.0      0.0
4    3.0  2021-01  04902852446        0.0      2.0       1.0      0.0

情节4:

据我了解,这应该正是您正在寻找的。​​p>

详情:

在最后一次迭代中:

for i,dni in enumerate(sorted(df_merged.dni.unique())):

...args=["visible": args] 中的args 看起来像这样:

[False, False, False, False, True]

这并没有涵盖您设置中的所有选项。你看,当method = 'update' 要查找并可能更改fig.data 中的visible 属性时,args=["visible": args]) 中的args 会做什么,在你的情况下,它是一个带有 的元组十二 个元素,其中第一个看起来像这样:

(Bar(
     'name': 'goalTrans',
     'visible': True,
     'x': array([datetime.datetime(2020, 12, 1, 0, 0)], dtype=object),
     'y': array([2.])
 ),

为什么是 12?因为您有四个数据帧子集的三个go.Bar() 跟踪。因此,为了触发更新菜单的正确跟踪和正确选择选项(是的,按钮)的可见性,您不需要这样做:

[False, False, False, False, True]

但是这个:

[[True, True,True,False, False, False, False, False, False, False, False, False],
 [False, False, False, True, True, True, False, False, False, False, False, False],
 [False, False, False, False, False, False, True, True, True, False, False, False],
 [False, False, False, False, False, False, False, False, False, True, True, True]]

或者更确切地说,外部列表的每个元素都在迭代中:

for i, dni in enumerate(sorted(df_merged.dni.unique()[:])):

...像这样:

button =  dict(label=dni,
               method = 'restyle',
                args = ['visible',visibility[i]]
              )

...例如,visibility[2] 如下所示:

[False, False, False, False, False, False, False, False, False, True, True, True]

这就是下面完整代码中这个有点神秘的部分所需要处理的:

frames = len(df_merged.dni.unique())
bars = len(df.columns[3:-1])
scenarios = [list(s) for s in [e==1 for e in np.eye(frames)]]
visibility = [list(np.repeat(e, bars)) for e in scenarios]

您绝对可以考虑将其中一些行移出循环,因为它们重复了不必要的次数,但我发现让这些步骤彼此靠近更具可读性。还有问题吗?不要犹豫,问!以下是全部内容:

完整代码:

import pandas as pd
import numpy as np
import plotly.graph_objects as go
df_merged = pd.DataFrame('bills': 0: 1.0, 1: 6.0, 2: 4.0, 3: 9.0, 4: 3.0,
                     'date': 0: '2020-12',
                      1: '2021-02',
                      2: '2020-12',
                      3: '2020-12',
                      4: '2021-01',
                     'dni': 0: '00820054194',
                      1: '01717705014',
                      2: '02252584041',
                      3: '04902852446',
                      4: '04902852446',
                     'goalTrans': 0: 2.0, 1: 0.0, 2: 0.0, 3: 1.0, 4: 0.0,
                     'incomes': 0: 1.0, 1: 2.0, 2: 2.0, 3: 1.0, 4: 2.0,
                     'payments': 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 1.0,
                     'savings': 0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0)

fig = go.Figure()
buttons = []

frames = len(df_merged.dni.unique())
for i, dni in enumerate(sorted(df_merged.dni.unique()[:])):
    df = df_merged[df_merged['dni']==dni]
    print(dni)
    print(df)
    
    args_data = []
    bars = len(df.columns[3:-1])
    for c, column in enumerate(df.columns[3:-1]):
        fig.add_trace(go.Bar(
                        name = column,
                        x = pd.to_datetime(df.date.astype('str')),
                        y = df[column], 
                        visible=False
                      ))
    
    frames = len(df_merged.dni.unique())
    bars = len(df.columns[3:-1])
    scenarios = [list(s) for s in [e==1 for e in np.eye(frames)]]
    visibility = [list(np.repeat(e, bars)) for e in scenarios]   
        
    button =  dict(label=dni,
                   method = 'restyle',
                    args = ['visible',visibility[i]]
                  )
    
    buttons.append(button)
       
fig.update_layout(
    updatemenus=[
        dict(
        type="dropdown",
        direction="down",
        buttons = buttons)
    ],
    barmode = "stack",)
f = fig.full_figure_for_development(warn=False)

fig.show()

【讨论】:

以上是关于如何在 Plotly 中正确使用按钮过滤数据的主要内容,如果未能解决你的问题,请参考以下文章

Plotly:如何在将多个组作为条形图的同时显示和过滤具有多个下拉列表的数据框?

Plotly:如何使用类似于在图例中单击它们的按钮来切换轨迹?

python - 如何使用plotly python中的下拉按钮更新多个散点图的数据?

即使屏幕尺寸在 Dash-plotly 中使用 python 改变,如何在导航栏中固定按钮的位置

使用下拉菜单在 Plotly 中交互式过滤数据表

如何通过 Python 中的 Plotly 从 Dash 的下拉列表中选择数据集列?