Plotly:如何在将多个组作为条形图的同时显示和过滤具有多个下拉列表的数据框?

Posted

技术标签:

【中文标题】Plotly:如何在将多个组作为条形图的同时显示和过滤具有多个下拉列表的数据框?【英文标题】:Plotly: How to display and filter a dataframe with multiple dropdowns while having multiple groups as bargraph? 【发布时间】:2021-07-28 11:57:47 【问题描述】:

Plotly: How to display and filter a dataframe with multiple dropdowns?

数据集 = https://community.tableau.com/s/question/0D54T00000CWeX8SAL/sample-superstore-sales-excelxls

嘿,我也是 plotly 的新手,继续回答这个问题。我的情况类似,但有额外的条件。我需要绘制按“年份”和“类别”过滤的每个地区每个季度的总销售额。我能够重现文章中的折线图。但未能重现条形图。一年有4个季度和4个地区。因此,必须始终存在至少 16 个条形图。

这就是我想要构建的: enter image description here

import plotly.graph_objs as go
import pandas as pd
import numpy as np

file = pd.read_excel(r"Sample - Superstore.xlsx")
sales = file[['Sales','Region', 'Order Date','Category', 'State']]
sales["Quarters"] = sales['Order Date'].apply(lambda x: x.quarter)
sales["Years"] = sales['Order Date'].apply(lambda x: x.year)
df = sales.groupby(['Years','Quarters', 'Region', 'Category'], as_index = False).sum()
df_input = df.copy()

years = df['Years'].unique().tolist()
categories = df['Category'].unique().tolist()
regions = df['Region'].unique().tolist()
quarters = df['Quarters'].unique().tolist()

dfs = 
for year in years:
    dfs[year]=pd.pivot_table(df[df['Years']==year],
                                    values='Sales',
                                    index=['Quarters','Region'],
                                    columns=['Category'],
                                    aggfunc=np.sum)

# find row and column unions
common_cols = []
common_rows = []
for df in dfs.keys():
    common_cols = sorted(list(set().union(common_cols,list(dfs[df]))))
    common_rows = sorted(list(set().union(common_rows,list(dfs[df].index))))
    
df_common = pd.DataFrame(np.nan, index=common_rows, columns=common_cols)

# reshape each dfs[df] into common dimensions
dfc=
for df_item in dfs:
    #print(dfs[unshaped])
    df1 = dfs[df_item].copy()
    s=df_common.combine_first(df1)
    df_reshaped = df1.reindex_like(s)
    dfc[df_item]=df_reshaped

# plotly start 
fig = go.Figure()

# for year in all_years:
#     df2 = group_sales.loc[group_sales["Years"] == year]
#     all_quarters =list(sorted(set(df2["Quarters"].astype(str))))
#     all_regions =list(sorted(set(df2["Region"].astype(str))))
    
#     fig.add_trace(go.Bar(x= all_quarters, y=df2.loc[df2["Region"] == all_regions[0]]["Sales"], name=all_regions[0],marker_color='blue',  visible=(year== default_year)))
#     fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[1]]["Sales"], name=all_regions[1], marker_color='lightblue', visible=( year == default_year)))
#     fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[2]]["Sales"],name= all_regions[2],marker_color='grey', visible=(year== default_year)))
#     fig.add_trace(go.Bar(x= all_quarters, y= df2.loc[df2["Region"] == all_regions[3]]["Sales"],name=all_regions[3], marker_color='red', visible=(year== default_year)))
#     year_plot_names.extend([year]*4)

print(common_cols)

for col in common_cols:
#     fig.add_trace(go.Bar(x= all_quarters, y=df2.loc[df2["Region"] == all_regions[0]]["Sales"], name=all_regions[0],marker_color='blue',  visible=(year== default_year)))
    fig.add_trace(go.Bar(x= quarters, name= regions[0],marker_color='blue',  visible= True))
    fig.add_trace(go.Bar(x= quarters, name= regions[1],marker_color='lightblue',  visible= True))
    fig.add_trace(go.Bar(x= quarters, name= regions[2],marker_color='grey',  visible= True))
    fig.add_trace(go.Bar(x= quarters, name= regions[3],marker_color='red',  visible= True))
    
#     fig.add_trace(go.Bar(x= regions,marker_color='blue',  visible= True))

#     fig.add_trace(go.Scatter(x=regions,
#                              visible=True,
#                              marker=dict(size=12, line=dict(width=2)),
#                              marker_symbol = 'diamond',name=col
#                   )
#              )

fig.show()
# menu setup    
updatemenu= []

# buttons for menu 1, names
buttons=[]

# create traces for each color: 
# build argVals for buttons and create buttons
for df in dfc.keys():
    argList = []
    
    for col in dfc[df]:
        temp = []
        j = 0

#         for i in range(0,4):
#             temp2 = []
#             for i in range(0,4):
#                 temp2.append(dfc[df][col].values[j])
#                 j+=1
#             temp.append(temp2)
#         argList.append(temp)
        print(dfc[df][col])
        argList.append(dfc[df][col].values)
    argVals = [ 'y':argList]

    buttons.append(dict(method='update',
                        label=df,
                        visible=True,
                        args=argVals))
