有条件地聚合数据,同时按站点分组

Conditionally aggregating data while also grouping by site

提问人:BMcBee 提问时间:11/8/2023 更新时间:11/8/2023 访问量:31

问:

我正在使用一个大型案例级数据集,并且是 python 的新手。数据集通常如下所示:

Site          Type          Value
A              Red            10
A              Blue           15
B              Red            35
B              Yellow          5
C              Blue           45
C              Red            25

我正在尝试使用 python 自动化我们的一些流程,其中包括聚合数据并按站点对其进行分组。我需要的输出如下所示:

Site         RedType    BlueType   YellowType    Value0-20    Value20-40    Value40+
A               1           1          0              2            0           0
B               1           0          1              1            1           0
C               1           1          0              0            1           1

基于我对我目前所做的事情的有限理解,我相信我偶然发现的解决方案是创建一个字典,其中包含我需要的每个列的字段,并将其应用为按站点分组的一部分:

def my_agg(data):
    names = {
        'RedType': data[data['Type']=="Red"]['Id'].count(),
        'BlueType': data[data['Type']=="Blue"]['Id'].count(),
        'YellowType':data[data['Type']=="Yellow"]['Id'].count(),
        'Value0-20': data[data['Value']>=0]['Id'].count() and data[data['Value']<=20]['Id'].count()
        etc........
    return pd.Series(names)

df = data.groupby('Site').apply(my_agg)

我现在知道 and 运算符不适用于值字段,但到目前为止其余的都有效。有没有办法使用此方法有条件地计算值(计算两个不同值之间的每个情况)?无论哪种方式,我是否忽略了我可能这样做的其他方式?

Python Pandas 数据帧 分组聚合

评论


答:

2赞 mozway 11/8/2023 #1

使用 cut 计算值的条柱,然后熔化 Type/Value 列,最后使用交叉表进行计数并展平 MultiIndex:

tmp = (df.assign(Value=lambda d: pd.cut(d['Value'],
                                        bins=[0, 20, 40, np.inf],
                                        labels=['0-20', '20-40', '40+']))
         
          .melt('Site')
      )

out = pd.crosstab(tmp['Site'], [tmp['variable'], tmp['value']])

out.columns = out.columns.map(lambda x: f'{x[0]}_{x[1]}')

out = out.reset_index()

或者,使用循环和 concat

index = 'Site'

# the following command modifies the original input in place
df['Value'] = pd.cut(df['Value'],
                     bins=[0, 20, 40, np.inf],
                     labels=['0-20', '20-40', '40+'])

out = pd.concat([pd.crosstab(df[index], df[col]).add_prefix(f'{col}_')
                 for col in df.columns.difference([index])],
                axis=1).reset_index()

输出:

  Site  Type_Blue  Type_Red  Type_Yellow  Value_0-20  Value_20-40  Value_40+
0    A          1         1            0           2            0          0
1    B          0         1            1           1            1          0
2    C          1         1            0           0            1          1