如何在 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:])
)
})
我在下面的折线图中使用下拉菜单过滤状态。这里 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:])
)
})