简体   繁体   English

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

[英]Plotly: How to toggle traces with a button similar to clicking them in legend?

I'm using python and creating standalone html files with interactive plots (no Dash).我正在使用 python 并创建带有交互式绘图(无破折号)的独立 html 文件。 I have been able to build a plotly plot with buttons that can toggle the visibility of traces in the plot.我已经能够构建一个 plotly plot 按钮,可以切换 plot 中迹线的可见性。 However, this functionality removes the traces from the legend as well.但是,此功能也会从图例中删除痕迹。 What I would like is to be able to keep the functionality of the legend (click a single trace to toggle visibility) but also have a set of buttons that extends that functionality to a group of traces that I define.我想要的是能够保留图例的功能(单击单个跟踪以切换可见性),但也有一组按钮将该功能扩展到我定义的一组跟踪。

The goal is to be able toggle everything (or a select group) to invisible but add individual items from that group back to visible as needed.目标是能够将所有内容(或 select 组)切换为不可见,但根据需要将该组中的单个项目添加回可见。

Below is an example (using modified code from this answer by vestland) to show what I am currently attempting.下面是一个示例(使用来自 vestland 这个答案的修改代码)来显示我目前正在尝试的内容。

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import datetime

# mimic OP's datasample

NPERIODS = 200

np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(NPERIODS, 4)),
                  columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),
                         periods=NPERIODS).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum()

# set up multiple traces
traces = []
buttons = []
for col in df.columns:
    traces.append(go.Scatter(x=df.index,
                            y=df[col],
                            visible=True,
                            name=col)
                )
    buttons.append(dict(method='update',
                        label=col,
                        visible=True,
                        args=[{'visible':[x == col for x in df.columns]}],
                        args2=[{'visible':[x != col for x in df.columns]}]
                        )
                )

# create the layout 
layout = go.Layout(
    updatemenus=[
        dict(
            type='buttons',
            direction='right',
            x=0.7,
            y=1.3,
            showactive=True,
            buttons=buttons
        )
    ],
    title=dict(text='Toggle Traces',x=0.5),
    showlegend=True
)

fig = go.Figure(data=traces,layout=layout)

# add dropdown menus to the figure
fig.show()

That example does not work how I would like.该示例无法按我的意愿工作。 Below is a screenshot of what it looks like at first.下面是它最初的样子的屏幕截图。

库存图

The problem is that if I use one of those buttons, it does hide all the other traces but it also removes them from the legend so they can't be toggled back to visible.问题是,如果我使用其中一个按钮,它会隐藏所有其他痕迹,但也会将它们从图例中删除,因此无法将它们切换回可见状态。

只见

So my question becomes, is there a different value in the args list/dictionary that can be given for the functionality to match that of simply clicking a trace in the legend?所以我的问题变成了,在 args 列表/字典中是否有不同的值可以提供与简单地单击图例中的跟踪相匹配的功能?

Sort of related, is there some way to get the current state of visibility for each trace?有点相关,有什么方法可以让每个跟踪的当前 state 可见性?

In order to make it possible to toggle any trace on and off without affecting the others, it seems you'll have to include one updatemenu per button.为了能够在不影响其他跟踪的情况下打开和关闭任何跟踪,看来您必须为每个按钮包含一个更新菜单。 There might be other ways to do it, but the code snippet below will produce the following plot:可能还有其他方法可以做到这一点,但下面的代码片段将生成以下 plot:

Plot 1 - At launch all are selected Plot 1 - 启动时全部选中

在此处输入图像描述

Plot 2 - C and D toggled off Plot 2 - CD关闭

在此处输入图像描述

Plot 3 - All off Plot 3 - 全部off

在此处输入图像描述

Plot 4 - All on Plot 4 - 全部on

在此处输入图像描述

Complete code:完整代码:

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import datetime
import plotly.express as px

periods = 200
cols = list('ABCD')

np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(periods, len(cols))),
                  columns=cols)
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),
                         periods=periods).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum()

# # plotly
fig = go.Figure()
colors = px.colors.qualitative.Plotly

# set up multiple traces
for col in df.columns:
    fig.add_trace(go.Scatter(x=df.index,
                             y=df[col],
                             name  = col,
                             visible=True
                            )
                 )

um = [ {} for _ in range(len(df.columns)) ]
buttons = []
menuadjustment = 0.15

buttonX = -0.1
buttonY = 1 + menuadjustment
for i, col in enumerate(df.columns):
    button = dict(method='restyle',
                  label=col,
                  visible=True,
                  args=[{'visible':True,
                         'line.color' : colors[i]}, [i]],
                  args2 = [{'visible': False,
                            'line.color' : colors[i]}, [i]],
                 )
    
    # adjust some button features
    buttonY = buttonY-menuadjustment
    um[i]['buttons'] = [button]
    um[i]['showactive'] = False
    um[i]['y'] = buttonY
    um[i]['x'] = buttonX

