假设我有以下数据框。
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。
例如,我想将功能应用于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
并填充值的方法。
一些说明
关于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
效果很好!
答案 0 :(得分:1)
您可以将groupby
与groupby.transform
,groupby.head
和groupby.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
说明
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
.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
.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
与第一种方法基本相同,但随后使用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)
获取列表中可以访问的前四个元素:
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