pandas数据分组聚合 您所在的位置:网站首页 大量数据求和 pandas数据分组聚合

pandas数据分组聚合

2023-06-24 13:21| 来源: 网络整理| 查看: 265

数据分组

数据分组就是根据一个或多个键(可以是函数、数组或df列名)将数据分成若干组,然后对分组后的数据分别进行汇总计算,并将汇总计算后的结果进行合并,被用作汇总计算的函数称为聚合函数。数据分组的具体分组流程如下图所示

数据分组原理

在Python中对数据分组利用的是**groupby()**方法:

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

by :分组字段,接收list、str、mapping或generator,用于确定进行分组的键值。如果传入的是一个函数则对索引进行计算并分组;如果传入的是一个字典或者series则用字典或者series的值作为分组依据;如果传入一个numpy数组则用数据的元素作为分组依据;如果传入的是字符串或者字符串列表则用这些字符串所代表的字段作为分段依据 axis :指定切分方向,默认为0,表示沿着行切分,对列进行操作 level :表示标签所在级别,默认为None as_index:表示聚合后的聚合标签是否以DataFrame索引形式输出,默认为True;当设置为False时相当于加了reset_index功能 sort :通过sort参数指定是否对输出结果按索引排序,默认为True group_keys :表示是否显示分组标签的名称,默认为True squeeze :表示是否在允许的情况下对返回数据进行降维,默认为True 1 分组类型

分组类型

1.1 分组键是列名

分组键是列名时直接将某一列或多列的列名传给groupby()方法,groupby()方法就会按照这一列或多列进行分组。

1.1.1 按照一列进行分组 import pandas as pd df = pd.read_excel(r"D:\testdata\data.xlsx") print(df.head()) print("="*30) df.info() 实际价格(元) 运费(元) 数量 实际支付(元) 状态 商品SKU信息 省 市 \ 0 218.9 0.0 1 218.9 已付款未发货 金色110cm 1个 内蒙古自治区 呼伦贝尔市 1 218.9 0.0 1 218.9 交易关闭 金色120cm 1个 内蒙古自治区 呼伦贝尔市 2 328.9 0.0 1 328.9 交易取消 米白S 1个 山东省 枣庄市 3 218.9 0.0 1 218.9 交易关闭 黑色M 105-120斤 1个 浙江省 嘉兴市 4 185.9 0.0 1 185.9 已付款未发货 黑色L 1个 陕西省 咸阳市 区 会员等级 优惠信息 是否白付美支付 货款退款金额 运费退款金额 退款完成时间 0 新巴尔虎右旗 获取会员信息失败! 无优惠信息 否 0.0 0.0 NaN 1 新巴尔虎右旗 获取会员信息失败! 无优惠信息 否 218.9 0.0 2020-11-12 09:35:43 2 薛城区 获取会员信息失败! 无优惠信息 否 0.0 0.0 NaN 3 桐乡市 获取会员信息失败! 无优惠信息 否 218.9 0.0 2020-11-12 06:37:25 4 杨陵区 获取会员信息失败! 无优惠信息 是 0.0 0.0 NaN ============================== RangeIndex: 88 entries, 0 to 87 Data columns (total 15 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 实际价格(元) 88 non-null float64 1 运费(元) 88 non-null float64 2 数量 88 non-null int64 3 实际支付(元) 88 non-null float64 4 状态 88 non-null object 5 商品SKU信息 88 non-null object 6 省 88 non-null object 7 市 88 non-null object 8 区 88 non-null object 9 会员等级 88 non-null object 10 优惠信息 88 non-null object 11 是否白付美支付 88 non-null object 12 货款退款金额 88 non-null float64 13 运费退款金额 88 non-null float64 14 退款完成时间 41 non-null object dtypes: float64(5), int64(1), object(9) memory usage: 10.4+ KB df.groupby("状态") # 以状态列分组 df.groupby("状态").groups # groups属性返回一个字典,包含所有分组子数据帧与索引值 {'交易关闭': Int64Index([ 1, 3, 5, 6, 7, 8, 13, 14, 15, 16, 17, 18, 21, 22, 23, 28, 29, 30, 33, 35, 37, 38, 39, 43, 45, 46, 49, 50, 51, 54, 56, 59, 65, 67, 70, 75, 77, 80, 81, 82, 84], dtype='int64'), '交易取消': Int64Index([ 2, 11, 24, 31, 36, 41, 44, 47, 48, 52, 55, 60, 61, 62, 64, 66, 69, 73, 79, 83, 85, 86], dtype='int64'), '已付款未发货': Int64Index([ 0, 4, 9, 10, 12, 19, 20, 25, 26, 27, 32, 34, 40, 42, 53, 57, 58, 63, 68, 71, 72, 74, 76, 78], dtype='int64'), '已发货': Int64Index([87], dtype='int64')} print(df.groupby("状态").size()) # 查看各状态的元素个数 状态 交易关闭 41 交易取消 22 已付款未发货 24 已发货 1 dtype: int64

从上面的结果可以看出,如果只是传入列名,运行groupby()方法以后返回的不是一个DataFrame对象,而是一个DataFrameGroupBy对象,这个对象里面包含着分组以后的若干组数据,但是没有直接显示出来,需要对这些分组数据进行汇总计算以后才会展示出来。

df.groupby("状态").count() 实际价格(元)运费(元)数量实际支付(元)商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间状态交易关闭4141414141414141414141414141交易取消222222222222222222222222220已付款未发货242424242424242424242424240已发货11111111111110

通过调用get_group( )函数可以返回一个按照分组得到的DataFrame对象,如果想让这个DataFrame对象的索引重新定义可以通过set_index()方法

df.groupby("状态").get_group("已发货").set_index("省") #获取状态分组中的已发货信息并以省作为新索引 实际价格(元)运费(元)数量实际支付(元)状态商品SKU信息市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间省上海市328.90.01328.9已发货米白S 1个上海市青浦区获取会员信息失败!无优惠信息否0.00.0NaN

由于对分组后的数据进行了计数运算,因此每一列都会有一个结果,但是如果对分组后的结果做一些数值运算,这个时候就只有数据类型是数值(int、float)的列才会参与运算,比如下面的求和运算。

df.groupby("状态").sum() 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态交易关闭8870.30.0418870.38870.30.0交易取消14767.70.02214767.70.00.0已付款未发货4910.60.0244910.60.00.0已发货328.90.01328.90.00.0

我们把这种对分组后的数据进行汇总运算的操作称为聚合,使用的函数称为聚合函数

1.1.2 按照多列进行分组

上面分组键是某一列,即按照一列进行分组,也可以按照多列进行分组,只要**将多个列名以列表的形式传给groupby()**即可,汇总计算方式与按照单列进行分组以后数据运算的方式一致。

df.groupby(["状态","是否白付美支付"]).sum() # 以”状态“列与”是否白付美支付“列作为分组键汇总求和 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态是否白付美支付交易关闭否4280.70.0194280.74280.70.0是4589.60.0224589.64589.60.0交易取消否14767.70.02214767.70.00.0已付款未发货否3132.70.0153132.70.00.0是1777.90.091777.90.00.0已发货否328.90.01328.90.00.0

无论分组键是一列还是多列,只要直接在分组后的数据上进行汇总计算,就是对所有可以计算的列进行计算。有的时候我们不需要对所有列进行计算,这个时候就可以把想要计算的列(可以是单列,也可以是多列)通过索引的方式取出来,然后在取出来这列数据的基础上进行汇总计算。

df.groupby("状态")["区"].count() # 以”状态“列分组单独对”区“列进行汇总计数 状态 交易关闭 41 交易取消 22 已付款未发货 24 已发货 1 Name: 区, dtype: int64 df.groupby("状态")[["区","省"]].count() # 以”状态“列分组单独对”区“列与”省“进行汇总计数 区省状态交易关闭4141交易取消2222已付款未发货2424已发货11 1.2 分组键是Series

把DataFrame的其中一列取出来就是一个Series

分组键是列名与分组键是Series的唯一区别就是,给groupby()方法传入了什么,其他都一样。可以按照一个或多个Series进行分组,分组以后的汇总计算也是完全一样的,也支持对分组以后的某些列进行汇总计算

1.2.1 按照一个Series进行分组 df.groupby(df["状态"]).count() # 以”状态“列作为分组键 实际价格(元)运费(元)数量实际支付(元)商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间状态交易关闭4141414141414141414141414141交易取消222222222222222222222222220已付款未发货242424242424242424242424240已发货11111111111110 1.2.2 按照多个Series进行分组 df.groupby([df["状态"],df["是否白付美支付"]]).sum() # 以”状态“列与”是否白付美支付“列作为分组键汇总求和 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态是否白付美支付交易关闭否4280.70.0194280.74280.70.0是4589.60.0224589.64589.60.0交易取消否14767.70.02214767.70.00.0已付款未发货否3132.70.0153132.70.00.0是1777.90.091777.90.00.0已发货否328.90.01328.90.00.0

对分组后的某些列进行汇总运算

df.groupby(df["状态"])["区"].count() # 以”状态“列分组单独对”区“列进行汇总计数 状态 交易关闭 41 交易取消 22 已付款未发货 24 已发货 1 Name: 区, dtype: int64 1.3 groupby()核心用法总结

(1)根据DataFrame本身的某一列或多列内容进行分组聚合:

(a)若按某一列聚合,则新DataFrame将根据某一列的内容分为不同的维度进行拆解,同时将同一维度的再进行聚合, (b)若按某多列聚合,则新DataFrame将是多列之间维度的笛卡尔积,即:新DataFrame具有一个层次化索引(由唯一的键对组成),例如:“key1”列,有a和b两个维度,而“key2”有one和two两个维度,则按“key1”列和“key2”聚合之后,新DataFrame将有四个group; (c)通过调用get_group()函数可以返回一个按照分组得到的DataFrame对象,该对象是分组中的一种情况; (d)可以将想要计算的列(可以是单列,也可以是多列)通过索引的方式取出来,然后在取出来这列数据的基础上进行汇总计算。eg:df.groupby(“状态”)[“区”]、df.groupby(“状态”)[[“区”,“省”]]

注意:groupby默认是在axis=0上进行分组的,通过设置axis=1,也可以在其他任何轴上进行分组。

(2)GroupBy对象结合描述统计方法从各个分组中产生标量值:

(a)这个标量值可以是平均值、数量、中位数等。GroupBy对象常用的描述性统计方法如下表所示:

Groupby常用的统计描述性方法

2 分组运算方法

pandas提供了多种分组运算方法,包括aggregate、apply和transform,它们的用法各有不同,适用于不同的场景。

2.1 aggregate方法

前面用到的聚合函数都是直接在DataFrameGroupBy上调用的,这样分组以后所有列做的都是同一种汇总运算,且一次只能使用一种汇总方式。

aggregate的神奇之处在于,一次可以使用多种汇总方式,比如下面的例子先对分组后的所有列做计数汇总运算,然后对所有列做求和汇总运算。

aggregate方法是一个既能作用于Series、DataFrame,也能作用于GroupBy的聚合方法。aggregate方法接收函数并应用于每个分组,返回标量值,其基本语法格式如下

Groupby.aggregate(func,axis=0,*args,**kwargs)

func : 指定用于集合运算的函数,具体类型包含自定义函数名、字符串函数名、列表函数名,字典函数名。该参数支持的统计函数是pandas、numpy、scipy、python提供的所有统计函数,也可以是自定义函数 axis :值为0则在列向做聚合运算,值为1则在行向做聚合运算 2.1.1 内置函数聚合运算 import numpy as np df.groupby("状态").aggregate("count") 实际价格(元)运费(元)数量实际支付(元)商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间状态交易关闭4141414141414141414141414141交易取消222222222222222222222222220已付款未发货242424242424242424242424240已发货11111111111110 df.groupby("状态").aggregate(np.mean) # 状态分组汇总求均值 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态交易关闭216.3487800.01216.348780216.348780.0交易取消671.2590910.01671.2590910.000000.0已付款未发货204.6083330.01204.6083330.000000.0已发货328.9000000.01328.9000000.000000.0

aggregate可以针对不同的列做不同的汇总运算,比如下面的例子,我们想看不同状态的商品SKU信息有多少,那么对商品SKU信息进行计数;我们想看不同状态的实际价格,则需要对销量进行求和。

df.groupby("状态").aggregate({"商品SKU信息":"count","实际价格(元)":"sum"}) 商品SKU信息实际价格(元)状态交易关闭418870.3交易取消2214767.7已付款未发货244910.6已发货1328.9 2.1.2 自定义函数聚合运算 def Max_cut_Min(group): return group.max()-group.min() df.groupby("状态").aggregate(Max_cut_Min) # 求状态分组下 各数值元素的最大值与最小值之差 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态交易关闭462.00.00462.0462.00.0交易取消2000.00.002000.00.00.0已付款未发货143.00.00143.00.00.0已发货0.00.000.00.00.0 df.groupby("状态").aggregate(lambda group:group.max()-group.min()) # 运用lambda表达式 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态交易关闭462.00.00462.0462.00.0交易取消2000.00.002000.00.00.0已付款未发货143.00.00143.00.00.0已发货0.00.000.00.00.0 2.1.3 单列聚合 df.groupby(["状态","是否白付美支付"])["实际价格(元)"].aggregate(np.mean) 状态 是否白付美支付 交易关闭 否 225.300000 是 208.618182 交易取消 否 671.259091 已付款未发货 否 208.846667 是 197.544444 已发货 否 328.900000 Name: 实际价格(元), dtype: float64 2.1.4 多列聚合 df.groupby(["状态","是否白付美支付"]).aggregate("mean") 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态是否白付美支付交易关闭否225.3000000.01225.300000225.3000000.0是208.6181820.01208.618182208.6181820.0交易取消否671.2590910.01671.2590910.0000000.0已付款未发货否208.8466670.01208.8466670.0000000.0是197.5444440.01197.5444440.0000000.0已发货否328.9000000.01328.9000000.0000000.0 2.1.5 多种聚合运算 df.groupby(["状态","是否白付美支付"])["实际价格(元)"].aggregate([np.max,np.min]) amaxamin状态是否白付美支付交易关闭否658.9199.0是416.9196.9交易取消否2199.0199.0已付款未发货否328.9196.9是199.0185.9已发货否328.9328.9 2.1.6 多种聚合运算的同时更改列名 df.groupby(["状态","是否白付美支付"])["实际价格(元)"].aggregate([("最大值","max"),("最大值","min")]) 最大值最大值状态是否白付美支付交易关闭否199.0199.0是196.9196.9交易取消否199.0199.0已付款未发货否196.9196.9是185.9185.9已发货否328.9328.9 2.1.7 不同的列运用不同的聚合函数 df.groupby(["状态","是否白付美支付"]).aggregate({'实际价格(元)':['sum','mean'],'货款退款金额':['sum','mean']}) 实际价格(元)货款退款金额summeansummean状态是否白付美支付交易关闭否4280.7225.3000004280.7225.300000是4589.6208.6181824589.6208.618182交易取消否14767.7671.2590910.00.000000已付款未发货否3132.7208.8466670.00.000000是1777.9197.5444440.00.000000已发货否328.9328.9000000.00.000000 2.2 apply 方法

apply方法是一个既能接收返回标量值的函数,又能接收返回数组的函数的聚合方法,功能更加强大。而aggregate方法仅能接收返回标量值的函数。

apply方法的基本语法格式如下:

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

apply只能传入函数或者lambda表达式,这个函数和aggregate有区别,aggregate函数传入的是每个子数据帧的每一列,而这里的apply的函数,传入的是“每个子数据帧的所有列/行”,一般默认为列,apply函数可以对多列进行操作, 如果是自定义函数 / 自定义lambda,函数传入的是整个子数据帧,那么可以用['列名']的形式选中数据帧的某几行进行操作; 如果传入的是numpy自带的函数,默认axis = 0,即对每一列进行操作,这一点不要搞混,因为numpy中,如果不写axis的话默认是对所有数进操作; apply返回的结果根据传入函数的返回值决定: 如果传入函数的返回值是标量,则和aggregate很类似,只不过aggregate最后把分类的那一列提到第一列,而apply是在原来列顺序的基础上,在最前面的一列补充了分类的那一列,即分类的那一列展示了两次; 如果传入函数的返回值是向量,会返回若干个子数据帧 2.2.1 传入的是自定义函数/自定义lambda df.groupby("状态").apply(lambda x : x['实际价格(元)'] - x['货款退款金额']) #这里的x代表整个子数据帧 状态 交易关闭 1 0.0 3 0.0 5 0.0 6 0.0 7 0.0 ... 已付款未发货 72 199.0 74 199.0 76 199.0 78 199.0 已发货 87 328.9 Length: 88, dtype: float64 df.groupby("状态").get_group("已发货") 实际价格(元)运费(元)数量实际支付(元)状态商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间87328.90.01328.9已发货米白S 1个上海市上海市青浦区获取会员信息失败!无优惠信息否0.00.0NaN 2.1.2 传入的是numpy自带的函数 df.groupby('状态').apply(np.min) 实际价格(元)运费(元)数量实际支付(元)状态商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间状态交易关闭196.90.01196.9交易关闭米色S 90-105斤 1个云南省三门峡市丹阳市获取会员信息失败!无优惠信息否196.90.02020-11-11 15:02:31交易取消199.00.01199.0交易取消米白S 1个内蒙古自治区临沂市临猗县获取会员信息失败!无优惠信息否0.00.0None已付款未发货185.90.01185.9已付款未发货米色S 90-105斤 1个上海市上海市中原区获取会员信息失败!无优惠信息否0.00.0None已发货328.90.01328.9已发货米白S 1个上海市上海市青浦区获取会员信息失败!无优惠信息否0.00.0None

虽然apply传入的函数的是整个子数据帧,但是apply仍能用df.group(‘XXX’)[‘列名’].apply的形式来指定列

df.groupby('状态')["实际价格(元)"].apply(lambda x : x/100) 0 2.189 1 2.189 2 3.289 3 2.189 4 1.859 ... 83 21.990 84 6.589 85 3.289 86 3.289 87 3.289 Name: 实际价格(元), Length: 88, dtype: float64

如果希望返回的结果不以分组键为索引,通过group_keys=False可以完成

df.groupby("状态",group_keys=False).apply(lambda x : x['实际价格(元)'] - x['货款退款金额']) 1 0.0 3 0.0 5 0.0 6 0.0 7 0.0 ... 72 199.0 74 199.0 76 199.0 78 199.0 87 328.9 Length: 88, dtype: float64 2.3 transform方法

aggregate(func,axis=0,*args,**kwargs)

func : 指定用于转换数据函数,具体类型包含自定义函数名、字符串函数名、列表函数名,字典函数名。该参数支持的统计函数是pandas、numpy、scipy、python提供的所有统计函数,也可以是自定义函数 axis :值为0则在列向做聚合运算,值为1则在行向做聚合运算

使用transform方法需要注意以下事项: (1)返回值与组大小相同或可以广播大组大小 (2)只能在组上逐列操作 (3)不能修改组里的元素,如用fillna方法填充时,不能设置inplace=True

transform方法只能传入函数和匿名函数,这个函数的形参和aggregate一样,是每个子数据帧的每个列,但是输出的却是向量。因此这个用了transform函数之后,每个子数据帧返回每个子数据帧原有的行数,aggregate聚合方法的每个子数据帧最后都只能返回一行,因为聚合函数传入的每一列都只能返回一个数。

df.groupby("状态").transform(np.mean) 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额0204.6083330.01204.6083330.000000.01216.3487800.01216.348780216.348780.02671.2590910.01671.2590910.000000.03216.3487800.01216.348780216.348780.04204.6083330.01204.6083330.000000.0.....................83671.2590910.01671.2590910.000000.084216.3487800.01216.348780216.348780.085671.2590910.01671.2590910.000000.086671.2590910.01671.2590910.000000.087328.9000000.01328.9000000.000000.0

88 rows × 6 columns

df.groupby("状态").aggregate(np.mean) 实际价格(元)运费(元)数量实际支付(元)货款退款金额运费退款金额状态交易关闭216.3487800.01216.348780216.348780.0交易取消671.2590910.01671.2590910.000000.0已付款未发货204.6083330.01204.6083330.000000.0已发货328.9000000.01328.9000000.000000.0 df.groupby('状态')["实际价格(元)"].transform(lambda x : x/100) 0 2.189 1 2.189 2 3.289 3 2.189 4 1.859 ... 83 21.990 84 6.589 85 3.289 86 3.289 87 3.289 Name: 实际价格(元), Length: 88, dtype: float64

transform方法也可以指定列操作 聚合与转换的主要区别就是转换的函数主要应用于值得计算,聚合的函数主要应用于值的统计

2.4 filter方法

根据指定的过滤条件,获取DataFrame符合条件的子数据集

filter(item=None,like=None,regex=None,axis=None)

item :指定过滤索引值 like :类似数据库SELECT里的like命令,模糊查找 regex :指定正则表达式,设置过滤条件 axis :指定过滤方向,值为1是列方向,值为0是行方向

注意 :item, like, regex, axis不能同时使用

2.4.1 普通过滤 df.filter(items=["状态","市"]).head() 状态市0已付款未发货呼伦贝尔市1交易关闭呼伦贝尔市2交易取消枣庄市3交易关闭嘉兴市4已付款未发货咸阳市 df.filter(like="9",axis=0).head() # 寻找行索引带9的行 实际价格(元)运费(元)数量实际支付(元)状态商品SKU信息省市区会员等级优惠信息是否白付美支付货款退款金额运费退款金额退款完成时间9328.90.01328.9已付款未发货粉色S 1个河北省邯郸市曲周县获取会员信息失败!无优惠信息否0.00.0NaN19199.00.01199.0已付款未发货黑色M 105-120斤 1个山西省太原市小店区获取会员信息失败!无优惠信息是0.00.0NaN29199.00.01199.0交易关闭黑色M 105-120斤 1个江西省南昌市新建区获取会员信息失败!无优惠信息是199.00.02020-11-11 22:55:3339199.00.01199.0交易关闭黑色M 105-120斤 1个河北省张家口市怀来县获取会员信息失败!无优惠信息是199.00.02020-11-11 23:30:4949199.00.01199.0交易关闭黑色M 105-120斤 1个贵州省贵阳市云岩区获取会员信息失败!无优惠信息是199.00.02020-11-11 22:45:40 df.filter(regex="\A实").head() # 寻找以实开头的列 实际价格(元)实际支付(元)0218.9218.91218.9218.92328.9328.93218.9218.94185.9185.9 2.4.2 分组过滤 df.groupby("状态").filter(lambda x:300


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有