如何在pandas中对多列分组进行求和或计数

4

我想将几组列分组以计算或汇总pandas数据框中的行。

我已经检查了很多问题,最相似的是这一个 > 在Python中对多个列进行分组求和和计数, 但是,根据我所理解的,我必须执行许多步骤才能达到我的目标。我还在看这个 链接

例如,我有以下数据框:

import numpy as np
df = pd.DataFrame(np.random.randint(0,5,size=(5, 7)), columns=["grey2","red1","blue1","red2","red3","blue2","grey1"])

     grey2   red1 blue1 red2 red3 blue2 grey1
0       4      3    0      2    4   0   2
1       4      2    0      4    0   3   1
2       1      1    3      1    1   3   1
3       4      4    1      4    1   1   1
4       3      4    1      0    3   3   1

我希望在这里按颜色对所有列进行分组,例如,我期望的是:

如果我求和这些数字,

blue  15
grey  22
red   34

如果我计算( x > 0 ),那么我会得到,
  blue  7
  grey  10
  red   13

这是我目前所取得的成果,现在我需要对它们进行总结,然后用结果创建一个数据框,但如果我有100个组,这将非常耗时。
pd.pivot_table(data=df, index=df.index, values=["red1","red2","red3"], aggfunc='sum', margins=True)
   red1  red2   red3
0    3     2    4
1    2     4    0
2    1     1    1
3    4     4    1
4    4     0    3
ALL  14   11    9

pd.pivot_table(data=df, index=df.index, values=["red1","red2","red3"], aggfunc='count', margins=True)

但这里也在计算零:
     red1 red2  red3
   0    1   1   1
   1    1   1   1
   2    1   1   1
   3    1   1   1
   4    1   1   1
  All   5   5   5

我不确定如何修改函数以获得我的结果,而且我已经花了好几个小时,希望你能帮忙。

注意: 在这个例子中,我只使用颜色来简化情况,但我可能有许多列,它们被称为col001到col300等等... 因此,这些组可能是:

blue = col131, col254, col005
red =  col023, col190, col053

等等……


df.groupby(df.columns.str.replace('\d+', ''),axis=1).sum().sum() - ansev
4个回答

3
你可以使用 pd.wide_to_long 函数:
data= pd.wide_to_long(df.reset_index(), stubnames=['grey','red','blue'], 
                i='index',
                j='group',
                sep=''
               )

输出:

# data
             grey  red  blue
index group                 
0     1       2.0    3   0.0
      2       4.0    2   0.0
      3       NaN    4   NaN
1     1       1.0    2   0.0
      2       4.0    4   3.0
      3       NaN    0   NaN
2     1       1.0    1   3.0
      2       1.0    1   3.0
      3       NaN    1   NaN
3     1       1.0    4   1.0
      2       4.0    4   1.0
      3       NaN    1   NaN
4     1       1.0    4   1.0
      2       3.0    0   3.0
      3       NaN    3   NaN

并且:

data.sum()
# grey    22.0
# red     34.0
# blue    15.0
# dtype: float64

data.gt(0).sum()
# grey    10
# red     13
# blue     7
# dtype: int64

更新 wide_to_long 只是将 mergerename 方便地缩短了。因此,如果您有一个字典 {cat:[col_list]},您可以解决这个问题:

groups = {'blue' : ['col131', 'col254', 'col005'],
          'red' : ['col023', 'col190', 'col053']}

# create the inverse dictionary for mapping
inv_group = {v:k for k,v in groups.items()}

data = df.melt()

# map the original columns to group
data['group'] = data['variable'].map(inv_group)

# from now on, it's similar to other answers
# sum
data.groupby('group')['value'].sum()

# count
data['value'].gt(0).groupby(data['group']).sum()

谢谢,我从未听说过这个函数,我只是用颜色来命名列,但如果列使用随机名称,我能在 stubnames 中使用字典吗? - VMEscoli

1
这里的复杂之处在于您想同时按行列进行折叠,这通常很难同时实现。我们可以使用melt将宽格式转换为长格式,然后将问题减少到单个groupby
# Get rid of the numbers + reshape
df.columns = pd.Index(df.columns.str.rstrip('0123456789'), name='color')
df = df.melt()

df.groupby('color').sum()
#       value
#color       
#blue      15
#grey      22
#red       34

df.value.gt(0).groupby(df.color).sum()
#color
#blue     7.0
#grey    10.0
#red     13.0
#Name: value, dtype: float64

如果名称不太容易分类,我们需要在某个地方进行映射,步骤非常相似:

# Unnecessary in this case, but more general
d = {'grey1': 'color_1', 'grey2': 'color_1', 
     'red1': 'color_2', 'red2': 'color_2', 'red3': 'color_2',
     'blue1': 'color_3', 'blue2': 'color_3'}

df.columns = pd.Index(df.columns.map(d), name='color')
df = df.melt()
df.groupby('color').sum()

#         value
#color         
#color_1     22
#color_2     34
#color_3     15

哇,看起来现在很简单,因为我只是用颜色来命名列,但如果列使用随机名称怎么办,也许我可以准备一个包含它们需要分组的方式的字典。例如: 蓝色=col001,col134,col567 红色=col876,col324,col9876 - VMEscoli
@VMEscoli 上面的代码仍然会将这三个列名分组在一起,因为 rstrip 会将右侧所有数字都去掉,所以这三个列名都会变成 'col'。但是,如果你需要对 col001、col134、col56 进行分组,然后对 col002、col007、col131 进行分组,那么我的方法就不适用了。在这种情况下,你需要准备一个字典 d ={'col001': 'label1', 'col134': 'label1', ...},然后将第一步替换为 = ... df.columns.map(d) ...。如果真的没有简单的模式可以进行分组,那么你就必须手动编写字典了。 - ALollz

0

使用:

df.groupby(df.columns.str.replace('\d+', ''),axis=1).sum().sum()

输出:

blue    15
grey    22
red     34
dtype: int64

这个方法可以适用于列名中包含任意位数数字的情况:

df=df.add_suffix('22')
print(df)

   grey22222  red12222  blue12222  red22222  red32222  blue22222  grey12222
0          4         3          0         2         4          0          2
1          4         2          0         4         0          3          1
2          1         1          3         1         1          3          1
3          4         4          1         4         1          1          1
4          3         4          1         0         3          3          1

df.groupby(df.columns.str.replace('\d+', ''),axis=1).sum().sum()
blue    15
grey    22
red     34
dtype: int64

0

对于一般情况,您也可以这样做:

colors = {'blue':['blue1','blue2'], 'red':['red1','red2','red3'], 'grey':['grey1','grey2']}
orig_columns = df.columns
df.columns = [key for col in df.columns for key in colors.keys() if col in colors[key]]
print(df.groupby(level=0,axis=1).sum().sum())
df.columns = orig_columns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接