【问题标题】:pandas GroupBy 在一个图上为每个组绘制两条线
【发布时间】:2022-01-19 12:49:28
【问题描述】:

我一直在努力将 GroupBy 的结果绘制在三列上。 我有 3 年 (MonthYear) 内员工 (Employee) 的不同缺勤 (AbsenceType) 的数据。我想在一个图中绘制一个员工在每个月每年有多少特定类型的缺勤。我在示例中只有两名员工,但数据中还有更多的员工以及更多的月-年值。

创建数据

data = {'Employee': ['ID1', 'ID1','ID1','ID1','ID1','ID1','ID1', 'ID1', 'ID1', 'ID2','ID2','ID2','ID2','ID2', 'ID2'],
'MonthYear': ['201708', '201601','201601','201708','201710','201801','201801', '201601', '201601', '201705', '201705', '201705', '201810', '201811', '201705'],
'AbsenceType': ['0210', '0210','0250','0215','0217','0260','0210', '0210', '0210', '0260', '0250', '0215', '0217', '0215', '0250']}

columns = ['Employee','MonthYear','AbsenceType']

df = pd.DataFrame(data, columns=columns)

然后我将 AbsenceType 的每个代码映射为两类:生病或受伤。

df['SickOrInjury'] =df['AbsenceType'].replace({'0210':'Sick', '0215':'Sick', '0217':'Sick', '0250':'Injury', '0260':'Injury'})

我想要实现的是以下groupby:

test = df.groupby(['Employee', 'MonthYear', 'SickOrInjury'])['SickOrInjury'].count()

但是,当我尝试绘制它时,它并没有完全显示我想要的。到目前为止,我设法登上了舞台:

df.groupby(['Employee', 'MonthYear', 'SickOrInjury'])['SickOrInjury'].count().unstack('SickOrInjury', fill_value=0).plot()
plt.show()

test plot 但是,员工 ID 显示在 X 轴上,而不是图例中。

我想要的是这样的: desired plot 我想在 X 轴上有时间,在 Y 轴上有每种缺勤类型(生病或受伤)的计数。每个缺勤类型应该有两种不同类型的线条(例如实线和虚线),每个员工应该有不同的颜色(例如黑色和红色)。

非常感谢任何建议。

【问题讨论】:

    标签: python pandas matplotlib group-by


    【解决方案1】:

    我认为取消堆叠是填充缺失值的正确方法,但您可能应该将MonthYear 转换为日期并按月重新采样。然后,您可以使用seaborn.lineplot 绘制数据框:

    import pandas as pd
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    data = {'Employee': ['ID1', 'ID1','ID1','ID1','ID1','ID1','ID1', 'ID1', 'ID1', 'ID2','ID2','ID2','ID2','ID2', 'ID2'],
    'MonthYear': ['201708', '201601','201601','201708','201710','201801','201801', '201601', '201601', '201705', '201705', '201705', '201810', '201811', '201705'],
    'AbsenceType': ['0210', '0210','0250','0215','0217','0260','0210', '0210', '0210', '0260', '0250', '0215', '0217', '0215', '0250']}
    
    columns = ['Employee','MonthYear','AbsenceType']
    
    df = pd.DataFrame(data, columns=columns)
    
    df['SickOrInjury'] = df['AbsenceType'].replace({'0210':'Sick', '0215':'Sick', '0217':'Sick', '0250':'Injury', '0260':'Injury'})
    df['MonthYear'] = pd.to_datetime(df['MonthYear'], format="%Y%m")
    df = df.groupby(['MonthYear', 'Employee', 'SickOrInjury']).count()
    
    # renaming the aggregated (and unique) column
    df = df.rename(columns={'AbsenceType': 'EmpAbsCount'})
    
    df = df.unstack(['Employee', 'SickOrInjury'], fill_value=0)
    # resampling for monthly values:
    df = df.resample('M').sum().stack(['Employee', 'SickOrInjury'])
    
    sns.lineplot(x='MonthYear', y='EmpAbsCount', data=df, hue='Employee', style='SickOrInjury', markers=True, ci=None)
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.show()
    

    输出:

    【讨论】:

    • 谢谢!那行得通,我无法将头绕在堆栈上并自己解开:)如果我使用 df = df.resample('D').sum().stack([ “员工”、“生病或受伤”])?
    • resample 也可以使用 'D' 作为参数,但您的数据在示例中没有日期信息...
    • 我尝试了这些天,但 resample('D') 似乎添加了没有数据的天数,并且只是用 SickOrInjury 的零值填充它。最后因为数据太多(3年),所以图不清楚,Ox上只显示年月(不是年月日)。
    猜你喜欢
    • 2019-06-12
    • 2018-05-13
    • 2015-01-10
    • 2021-11-20
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多