groupby,自定义函数每隔前四行使用一列(分组后)

时间:2019-06-18 12:24:35

标签: python-3.x pandas pandas-groupby

假设我有以下数据框。

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)的函数,该函数接收一个numpy数组作为输入包含第四个ID。

例如,我想将功能应用于namea 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并填充值的方法。

一些说明

关于calc函数。我想做更复杂的计算(而不是加法)。 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

效果很好!

2 个答案:

答案 0 :(得分:1)

方法1

您可以将groupbygroupby.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. 最后,我们用.sum得到每组前4行的总和,并删除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

方法2

与第一种方法基本相同,但随后使用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

方法3

在注释OP之后添加以应用自定义功能

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

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

答案 1 :(得分:0)

如果我的问题正确无误,则下面的内容将为您提供帮助

grouped = df.groupby('name').filter(lambda x: x['name'].count() > 3)

for x in grouped['name'].unique():
    subf=df[df['name']==x]
    # you are still able to use the list
    #[0.1, 0.2, 0.02, 0.52, 0.017]
    #[0.87, 0.24, 0.67, 0.9]
    a=subf['val'].tolist()
    print(sum(a))
>> output
0.8570000000000001
2.6799999999999997