如何在 Plotly 中实现下拉菜单以重新更新多个图而不是单个图

How to implement dropdown in Plotly that will reupdate multiple graph instead of single graph

我在下面的折线图中使用下拉菜单过滤状态。这里 d 是数据帧,其中有 16 个。在每个数据框中,我都试图将周绘制为 x 轴,将其他值绘制为 y 轴或每个图中的轨迹。到目前为止,我能够获得每 16 个图表的下拉菜单,这将只更新给定的图表。相反,我希望单个下拉值根据在下拉列表中选择的状态更新所有 16 个图表。请参阅所附图片。

This is the image which shows dropdown for every graph

这是我的代码:

state_list = list(df['GeogName'].unique())
for item in d.values(): #d is dictionary which has 16 dataframe as value, item is datafrme here.
    fig=go.Figure()
    state_plot_names = []
    buttons=[]
    default_state = "NY"

    for state_name in state_list:
        state_df = item[item["GeogName"]== region_name]
        for i in range(5): #adding 5 other traces as value in graph
            fig.add_trace(go.Scatter(x=state_df['Week'], y =state_df.iloc[:,i], line={}, visible=(state_name==default_state)))
        state_plot_names.extend([state_name])
    
    for state_name in state_list :
        buttons.append(dict(method='update',
                        label=state_name ,
                        args = [{'visible': [state_name ==s for s in state_plot_names]}]))


    fig.update_layout(autosize=False,
    width=1000,
    height=700,showlegend=True, updatemenus=[{"buttons": buttons, "direction": "down", "active": state_list.index(default_state), "showactive": True, "x": 0.5, "y": 1.15}])
    fig.show()
  • 已经综合了您在评论中描述的数据框的 dict,考虑到每个数据框的值列名称都不同
  • 如前所述,下拉菜单只能对单个图形进行操作。因此唯一的选择是移动到 subplots
  • 实现这一点的最简单方法是将所有数据帧结构化为一个数据帧,该数据帧可用作 Plotly Express
  • 的输入
  • 现在可以创建一个 *多面体(子图)图形
  • 首先将默认状态 (NY) 设置为仅可见痕迹
  • 构建下拉菜单以控制其他状态选择的可见性
  • 子图 之间的间距做了一些工作,因为 16 会导致很多白色 space
import numpy as np
import pandas as pd
import plotly.express as px

# replicate data described in question
states = ["NY", "NH", "IN", "CT", "MT", "VA", "ME", "SD", "KS", "MN"]
sigma = 0.01
d = {}
for di in range(16):
    df = pd.DataFrame()
    for s, (state, mu) in enumerate(
        zip(states, np.random.uniform(0.001, 0.005, len(states)))
    ):
        np.random.seed((s + 1) * di)
        start_price = np.random.uniform(1, 5, [5, 1])
        returns = np.random.normal(loc=mu, scale=sigma, size=100)
        df = pd.concat(
            [
                df,
                pd.DataFrame(
                    (start_price * (1 + returns).cumprod()).T,
                    columns=[f"value{di}_{n}" for n in range(5)],
                )
                .assign(GeogName=state)
                .reset_index()
                .rename(columns={"index": "Week"}),
            ]
        )
    d[chr(ord("A") + di)] = df

# integrate all dataframes so it's simple to use plotly express
# encode column name and GeogName into single column for definition of trace
df = pd.concat(
    [
        df.set_index(["Week", "GeogName"])
        .stack()
        .reset_index()
        .rename(columns={"level_2": "trace", 0: "value"})
        .assign(dataframe=k, trace=lambda d: d["GeogName"]+d["trace"])
        for k, df in d.items()
    ]
)

fig = px.line(
    df,
    x="Week",
    y="value",
    color="trace",
    facet_row="dataframe",
)

# default state...
fig.for_each_trace(lambda t: t.update(visible=(t.name[0:2] == "NY")))

fig.update_layout(
    updatemenus=[
        {
            "buttons": [
                {"label": state, "method": "restyle", "args": [{"visible":[t.name[0:2]==state for t in fig.data]}]}
                for state in df["GeogName"].unique()
            ],
            "y":1.01,
            "x":.5
        }
    ],
    autosize=False,
    height=1500
)

# compress up space between subplots
fig.update_layout({
    f"yaxis{'' if axis==0 else axis+1}": {"domain": [s, e-.002]}
    for axis, (s, e) in enumerate(
        zip(np.linspace(0, 1, len(d.keys())+1), np.linspace(0, 1, len(d.keys())+1)[1:])
    )
})