将函数应用于groupby函数

时间:2013-11-22 19:49:14

标签: python pandas apply

我想计算groupby上有多少一致增量,以及第一个元素和最后一个元素之间的差异。但我无法在groupby上应用该功能。在groupby之后,它是一个列表吗?还有“应用”和“聚合”之间的区别是什么?对不起,我刚刚碰了几天蟒蛇。

def promotion(ls):
    pro =0
    if len(ls)>1:
        for j in range(1,len(ls)):
            if ls[j]>ls[j-1]:
                pro + = 1
    return pro
def growth(ls):
    head= ls[0]
    tail= ls[len(ls)-1]
    gro= tail-head
    return gro
titlePromotion= JobData.groupby("candidate_id")["TitleLevel"].apply(promotion)
titleGrowth= JobData.groupby("candidate_id")["TitleLevel"].apply(growth)

数据是:

candidate_id    TitleLevel     othercols
1                 2              foo
2                 1              bar
2                 2              goo
2                 1              gar
The result should be
titlePromotion
candidate_id 
1                  0
2                  1
titleGrowth
candidate_id
1               0
2               0

2 个答案:

答案 0 :(得分:3)

import pandas as pd

def promotion(ls):
    return (ls.diff() > 0).sum()

def growth(ls):
    return ls.iloc[-1] - ls.iloc[0]

jobData = pd.DataFrame(
    {'candidate_id': [1, 2, 2, 2],
     'TitleLevel': [2, 1, 2, 1]})

grouped = jobData.groupby("candidate_id")
titlePromotion = grouped["TitleLevel"].agg(promotion)
print(titlePromotion)
# candidate_id
# 1               0
# 2               1
# dtype: int64

titleGrowth = grouped["TitleLevel"].agg(growth)
print(titleGrowth)
# candidate_id
# 1               0
# 2               0
# dtype: int64

一些提示:

如果您定义通用功能

def foo(ls):
    print(type(ls))

并致电

jobData.groupby("candidate_id")["TitleLevel"].apply(foo)

Python将打印

<class 'pandas.core.series.Series'>

这是一种低调但有效的方式,可以发现调用jobData.groupby(...)[...].apply(foo)Series传递给foo


apply方法为每个组调用foo一次。它可以返回一个Series或一个DataFrame,并将生成的块粘在一起。当apply返回一个对象(如数值或字符串)时,可以使用foo,但在这种情况下,我认为使用agg是首选。使用apply的典型用例是,当您想要对组中的每个值进行平方时,因此需要返回相同形状的新组。

transform方法在这种情况下也很有用 - 当你想转换组中的每个值,因此需要返回相同形状的东西时 - 但结果可以与apply不同,因为可以将不同的对象传递给foo(例如,使用foo时,分组数据框的每一列都会传递给transform虽然使用foo时整个群组都会传递给apply。理解这一点的最简单方法是尝试使用简单的数据框和通用foo。)

agg方法为每个组调用foo一次,但与apply不同,它应该为每个组返回一个数字。该组聚合成为一个值。使用agg的典型用例是当您想要计算组中的项目数时。


您可以使用通用foo函数调试并了解原始代码出现的问题:

In [30]: grouped['TitleLevel'].apply(foo)
0    2
Name: 1, dtype: int64
--------------------------------------------------------------------------------
1    1
2    2
3    1
Name: 2, dtype: int64
--------------------------------------------------------------------------------
Out[30]: 
candidate_id
1               None
2               None
dtype: object

这会显示传递给foo的系列。请注意,在第二个系列中,索引值为1和2.因此,ls[0]会引发KeyError,因为第二个系列中没有值为0的标签。

你真正想要的是系列中的第一个项目。这就是iloc的用途。

总而言之,使用ls[label]选择索引值为label的系列行。使用ls.iloc[n]选择系列的n行。

因此,要使用最少量的更改来修复代码,可以使用

def promotion(ls):
    pro =0
    if len(ls)>1:
        for j in range(1,len(ls)):
            if ls.iloc[j]>ls.iloc[j-1]:
                pro += 1
    return pro
def growth(ls):
    head= ls.iloc[0]
    tail= ls.iloc[len(ls)-1]
    gro= tail-head
    return gro

答案 1 :(得分:1)

VAR0    VAR1
1       1 
1       2
1       3
1       4
2       5
2       6
2       7
2       8

你可以在申请中使用lambda:

下面的代码将减去第一个

中的所有值
grp = df.groupby('VAR0')['VAR1'].apply(lambda x: x.iloc[0] - x)

如果您尝试使用agg:

grp = df.groupby('VAR0')['VAR1'].agg(lambda x: x.iloc[0] - x)

它不起作用,因为agg需要为每个组获取一个值

如果你减去特定单元格的值,则agg和apply之间没有区别,它们都为每个组创建一个值

grp = df.groupby('VAR0')['VAR1'].apply(lambda x: x.iloc[0] - x.iloc[-1])
grp = df.groupby('VAR0')['VAR1'].agg(lambda x: x.iloc[0] - x.iloc[-1])

print grp

VAR0
1      -3
2      -3
Name: VAR1, dtype: int64

如果您希望例如从上一行中减去每一行的值(为了获得每行的增量),您可以使用如下变换:

grp = df.groupby('VAR0')

def subtr(x):
    y=x.copy()
    for i in range(1,len(x.index)):
        x.iloc[i]=y.iloc[i]-y.iloc[i-1]
    return x

new_var = grp['VAR1'].transform(subtr)
print new_var

0    1
1    1
2    1
3    1
4    5
5    1
6    1
7    1
Name: VAR1, dtype: int64

或更容易,对于这个特殊问题:

grp = df.groupby('VAR0')['VAR1'].apply(lambda x: x - x.shift())