使用按钮在 plotly python 中过滤不同的数据

Use button to filter different data in plotly python

我遵循了@PythononToast 的回答 首先生成的图是正确的,但是单击下拉过滤器后值发生了变化。正如我们从他生成的情节中看到的那样是不正确的。我可以知道原因吗?

import pandas as pd
import plotly.graph_objects as go

#Dummy data
df_germany = pd.DataFrame({'Fuels':[2010,2011],'Coal':[200,250],'Gas':[400,500]})
df_poland = pd.DataFrame({'Fuels':[2010,2011],'Coal':[500,150],'Gas':[600,100]})
df_spain = pd.DataFrame({'Fuels':[2010,2011],'Coal':[700,260],'Gas':[900,400]})

#put dataframes into object for easy access:
df_dict = {'Germany': df_germany, 
           'Poland': df_poland, 
           'Spain': df_spain}

#create a figure from the graph objects (not plotly express) library
fig = go.Figure()

buttons = []
i = 0

#iterate through dataframes in dict
for country, df in df_dict.items():
    #iterate through columns in dataframe (not including the year column)
    for column in df.drop(columns=['Fuels']):
        #add a bar trace to the figure for the country we are on
        fig.add_trace(go.Bar(
                          name = column,
                          #x axis is "fuels" where dates are stored as per example
                          x = df.Fuels.to_list(),
                          #y axis is the data for the column we are on
                          y = df[column].to_list(),
                          #setting only the first country to be visible as default
                          visible = (i==0)
                        )
                     )
        
    #args is a list of booleans that tells the buttons which trace to show on click
    args = [False] * len(df_dict)
    args[i] = True
    
    #create a button object for the country we are on
    button = dict(label = country,
                  method = "update",
                  args=[{"visible": args}])
    
    #add the button to our list of buttons
    buttons.append(button)
    
    #i is an iterable used to tell our "args" list which value to set to True
    i+=1
        
fig.update_layout(
    updatemenus=[
        dict(
        #change this to "buttons" for individual buttons
        type="dropdown",
        #this can be "left" or "right" as you like
        direction="down",
        #(1,1) refers to the top right corner of the plot
        x = 1,
        y = 1,
        #the list of buttons we created earlier
        buttons = buttons)
    ],
    #stacked bar chart specified here
    barmode = "stack",
    #so the x axis increments once per year
    xaxis = dict(dtick = 1))

fig.show()

此问题的根源在于代码未正确创建 buttons 列表。如果在创建后打印此列表,您将得到以下内容:

[{'label': 'Germany', 'method': 'update', 'args': [{'visible': [True, False, False]}]},
 {'label': 'Poland', 'method': 'update', 'args': [{'visible': [False, True, False]}]},
 {'label': 'Spain', 'method': 'update', 'args': [{'visible': [False, False, True]}]}]

与键'visible'相对应的每个列表指示选择一个国家时应在图中显示哪些迹线。问题是嵌套的 for 循环总共创建了 6 条轨迹:每个国家的天然气和煤炭数据各一条。因此,分配给 'visible' 的列表应该包含 6 个布尔值:对于给定的国家/地区,它应该有两个 True 值,对应于该国家/地区的天然气和煤炭踪迹。换句话说,buttons 列表应该如下所示:

[{'label': 'Germany', 'method': 'update', 'args': [{'visible': [True, True, False, False, False, False]}]},
 {'label': 'Poland', 'method': 'update', 'args': [{'visible': [False, False, True, True, False, False]}]},
 {'label': 'Spain', 'method': 'update', 'args': [{'visible': [False, False, False, False, True, True]}]}]

下面是为解决此问题而修改的代码。它仅更改 args 列表在外部 for 循环内创建的方式,因为这是分配给 'visible'.

的列表
import pandas as pd
import plotly.graph_objects as go

#Dummy data
df_germany = pd.DataFrame({'Fuels':[2010,2011],'Coal':[200,250],'Gas':[400,500]})
df_poland = pd.DataFrame({'Fuels':[2010,2011],'Coal':[500,150],'Gas':[600,100]})
df_spain = pd.DataFrame({'Fuels':[2010,2011],'Coal':[700,260],'Gas':[900,400]})

#put dataframes into object for easy access:
df_dict = {'Germany': df_germany, 
           'Poland': df_poland, 
           'Spain': df_spain}

#create a figure from the graph objects (not plotly express) library
fig = go.Figure()

buttons = []
i = 0

n_cols = len(df_germany.columns) - 1

#iterate through dataframes in dict
for country, df in df_dict.items():

    #iterate through columns in dataframe (not including the year column)
    for column in df.drop(columns=['Fuels']):
        #add a bar trace to the figure for the country we are on
        fig.add_trace(go.Bar(
                          name = column,
                          #x axis is "fuels" where dates are stored as per example
                          x = df.Fuels.to_list(),
                          #y axis is the data for the column we are on
                          y = df[column].to_list(),
                          #setting only the first country to be visible as default
                          visible = (i==0)
                        )
                     )
        
    #args is a list of booleans that tells the buttons which trace to show on click
    args = [False] * len(df_dict)*(n_cols)
    args[i*n_cols:(i+1)*n_cols] = [True]*n_cols
    
    #create a button object for the country we are on
    button = dict(label = country,
                  method = "update",
                  args=[{"visible": args}])
    
    #add the button to our list of buttons
    buttons.append(button)
    
    #i is an iterable used to tell our "args" list which value to set to True
    i+=1
        
fig.update_layout(
    updatemenus=[
        dict(
        #change this to "buttons" for individual buttons
        type="dropdown",
        #this can be "left" or "right" as you like
        direction="down",
        #(1,1) refers to the top right corner of the plot
        x = 1,
        y = 1,
        #the list of buttons we created earlier
        buttons = buttons)
    ],
    #stacked bar chart specified here
    barmode = "stack",
    #so the x axis increments once per year
    xaxis = dict(dtick = 1))

fig.show()