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

Posted

技术标签:

【中文标题】Plotly:如何使用类似于在图例中单击它们的按钮来切换轨迹?【英文标题】:Plotly: How to toggle traces with a button similar to clicking them in legend? 【发布时间】:2021-05-02 14:13:18 【问题描述】:

我正在使用 python 并创建带有交互式绘图的独立 html 文件(没有 Dash)。我已经能够构建一个带有按钮的绘图图,这些按钮可以切换图中迹线的可见性。但是,此功能也会从图例中删除痕迹。我想要的是能够保留图例的功能(单击单个跟踪以切换可见性),但也有一组按钮可以将该功能扩展到我定义的一组跟踪。

目标是能够将所有内容(或选择的组)切换为不可见,但根据需要将该组中的单个项目添加回可见。

下面是一个示例(使用来自 vestland 的 this answer 的修改代码)来展示我目前正在尝试的内容。

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()

该示例无法正常工作。下面是它最初的样子的屏幕截图。

问题在于,如果我使用其中一个按钮,它会隐藏所有其他痕迹,但也会将它们从图例中删除,因此无法将它们切换回可见状态。

所以我的问题变成了,args 列表/字典中是否有不同的值可以赋予功能以匹配只需单击图例中的跟踪的功能?

有点相关,有什么方法可以获取每个跟踪的当前可见性状态吗?

【问题讨论】:

【参考方案1】:

经过一番体面的搜索,感谢 Plotly 论坛上的this answer,我已经能够弄清楚了。我还没有找到列出所有这些选项的地方,但这会很有帮助。

似乎在 args 字典中给予“可见”的列表不需要只是布尔值。为了使项目在图例中可见但在图中隐藏,您需要将值设置为“legendonly”。然后仍然可以单击图例条目来切换单个可见性。这回答了我问题的主旨。

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

Vestland 的回答帮助解决了我的问题的第二部分,只修改了我想要的痕迹,而其他一切都保持不变。事实证明,您可以将字典后的索引列表传递给 args,而这些 args 将仅适用于提供的索引处的跟踪。我在示例中使用列表理解来查找与给定名称匹配的跟踪。我还为每一列添加了另一个跟踪,以显示这对多个跟踪是如何工作的。

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

下面是现在工作的代码。

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()

这提供了以下功能:

“全部”按钮可以切换所有跟踪的可见性。

每个其他按钮只会切换具有匹配名称的跟踪。这些痕迹在图例中仍然可见,并且可以通过在图例中单击它们或再次单击按钮来恢复可见。

点击“B”按钮后(两次点击 arg2)。

然后单击图例中的第一个 B 迹线后。

【讨论】:

【参考方案2】:

为了能够在不影响其他跟踪的情况下打开和关闭任何跟踪,看来您必须为每个按钮添加一个更新菜单。可能还有其他方法可以做到这一点,但下面的代码 sn-p 将产生以下情节:

情节 1 - 启动时全部被选中

情节 2 - CD 关闭

情节 3 - 全部 off

情节 4 - 全部 on

完整代码:

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()

【讨论】:

以上是关于Plotly:如何使用类似于在图例中单击它们的按钮来切换轨迹?的主要内容,如果未能解决你的问题,请参考以下文章

自定义外观 plotly.js 图例

plotly:单击图例中的点时突出显示(暗淡),而不是过滤

如何在 Plotly choropleth 地图上创建符号/按钮

类似于在 jQuery 中浏览文件的东西

如何在 Python 中使用 Plotly Express 仅显示图例的子集(plotly.express,px)?

Plotly:图例中的组跟踪并删除水平标签