# add a button to toggle all traces on and off
button2 = dict(method='restyle',
               label='All',
               visible=True,
               args=[{'visible':True}],
               args2 = [{'visible': False}],
               )
# assign button2 to an updatemenu and make some adjustments
um.append(dict())
um[i+1]['buttons'] = [button2]
um[i+1]['showactive'] = True
um[i+1]['y']=buttonY - menuadjustment
um[i+1]['x'] = buttonX
    
# add dropdown menus to the figure
fig.update_layout(showlegend=True, updatemenus=um)

# adjust button type
for m in fig.layout.updatemenus:
    m['type'] = 'buttons'

f = fig.full_figure_for_development(warn=False)
fig.show()

After a decent bit of searching, I have been able to figure it out thanks to this answer on the Plotly forum.经过一番体面的搜索,由于 Plotly 论坛上的这个答案,我已经能够弄清楚了。 I have not been able to find somewhere that lists all of these options yet, but that would be very helpful.我还没有找到列出所有这些选项的地方,但这会很有帮助。

It appears that the list given to 'visible' in the args dictionary does not need to be only booleans.似乎在 args 字典中给予“可见”的列表不需要只是布尔值。 In order to keep the items visible in the legend but hidden in the plot, you need to set the values to 'legendonly'.为了使项目在图例中可见但隐藏在 plot 中,您需要将值设置为“legendonly”。 The legend entries can then still be clicked to toggle individual visibility.然后仍然可以单击图例条目来切换单个可见性。 That answers the main thrust of my question.这回答了我问题的主旨。

args = [{'visible': True}]
args = [{'visible': 'legendonly'}]
args = [{'visible': False}]

Vestland's answer helped solve the second part of my question, only modifying the traces I want and leaving everything else the same. Vestland 的回答帮助解决了我的问题的第二部分,只修改了我想要的痕迹,而其他一切都保持不变。 It turns out that you can pass a list of indices after the dictionary to args and those args will only apply to the traces at the indices provided.事实证明,您可以将字典后的索引列表传递给 args,而这些 args 将仅适用于提供的索引处的跟踪。 I used list comprehension in the example to find the traces that match the given name.我在示例中使用列表理解来查找与给定名称匹配的跟踪。 I also added another trace for each column to show how this works for multiple traces.我还为每一列添加了另一个跟踪,以显示这对多个跟踪是如何工作的。

args = [{'key':arg}, [list of trace indices to apply key:arg to]]

Below is the now working code.下面是现在工作的代码。

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import datetime

# mimic OP's datasample

NPERIODS = 200

np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(NPERIODS, 4)),
                  columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),
                         periods=NPERIODS).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum()

# set up multiple traces
traces = []
buttons = []
for col in df.columns:
    traces.append(go.Scatter(x=df.index,
                            y=df[col],
                            visible=True,
                            name=col)
                )
    traces.append(go.Scatter(x=df.index,
                y=df[col]+20,
                visible=True,
                name=col)
    )
    buttons.append(dict(method='restyle',
                        label=col,
                        visible=True,
                        args=[{'visible':True},[i for i,x in enumerate(traces) if x.name == col]],
                        args2=[{'visible':'legendonly'},[i for i,x in enumerate(traces) if x.name == col]]
                        )
                )

allButton = [
    dict(
        method='restyle',
        label=col,
        visible=True,
        args=[{'visible':True}],
        args2=[{'visible':'legendonly'}]
    )
]

# create the layout 
layout = go.Layout(
    updatemenus=[
        dict(
            type='buttons',
            direction='right',
            x=0.7,
            y=1.3,
            showactive=True,
            buttons=allButton + buttons
        )
    ],
    title=dict(text='Toggle Traces',x=0.5),
    showlegend=True
)

fig = go.Figure(data=traces,layout=layout)

# add dropdown menus to the figure
fig.show()

This gives the following functionality:这提供了以下功能:

the "All" button can toggle visibility of all traces. “全部”按钮可以切换所有迹线的可见性。

Each other button will only toggle the traces with the matching name.每个其他按钮只会切换具有匹配名称的跟踪。 Those traces will still be visible in the legend and can be turned back to visible by clicking on them in the legend or clicking the button again.这些痕迹在图例中仍然可见,并且可以通过在图例中单击它们或再次单击按钮来恢复可见。

开始绘图,所有痕迹都可见

After clicking the "B" button (twice to hit arg2).单击“B”按钮后(两次点击 arg2)。

单击 B 按钮两次后绘图

And then after clicking the first B trace in the legend.然后单击图例中的第一个 B 迹线后。

再次启用第一个 B 跟踪后绘图

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM