Pandas数据分组的函数应用(df.appy()、df.agg()、df.transform()、df.appymap()、df.groupby())

发布时间 2023-10-18 11:41:58作者: 祺琪

     

  apply()数据聚合agg()数据转换transform()applymap()groupby().apply()分组分组 - 可迭代对象其他轴上的分组通过字典或者Series分组通过函数分组分组计算函数方法多函数计算:agg()             

 

将自己定义的或其他库的函数应用于Pandas对象:

   apply():逐行或逐列应用该函数 agg()和transform():聚合和转换 applymap():逐元素应用函数 groupby().apply():聚合之后应用于某个函数 

apply()

介绍

apply函数是pandas里面所有函数中自由度最高的函数。该函数如下:

   DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds) 

该函数最有用的是第一个参数,这个参数是函数,相当于C/C++的函数指针。

这个函数需要自己实现,函数的传入参数根据axis来定,比如axis = 1,就会把一行数据作为Series的数据 结构传入给自己实现的函数中,我们在函数中实现对Series不同属性之间的计算,返回一个结果,则apply函数 会自动遍历每一行DataFrame的数据,最后将所有结果组合成一个Series数据结构并返回。

样例

import numpy as np
import pandas as pd

if __name__ == '__main__':
    f = lambda x : x.max() - x.min()
    df = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['utah', 'ohio', 'texas', 'oregon']) #columns表述列标, index表述行标
    print(df)

    t1 = df.apply(f) #df.apply(function, axis=0),默认axis=0,表示将一列数据作为Series的数据结构传入给定的function中
    print(t1)

    t2 = df.apply(f, axis=1)
    print(t2)


输出结果如下所示:

b         d         e
utah    1.950737  0.318299  0.387724
ohio    1.584464 -0.082965  0.984757
texas   0.477283 -2.774454 -0.532181
oregon -0.851359 -0.654882  1.026698

b    2.802096
d    3.092753
e    1.558879
dtype: float64

utah      1.632438
ohio      1.667428
texas     3.251737
oregon    1.878057
dtype: float64


性能比较

import numpy as np
import pandas as pd

def my_test(a, b):
    return a + b

if __name__ == '__main__':
    df = pd.DataFrame({'a':np.random.randn(6),
                       'b':['foo', 'bar'] * 3,
                       'c':np.random.randn(6)})

    print(df)

    df['value1'] = df.apply(lambda row: my_test(row['a'], row['c']), axis=1)
    print(df)

    df['vaule2'] = df['a'] + df['c']
    print(df)


输出结果如下:

a    b         c
0 -1.745471  foo  0.723341
1 -0.378998  bar  0.229188
2 -1.468866  foo  0.788046
3 -1.323347  bar  0.323051
4 -1.894372  foo  2.216768
5 -0.649059  bar  0.858149

          a    b         c    value1
0 -1.745471  foo  0.723341 -1.022130
1 -0.378998  bar  0.229188 -0.149810
2 -1.468866  foo  0.788046 -0.680820
3 -1.323347  bar  0.323051 -1.000296
4 -1.894372  foo  2.216768  0.322396
5 -0.649059  bar  0.858149  0.209089

          a    b         c    value1    vaule2
0 -1.745471  foo  0.723341 -1.022130 -1.022130
1 -0.378998  bar  0.229188 -0.149810 -0.149810
2 -1.468866  foo  0.788046 -0.680820 -0.680820
3 -1.323347  bar  0.323051 -1.000296 -1.000296
4 -1.894372  foo  2.216768  0.322396  0.322396
5 -0.649059  bar  0.858149  0.209089  0.209089


注意:当数据量很大时,对于简单的逻辑处理建议方法2(个人处理几百M数据集时,方法1花时200s左右,方法2花时10s)

其中:设置axis = 1参数,可以逐行进行操作;默认axis=0,即逐列进行操作;

对于常见的描述性统计方法,可以直接使用一个字符串进行代替,例df.apply(‘mean’)等价于df.apply(np.mean);

>>> df = pd.read_excel('./input/class.xlsx)
>>> df = df[['score_math','score_music']]
>>> df
   score_math  score_music
0          95           79
1          96           90
2          85           85
3          93           92
4          84           90
5          88           70
6          59           89
7          88           86
8          89           74
 
#对音乐课和数学课逐列求成绩平均分
>>> df.apply(np.mean)
score_math     86.333333
score_music    83.888889
dtype: float64
>>> type(df.apply(np.mean))
<class 'pandas.core.series.Series'>
 
>>> df['score_math'].apply('mean')
86.33333333333333
>>> type(df['score_math'].apply(np.mean))
<class 'pandas.core.series.Series'>
 
#逐行求每个学生的平均分
>>> df.apply(np.mean,axis=1)
0    87.0
1    93.0
2    85.0
3    92.5
4    87.0
5    79.0
6    74.0
7    87.0
8    81.5
dtype: float64
>>> type(df.apply(np.mean,axis=1))
<class 'pandas.core.series.Series'>


apply()的返回结果与所用的函数是相关的:

返回结果是Series对象:如上述例子应用的均值函数,就是每一行或每一列返回一个值;返回大小相同的DataFrame:如下面自定的lambda函数。

#其中的x可以看作是每一类的Series对象
>>> df.apply(lambda x: x - 5)
   score_math  score_music
0          90           74
1          91           85
2          80           80
3          88           87
4          79           85
5          83           65
6          54           84
7          83           81
8          84           69
>>> type(df.apply(lambda x: x - 5))
<class 'pandas.core.frame.DataFrame'>


数据聚合agg()

数据聚合agg()指任何能够从数组产生标量值的过程;相当于apply()的特例,可以对pandas对象进行逐行或逐列的处理;能使用agg()的地方,基本上都可以使用apply()代替。

例: 1)对两门课逐列求平均分

>>> df.agg('mean')
score_math     86.333333
score_music    83.888889
dtype: float64
>>> df.apply('mean')
score_math     86.333333
score_music    83.888889
dtype: float64


2)应用多个函数,可将函数放于一个列表中;

例:对两门课分别求最高分与最低分

>>> df.agg(['max','min'])
     score_math  score_music
max          96           92
min          59           70
>>> df.apply([np.max,'min'])
      score_math  score_music
amax          96           92
min           59           70


3)使用字典可以对特定列应用特定及多个函数;

例:对数学成绩求均值和最小值,对音乐课求最大值

>>> df.agg({'score_math':['mean','min'],'score_music':'max'})
      score_math  score_music
max          NaN         92.0
mean   86.333333          NaN
min    59.000000          NaN


数据转换transform()

特点:使用一个函数后,返回相同大小的Pandas对象

与数据聚合agg()的区别:

数据聚合agg()返回的是对组内全量数据的缩减过程;数据转换transform()返回的是一个新的全量数据。

注意:df.transform(np.mean)将报错,转换是无法产生聚合结果的

#将成绩减去各课程的平均分,使用apply、agg、transfrom都可以实现
>>> df.transform(lambda x:x-x.mean())
>>> df.apply(lambda x:x-x.mean())
>>> df.agg(lambda x:x-x.mean())
   score_math  score_music
0    8.666667    -4.888889
1    9.666667     6.111111
2   -1.333333     1.111111
3    6.666667     8.111111
4   -2.333333     6.111111
5    1.666667   -13.888889
6  -27.333333     5.111111
7    1.666667     2.111111
8    2.666667    -9.888889


当应用多个函数时,将返回于原始DataFrame大小不同的DataFrame,返回结果中:

在列索引上第一级别是原始列名在第二级别上是转换的函数名

>>> df.transform([lambda x:x-x.mean(),lambda x:x/10])
  score_math          score_music
    <lambda> <lambda>    <lambda> <lambda>
0   8.666667      9.5   -4.888889      7.9
1   9.666667      9.6    6.111111      9.0
2  -1.333333      8.5    1.111111      8.5
3   6.666667      9.3    8.111111      9.2
4  -2.333333      8.4    6.111111      9.0
5   1.666667      8.8  -13.888889      7.0
6 -27.333333      5.9    5.111111      8.9
7   1.666667      8.8    2.111111      8.6
8   2.666667      8.9   -9.888889      7.4


applymap()

applymap()对pandas对象逐元素应用某个函数,成为元素级函数应用;

与map()的区别:

applymap()是DataFrame的实例方法map()是Series的实例方法

例:对成绩保留小数后两位

>>> df.applymap(lambda x:'%.2f'%x)
  score_math score_music
0      95.00       79.00
1      96.00       90.00
2      85.00       85.00
3      93.00       92.00
4      84.00       90.00
5      88.00       70.00
6      59.00       89.00
7      88.00       86.00
8      89.00       74.00
 
>>> df['score_math'].map(lambda x:'%.2f'%x)
0    95.00
1    96.00
2    85.00
3    93.00
4    84.00
5    88.00
6    59.00
7    88.00
8    89.00
Name: score_math, dtype: object


从上述例子可以看出,applymap()操作实际上是对每列的Series对象进行了map()操作

通过以上分析我们可以看到,apply、agg、transform三种方法都可以对分组数据进行函数操作,但也各有特色,总结如下:

apply中自定义函数对每个分组数据单独进行处理,再将结果合并;整个DataFrame的函数输出可以是标量、Series或DataFrame;每个apply语句只能传入一个函数;agg可以通过字典方式指定特征进行不同的函数操作,每一特征的函数输出必须为标量;transform不可以通过字典方式指定特征进行不同的函数操作,但函数运算单位也是DataFrame的每一特征,每一特征的函数输出可以是标量或者Series,但标量会被广播。

groupby().apply()

Dataframe在行(axis=0)或列(axis=1)上进行分组,将一个函数应用到各个分组并产生一个新值,然后函数执行结果被合并到最终的结果对象中。

   df.groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, **kwargs) 

import numpy as np
import pandas as pd


data = pd.DataFrame({'key1':list('aabba'),
                  'key2': ['one','two','one','two','one'],
                  'data1': np.random.randn(5),
                  'data2': np.random.randn(5)})

def f(group):
    group['sum'] = group.data1.sum()
    return group

aa = data.groupby(['key1','key2']).apply(f)

print(data)

print('*'*30)
print(aa)

bb = data.groupby(['key1','key2'])['data1'].apply(lambda x:x)
print('*'*30)
print(bb)

cc=data.groupby(['key1','key2']).indices
print('*'*30)
print(cc)


结果

  key1 key2     data1     data2
0    a  one -1.065003  0.775987
1    a  two -0.106187 -0.024468
2    b  one  1.079181 -0.499718
3    b  two -0.224642  0.213094
4    a  one  0.771805  1.877397
******************************
  key1 key2     data1     data2       sum
0    a  one -1.065003  0.775987 -0.293198
1    a  two -0.106187 -0.024468 -0.106187
2    b  one  1.079181 -0.499718  1.079181
3    b  two -0.224642  0.213094 -0.224642
4    a  one  0.771805  1.877397 -0.293198
******************************
0   -1.065003
1   -0.106187
2    1.079181
3   -0.224642
4    0.771805
Name: data1, dtype: float64
******************************
{('a', 'one'): array([0, 4], dtype=int64),
('a', 'two'): array([1], dtype=int64),
('b', 'one'): array([2], dtype=int64),
('b', 'two'): array([3], dtype=int64)}


分组

df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})
print(df)
print('------')
 
print(df.groupby('A'), type(df.groupby('A')))
print('------')
# 直接分组得到一个groupby对象,是一个中间数据,没有进行计算
 
a = df.groupby('A').mean()
b = df.groupby(['A','B']).mean()
c = df.groupby(['A'])['D'].mean()  # 以A分组,算D的平均值
print("-----------------")
print(a,type(a),'\n',a.columns)
print()
print(b,type(b),'\n',b.columns)
print()
print(c,type(c))
# 通过分组后的计算,得到一个新的dataframe
# 默认axis = 0,以行来分组
# 可单个或多个([])列分组
#按A列分组求出A,B列的个数
grouped = df.groupby(["A"])
n = grouped.agg({"A": ["count", pd.Series.unique], "B": pd.Series.nunique})
print(n)


分组 - 可迭代对象

df = pd.DataFrame({'X' : ['A', 'B', 'A', 'B'], 'Y' : [1, 4, 3, 2]})
print(df)
print(df.groupby('X'), type(df.groupby('X')))
print('-----')
 
print(list(df.groupby('X')), '→ 可迭代对象,直接生成list\n')
print(list(df.groupby('X'))[0], '→ 以元祖形式显示\n')
for n,g in df.groupby('X'):
    print(n)
    print(g)
    print('###')
