代码之家  ›  专栏  ›  技术社区  ›  user2731076

Plotly Python按钮,用于切换轨迹,类似于在图例中单击轨迹

  •  0
  • user2731076  · 技术社区  · 4 年前

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

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

    下面是一个示例(使用修改后的代码 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()
    

    这个例子不符合我的意愿。下面是它最初看起来的屏幕截图。

    stockplot

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

    onlyBvisible

    所以我的问题是,args列表/字典中是否有不同的值,可以为功能提供与简单单击图例中的跟踪相匹配的值?

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

    0 回复  |  直到 4 年前
        1
  •  10
  •   vestland    4 年前

    为了在不影响其他跟踪的情况下打开和关闭任何跟踪,似乎每个按钮都必须包含一个更新菜单。可能还有其他方法可以做到这一点,但下面的代码片段将生成以下图:

    图1-启动时,所有选项均已选定

    enter image description here

    地块2- C D 已关闭

    enter image description here

    图3-全部 off

    enter image description here

    图4-全部 on

    enter image description here

    完整代码:

    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()
    
        2
  •  9
  •   user2731076    4 年前

    经过一番搜索,我终于找到了答案,这要归功于 this answer 在Plotly论坛上。我还没有找到列出所有这些选项的地方,但这将非常有帮助。

    args字典中给“visible”的列表似乎不需要只是布尔值。为了使项目在图例中可见但在绘图中隐藏,您需要将值设置为“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()
    

    这提供了以下功能:

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

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

    starting plot with all traces visible

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

    plot after clicking B button twice

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

    plot after enabling first B trace again