在使用条件聚合进行分组时选择多个第 n 个值 - 熊猫

Posted

技术标签:

【中文标题】在使用条件聚合进行分组时选择多个第 n 个值 - 熊猫【英文标题】:select multiple nth values in grouping with conditional aggregate - pandas 【发布时间】:2020-10-09 06:55:54 【问题描述】:

我有一个四列的 pd.DataFrame

    df = pd.DataFrame('id':[1,1,1,1,1,2,2,2,2] 
                      , 'A':['H','H','E','E','H','E','E','H','H']
                      , 'B':[4,5,2,7,6,1,3,1,0]
                      , 'C':['M','D','M','D','M','M','M','D','D'])

   id  A  B  C
0   1  H  4  M
1   1  H  5  D
2   1  E  2  M
3   1  E  7  D
4   1  H  6  M
5   2  E  1  M
6   2  E  3  M
7   2  H  1  D
8   2  H  0  D

我想按 id 分组,并为 agg_B1 中的每个 id 第 n 次(假设是第二次)出现 A = 'H' 获取 B 的值,以及第 n 次(假设是第一次)出现的 B 的值C='M':

desired output:
   id  agg_B1  agg_B2
0   1      5      4  
1   2      0      1

desired_output = df.groupby('id').agg(
      agg_B1= ('B',lambda x:x[df.loc[x.index].loc[df.A== 'H'][1]])
   ,  agg_B2= ('B',lambda x:x[df.loc[x.index].loc[df.C== 'M'][0]])

    ).reset_index()

TypeError: Indexing a Series with DataFrame is not supported, use the appropriate DataFrame column

显然,我在索引方面做错了。


编辑:如果可能,我想将聚合与 lambda 函数一起使用,因为我想同时提取多个其他类型的聚合输出。

【问题讨论】:

【参考方案1】:

如果需要,您的解决方案可以更改GroupBy.agg:

desired_output = df.groupby('id').agg(
      agg_B1= ('B',lambda x:x[df.loc[x.index, 'A']== 'H'].iat[1]),
      agg_B2= ('B',lambda x:x[df.loc[x.index, 'C']== 'M'].iat[0])
    ).reset_index()


print (desired_output)
   id  agg_B1  agg_B2
0   1       5       4
1   2       0       1

但是如果性能很重要并且不确定是否总是存在第二个值匹配 H 第一个条件我建议分别处理每个条件并最后添加到原始聚合值:

#some sample aggregations
df0 = df.groupby('id').agg('B':'sum', 'C':'last')

df1 = df[df['A'].eq('H')].groupby("id")['B'].nth(1).rename('agg_B1')
df2 = df[df['C'].eq('M')].groupby("id")['B'].first().rename('agg_B2')

desired_output = pd.concat([df0, df1, df2], axis=1)
print (desired_output)
     B  C  agg_B1  agg_B2
id                       
1   24  M       5       4
2    5  D       0       1

EDIT1:如果需要GroupBy.agg,可以测试索引是否失败,然后添加缺失值:

#for second value in sample working nice
def f1(x):
    try:
        return x[df.loc[x.index, 'A']== 'H'].iat[1]
    except:
        return np.nan

desired_output = df.groupby('id').agg(
      agg_B1= ('B',f1),
      agg_B2= ('B',lambda x:x[df.loc[x.index, 'C']== 'M'].iat[0])
    ).reset_index()

print (desired_output)
   id  agg_B1  agg_B2
0   1       5       4
1   2       0       1
#third value not exist so added missing value NaN
def f1(x):
    try:
        return x[df.loc[x.index, 'A']== 'H'].iat[2]
    except:
        return np.nan

desired_output = df.groupby('id').agg(
      agg_B1= ('B',f1),
      agg_B2= ('B',lambda x:x[df.loc[x.index, 'C']== 'M'].iat[0])
    ).reset_index()

print (desired_output)
   id  agg_B1  agg_B2
0   1     6.0       4
1   2     NaN       1

工作原理是一样的:

df1 = df[df['A'].eq('H')].groupby("id")['B'].nth(2).rename('agg_B1')
df2 = df[df['C'].eq('M')].groupby("id")['B'].first().rename('agg_B2')

desired_output = pd.concat([df1, df2], axis=1)
print (desired_output)
    agg_B1  agg_B2
id                
1      6.0       4
2      NaN       1

【讨论】:

哇,我真的把索引搞砸了!非常感谢,这正是我想要的 @Ankhnesmerira - 我添加了一些编辑,如果工作正常,请在真实数据中测试(因为如果第一个 .iat[1] 不存在组的第二个值,解决方案也会失败) 所以,在我的真实数据集中,这将失败(我只使用第一次出现),并显示“IndexError:索引 0 超出轴 0 的范围,大小为 0。”我如何确保我只是为那些没有发生任何事件而不是错误消息的人获得 Null? @Ankhnesmerira - 刚刚为这种情况添加了编辑。【参考方案2】:

过滤A等于H的行,然后使用nth函数抓取第二行:

df.query("A=='H'").groupby("id").nth(1)

    A   B
id      
1   H   5
2   H   0

Python 使用基于零的符号,因此第 2 行将是 nth(1)

【讨论】:

感谢您的解决方案。这可行,但我的问题是我实际上想使用聚合。因为我感兴趣的输出不止一个。我想我在示例中过度简化了我的问题。如果我在同一个聚合操作中缺少另一个聚合但 A=='E' 怎么办?对于初始查询,这是不可能的。我如何在聚合中使用它? 当然,我已经编辑了问题,因为最初的简化问题并不是我的问题。 <:> 哈哈,不用担心。很高兴有人已经解决了它

以上是关于在使用条件聚合进行分组时选择多个第 n 个值 - 熊猫的主要内容,如果未能解决你的问题,请参考以下文章

在SQL中分组查询 Group by 的存在条件是啥

正则表达式从列表中选择第 n 个值,允许空值

按组选择前 N 个值

如何对多个列上的数据集进行分组并同时进行不同的聚合? Python

在表上聚合查询?

链接查询和分组查询