print('-----')
# n是组名,g是分组后的Dataframe
 
print(df.groupby(['X']).get_group('A'),'\n')
print(df.groupby(['X']).get_group('B'),'\n')
print('-----')
# .get_group()提取分组后的组
 
grouped = df.groupby(['X'])
print(grouped.groups)
print(grouped.groups['A'])  # 也可写:df.groupby('X').groups['A']
print('-----')
# .groups:将分组后的groups转为dict
# 可以字典索引方法来查看groups里的元素
 
sz = grouped.size()
print(sz,type(sz))
print('-----')
# .size():查看分组后的长度
 
df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar','foo', 'bar', 'foo', 'foo'],
                   'B' : ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three'],
                   'C' : np.random.randn(8),
                   'D' : np.random.randn(8)})
print(df)
print()
print(df.groupby(['A','B']))
print()
grouped = df.groupby(['A','B']).groups
 
print(grouped)
print()
print(grouped[('foo', 'three')])
# 按照两个列进行分组


其他轴上的分组

import pandas as pd
import numpy as np
 
df = pd.DataFrame({'data1':np.random.rand(2),
                  'data2':np.random.rand(2),
                  'key1':['a','b'],
                  'key2':['one','two']})
print(df)
print(df.dtypes)
print("-------------")
print(df.groupby(df.dtypes, axis=1))
print('-----')
print(list(df.groupby(df.dtypes, axis=1)))
print()
for n,p in df.groupby(df.dtypes, axis=1):
    print(n)
    print()
    print(p)
    print('##')
# 按照值类型分列


通过字典或者Series分组

df = pd.DataFrame(np.arange(16).reshape(4,4),
                  columns = ['a','b','c','d'])
print(df)
print('-----')
 
mapping = {'a':'one','b':'one','c':'two','d':'two','e':'three'}
by_column = df.groupby(mapping, axis = 1)
print(by_column.sum())
print('-----')
# mapping中,a、b列对应的为one,c、d列对应的为two,以字典来分组
 
s = pd.Series(mapping)
print(s,'\n')
print(s.groupby(s).count())
# s中,index中a、b对应的为one,c、d对应的为two,以Series来分组


通过函数分组

df = pd.DataFrame(np.arange(16).reshape(4,4),
                  columns = ['a','b','c','d'],
                 index = ['abc','bcd','aa','b'])
print(df,'\n')
print(df.groupby(len).sum())
# 按照字母长度分组  


分组计算函数方法

s = pd.Series([1, 2, 3, 10, 20, 30], index = [1, 2, 3, 1, 2, 3])
grouped = s.groupby(level=0)  # 唯一索引用.groupby(level=0),将同一个index的分为一组
print(grouped)
print(grouped.first(),'→ first:非NaN的第一个值\n')
print(grouped.last(),'→ last:非NaN的最后一个值\n')
print(grouped.sum(),'→ sum:非NaN的和\n')
print(grouped.mean(),'→ mean:非NaN的平均值\n')
print(grouped.median(),'→ median:非NaN的算术中位数\n')
print(grouped.count(),'→ count:非NaN的值\n')
print(grouped.min(),'→ min、max:非NaN的最小值、最大值\n')
print(grouped.std(),'→ std,var:非NaN的标准差和方差\n')
print(grouped.prod(),'→ prod:非NaN的积\n')
print(grouped.size()) # 获取分组后每组的数量


多函数计算:agg()

df = pd.DataFrame({'a':[1,1,2,2],
                  'b':np.random.rand(4),
                  'c':np.random.rand(4),
                  'd':np.random.rand(4),})
print(df)
print(df.groupby('a').agg(['mean',np.sum]))
print(df.groupby('a')['b'].agg({'result1':np.mean,
                               'result2':np.sum}))
# 函数写法可以用str,或者np.方法
# 可以通过list,dict传入,当用dict时,key名为columns


参考:https://www.cnblogs.com/Cheryol/p/13451562.html https://www.cnblogs.com/mliu222/p/12003794.html https://blog.csdn.net/spiral1221/article/details/76152002 https://www.cnblogs.com/feifeifeisir/p/13792217.html