print(buttons)

# buttons for menu 2, colors
b2_labels = common_cols

# matrix to feed all visible arguments for all traces
# so that they can be shown or hidden by choice
b2_show = [list(b) for b in [e==1 for e in np.eye(len(b2_labels))]]
buttons2=[]
buttons2.append('method': 'update',
                 'label': 'All',
                 'args': ['visible': [True]*len(common_cols)])

# create buttons to show or hide
for i in range(0, len(b2_labels)):
    buttons2.append(dict(method='update',
                        label=b2_labels[i],
                        args=['visible':b2_show[i]]
                        )
                   )

# add option for button two to hide all
buttons2.append(dict(method='update',
                        label='None',
                        args=['visible':[False]*len(common_cols)]
                        )
                   )

# some adjustments to the updatemenus
updatemenu=[]
your_menu=dict()
updatemenu.append(your_menu)
your_menu2=dict()
updatemenu.append(your_menu2)
updatemenu[1]
updatemenu[0]['buttons']=buttons
updatemenu[0]['direction']='down'
updatemenu[0]['showactive']=True
updatemenu[1]['buttons']=buttons2
updatemenu[1]['y']=0.6

fig.update_layout(showlegend=False, updatemenus=updatemenu)
fig.update_layout(yaxis=dict(range=[0,df_input['Sales'].max()+0.4]))

# title
fig.update_layout(
    title=dict(
        text= "<i>Filtering with multiple dropdown buttons</i>",
        font='size':18,
        y=0.9,
        x=0.5,
        xanchor= 'center',
        yanchor= 'top'))

# button annotations
fig.update_layout(
    annotations=[
        dict(text="<i>Year</i>", x=-0.4, xref="paper", y=1.1, yref="paper",
            align="left", showarrow=False, font = dict(size=16, color = 'steelblue')),
        dict(text="<i>Category</i>", x=-0.4, xref="paper", y=0.7, yref="paper",
            align="left", showarrow=False, font = dict(size=16, color = 'steelblue')

                             )
    ])

fig.show()

【问题讨论】:

【参考方案1】: 我尝试使用 plotly 按钮来做到这一点,但是让两个按钮与静态定义的菜单一起使用具有挑战性 如果 dashplotly 一起使用会变得非常简单,因为现在有 callback 功能使需求变得简单。
import pandas as pd
import itertools
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

# Load Data
dfraw = pd.read_excel("Sample - Superstore.xls")

df = (dfraw.loc[:,['Sales','Region', 'Order Date','Category', 'State']]
      .assign(Quarters=dfraw["Order Date"].dt.quarter,
             Years=dfraw["Order Date"].dt.year)
      .groupby(['Years','Quarters', 'Region', 'Category'], as_index=False).sum()
     )
# add in totals with category "All"
df = (pd.concat([df, df.groupby(["Years","Quarters","Region"], as_index=False).agg("Sales":"sum").assign(Category="All")])
 .set_index(['Years', 'Region', 'Category','Quarters'])
 .unstack("Region")
 .droplevel(0, axis=1)
)

# colors...
config = 'Central':"blue", 'East':"lightblue", 'South':"grey", 'West':"red"

fig = go.Figure()
# create a trace for every year, category & region
for y,cat in itertools.product(df.index.get_level_values("Years").unique(),df.index.get_level_values("Category").unique()):
    dff = df.loc[(y,cat)]
    for region in dff.columns:
        fig.add_trace(go.Bar(x=dff.index, y=dff[region], name=region, meta=f"ycat", marker_color=config[region], 
                             visible=(y==2014 and cat=="All"),
                             text=dff[region].apply(lambda v: f"v/10**3:.0fk"),textposition="outside")
                            )

# Build App
app = JupyterDash(__name__)
app.layout = html.Div([
    dcc.Graph(id='graph'),
    html.Label(["Year",dcc.Dropdown(id='year-dropdown', clearable=False,
                                    value='2014', options=['label': year, 'value': year
                                                           for year in df.index.get_level_values("Years").unique()])
    ]),
    html.Label([
        "Category",dcc.Dropdown(id='cat-dropdown', clearable=False, value='All', 
                                options=['label': year, 'value': year
                                         for year in df.index.get_level_values("Category").unique()])
    ]),
])
# Define callback to update graph
@app.callback(
    Output('graph', 'figure'),
    [Input("year-dropdown", "value"),Input("cat-dropdown", "value")]
)
def update_figure(year, cat):
    return fig.update_traces(visible=False).update_traces(visible=True, selector="meta":f"yearcat").update_layout(title=f"year cat")
    
# Run app and display result inline in the notebook
app.run_server(mode='inline')

【讨论】:

以上是关于Plotly:如何在将多个组作为条形图的同时显示和过滤具有多个下拉列表的数据框?的主要内容,如果未能解决你的问题,请参考以下文章

Plotly:如何重命名 plotly express 堆积条形图的图例元素?

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

如何实现条形图的日期范围

Plotly 不能很好地处理条形图的 NA。我该在哪里解决这个问题?

R Shiny中的Plotly分组条形图

Plotly Python:在具有多个 Y 轴的分组条形图中对齐 X 轴