首页 > 解决方案 > groupby ,自定义函数每前 4 行使用一列(分组后)

问题描述

假设我有以下数据框。

import numpy as np
import pandas as pd

df = pd.DataFrame({'name':['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c'],
                   'id':[0,1,2,3,4, 0, 1,2,3, 0, 1, 2], 
                   'val':[0.1, 0.2, 0.02, 0.52, 0.017,0.87, 0.24, 0.67, 0.9, 1.0, 0.99, 0.56]})

df

name    id  val
0   a   0   0.100
1   a   1   0.200
2   a   2   0.020
3   a   3   0.520
4   a   4   0.017
5   b   0   0.870
6   b   1   0.240
7   b   2   0.670
8   b   3   0.900
9   c   0   1.000
10  c   1   0.990
11  c   2   0.560

现在,我想这样做。

我想按名称分组并将自定义函数应用于数据框。

按名称分组后,我想检查该id列,如果它包含至少 4 行,然后应用调用另一个 ( calc) 的函数,该函数接收一个包含 4 个第一个 id 的 numpy 数组作为输入。

例如,我想将函数应用于name:a and b因为它们有id: 0,1,2,3,4 和 0,1,2,3 。因此,它们都至少有 4 行。

我想使用前 4 行,以便将它们用作calc函数的输入。

def calc(p):

    return p[0] + p[1] + p[2] + p[3]

现在,对于自定义函数(它不起作用)类似的东西:

def myfunc(data):
    if (data.id.values <=3):
        val1 = data[data.id==0].val.values
        val2 = data[data.id==1].val.values
        val3 = data[data.id==2].val.values
        val4 = data[data.id==3].val.values


    data['calc'] = calc(np.array([val1, val2, val3, val4]))
    return data

它给了我The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

我找不到正确计数id和填充 val 的方法。

一些澄清

关于计算函数。我想做更复杂的计算(不是加法)。calc 函数必须接收一个包含至少 4 个值的数组作为输入。

预期的输出是:

name    calc
a       0.84 (0.1+0.2+0.020+0.520)
b       2.68 (0.870+0.240+0.670+0.900)

or maybe something like (since it is name based):

name    id  val    calc
0   a   0   0.100  0.84
1   a   1   0.200  0.84
2   a   2   0.020  0.84
3   a   3   0.520  0.84
4   a   4   0.017  0.84
5   b   0   0.870  2.68
6   b   1   0.240  2.68
7   b   2   0.670  2.68
8   b   3   0.900  2.68
9   c   0   1.000  
10  c   1   0.990
11  c   2   0.560

更新

我将(根据@Erfan 的回答)更改为,groupby('name')[['val']].apply(calc).reset_index()而不是groupby('name')['val'].apply(list).reset_index()calc函数更改为:

def calc(data):
    p0 = np.array([data.val.values[0]])
    p1 = np.array([data.val.values[1]])
    p2 = np.array([data.val.values[2]])
    p3 = np.array([data.val.values[3]])


    data['calc'] = np.array([p0, p1, p2, p3])
    return data

它工作正常!

标签: python-3.xpandaspandas-groupby

解决方案


方法一

您可以使用,和链接groupby三次:groupby.transformgroupby.headgroupby.sum

df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)\
     .groupby('name', as_index=False).sum().drop('id', axis=1)


  name   val
0    a  0.84
1    b  2.68

解释

  1. df[df.groupby('name')['id'].transform('count').ge(4)]返回具有 4 行或更多行的每个唯一名称的所有行:
  name  id    val
0    a   0  0.100
1    a   1  0.200
2    a   2  0.020
3    a   3  0.520
4    a   4  0.017
5    b   0  0.870
6    b   1  0.240
7    b   2  0.670
8    b   3  0.900
  1. 然后我们链接它.head(4),它只给我们每组的前 4 行:
df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)

  name  id   val
0    a   0  0.10
1    a   1  0.20
2    a   2  0.02
3    a   3  0.52
5    b   0  0.87
6    b   1  0.24
7    b   2  0.67
8    b   3  0.90
  1. 最后,我们得到每组前 4 行的总和.sum并删除该id列:
df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)\
     .groupby('name', as_index=False).sum().drop('id', axis=1)

  name   val
0    a  0.84
1    b  2.68

方法二

与第一种方法基本相同,但随后使用groupby.filter

df.groupby('name').filter(lambda x: x['id'].count() >= 4)\
  .groupby('name').head(4)\
  .groupby('name', as_index=False).sum().drop('id', axis=1)

  name   val
0    a  0.84
1    b  2.68

方法三

在评论 OP 后添加以应用自定义功能

您可以使用.apply(list)获取可以访问的列表中的前 4 个元素:

df2 = df[df.groupby('name')['id'].transform('count').ge(4)]\
           .groupby('name').head(4)\
           .groupby('name')['val'].apply(list).reset_index()

  name                      val
0    a   [0.1, 0.2, 0.02, 0.52]
1    b  [0.87, 0.24, 0.67, 0.9]

然后,如果您想对这些值求和:

df2['val'].apply(lambda x: sum(x))

0    0.84
1    2.68
Name: val, dtype: float64

推荐阅读