【问题标题】:How to create grouped and stacked bars如何创建分组和堆叠的条形图
【发布时间】:2021-11-13 11:35:41
【问题描述】:

我有一个非常庞大的数据集,其中有很多子公司为不同国家的三个客户群提供服务,如下所示(实际上有更多的子公司和日期):

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({'subsidiary': ['EU','EU','EU','EU','EU','EU','EU','EU','EU','US','US','US','US','US','US','US','US','US'],'date': ['2019-03','2019-04', '2019-05','2019-03','2019-04', '2019-05','2019-03','2019-04', '2019-05','2019-03','2019-04', '2019-05','2019-03','2019-04', '2019-05','2019-03','2019-04', '2019-05'],'business': ['RETAIL','RETAIL','RETAIL','CORP','CORP','CORP','PUBLIC','PUBLIC','PUBLIC','RETAIL','RETAIL','RETAIL','CORP','CORP','CORP','PUBLIC','PUBLIC','PUBLIC'],'value': [500.36,600.45,700.55,750.66,950.89,1300.13,100.05,120.00,150.01,800.79,900.55,1000,3500.79,5000.36,4500.25,50.17,75.25,90.33]})
print(df)

我想通过生成堆积条形图对每个子公司进行分析。为此,我首先将 x 轴定义为唯一的月份,并在这样的国家/地区为每种业务类型定义一个子集:

x=df['date'].drop_duplicates() 
EUCORP = df[(df['subsidiary']=='EU') & (df['business']=='CORP')] 
EURETAIL = df[(df['subsidiary']=='EU') & (df['business']=='RETAIL')] 
EUPUBLIC = df[(df['subsidiary']=='EU') & (df['business']=='PUBLIC')] 

然后我可以为每种业务类型制作条形图:

plotEUCORP = plt.bar(x=x, height=EUCORP['value'], width=.35)
plotEURETAIL = plt.bar(x=x, height=EURETAIL['value'], width=.35)
plotEUPUBLIC = plt.bar(x=x, height=EUPUBLIC['value'], width=.35)

但是,如果我尝试将所有三个叠加在一个图表中,我总是失败:

plotEURETAIL = plt.bar(x=x, height=EURETAIL['value'], width=.35)
plotEUCORP = plt.bar(x=x, height=EUCORP['value'], width=.35, bottom=EURETAIL)
plotEUPUBLIC = plt.bar(x=x, height=EUPUBLIC['value'], width=.35, bottom=EURETAIL+EUCORP)
plt.show()

我总是收到以下错误消息:


ValueError:缺少 StrCategoryConverter 的类别信息;这可能是由于无意中混合了分类数据和数字数据造成的

ConversionError:无法将值转换为轴单位:子公司日期业务价值 0 欧盟 2019-03 零售 500.36 1 欧盟 2019-04 零售 600.45 2 欧盟 2019-05 零售 700.55


我尝试将月份转换为日期格式和/或对其进行索引,但它实际上让我更加困惑......

我非常感谢您在以下任何方面的任何帮助/支持,因为我已经花了很多时间来尝试解决这个问题(我仍然是 python 菜鸟,sry):

  1. 如何解决创建堆叠条形图的错误?
  2. 假设可以修复错误,这是创建条形图最有效的方法(例如,我真的需要为每个子公司创建三个 sub-dfs,还是有更优雅的方法?)
  3. 是否可以编写迭代代码,按国家/地区生成堆积条形图,这样我就不需要为每个子公司创建一个?

【问题讨论】:

    标签: python matplotlib seaborn bar-chart stacked-chart


    【解决方案1】:
    • 作为仅供参考,堆叠条形图并不是最佳选择,因为它们会使条形图值的比较变得困难,并且很容易被误解。可视化的目的是以易于理解的格式呈现数据;确保信息清晰。并排栏通常是更好的选择。
    • 并排堆叠条形是一个难以手动构建的过程,最好使用像 seaborn.catplot 这样的图形级方法,这将创建一个单一的、易于阅读的数据可视化。
    • 条形图刻度位于 0 索引范围(不是日期时间),日期只是标签,因此无需将它们转换为 datetime dtype
    • python 3.8.11pandas 1.3.2matplotlib 3.4.3seaborn 0.11.2中测试

    seaborn

    import seaborn as sns
    
    sns.catplot(kind='bar', data=df, col='subsidiary', x='date', y='value', hue='business')
    

    创建分组和堆叠的条形

    • Stacked Bar ChartGrouped bar chart with labels
    • 在 OP 中创建堆叠条的问题是 bottom 正在为该组的整个数据框设置,而不仅仅是构成条高度的值。
    • 我真的需要为每个子公司创建三个 sub-dfs。是的,每个组都需要一个DataFrame,在这种情况下是 6 个。
      • 可以使用dict-comprehension.groupby 对象解压缩到dict 中自动创建数据子集。
        • data = {''.join(k): v for k, v in df.groupby(['subsidiary', 'business'])} 创建 dictDataFrames
        • 访问如下值:data['EUCORP'].value
    • 自动化绘图创建更加艰巨,可以看出x 取决于每个刻度有多少条柱线组,bottom 取决于每个后续绘图的值。
    import numpy as np
    import matplotlib.pyplot as plt
    
    labels=df['date'].drop_duplicates()  # set the dates as labels
    x0 = np.arange(len(labels))  # create an array of values for the ticks that can perform arithmetic with width (w)
    
    # create the data groups with a dict comprehension and groupby
    data = {''.join(k): v for k, v in df.groupby(['subsidiary', 'business'])}
    
    # build the plots
    subs = df.subsidiary.unique()
    stacks = len(subs)  # how many stacks in each group for a tick location
    business = df.business.unique()
    
    # set the width
    w = 0.35
    
    # this needs to be adjusted based on the number of stacks; each location needs to be split into the proper number of locations
    x1 = [x0 - w/stacks, x0 + w/stacks]
    
    fig, ax = plt.subplots()
    for x, sub in zip(x1, subs):
        bottom = 0
        for bus in business:
            height = data[f'{sub}{bus}'].value.to_numpy()
            ax.bar(x=x, height=height, width=w, bottom=bottom)
            bottom += height
            
    ax.set_xticks(x0)
    _ = ax.set_xticklabels(labels)
    
    • 如您所见,小值很难辨别,使用 ax.set_yscale('log') 无法像预期的那样处理堆叠条(例如,它不会使小值更具可读性)。

    只创建堆叠条形

    • @r-beginners 所述,使用.pivot.pivot_table 将数据框重塑为宽形式,以创建x 轴为元组的堆叠条形('date''subsidiary')。
      • 如果每个类别没有重复值,请使用 .pivot
      • 如果存在必须与aggfunc 组合的重复值(例如'sum''mean' 等),请使用.pivot_table
    # reshape the dataframe
    dfp = df.pivot(index=['date', 'subsidiary'], columns=['business'], values='value')
    
    # plot stacked bars
    dfp.plot(kind='bar', stacked=True, rot=0, figsize=(10, 4))
    

    【讨论】:

      猜你喜欢
      • 2017-11-01
      • 2021-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-25
      • 2021-02-25
      相关资源
      最近更新 更多