点击上方“潜心的Python小屋”关注我们,第一时间推送优质文章。

前言

大家好,我是潜心。上篇文章提到了Groupby,但其中举例的代码有点问题,在提取序列时用到了for循环,效率很慢,后来查找了官方文档,才明白apply的重要性,再次对Groupby进行深入并总结。

本文约2.1k字,预计阅读15分钟。

Groupby: split-apply-combine

Pandas中Groupby定义如下:

def groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False, observed=False)

Groupby具体来说指的是涉及以下一个或多个步骤的过程:

  • 分割(Splitting):根据一些标准将数据划分为多个组。

  • 应用(Applying):独立地对每个组应用一个函数。

  • 组合(Combing):将结果组合成数据结构。

其中,分割是最直接的,也是最常用的(如上图)。但是事实上,在许多情况下,我们可能希望将数据集分成组,并对这些组做一些统计值计算。在Applying中,可能希望做以下其中一项:

  • 聚集(Aggregation):计算每个组的汇总统计信息(或统计信息),例如计算分组的和、均值、个数等。

  • 转换(Transformation):执行一些特定组的计算并返回一个like-indexed对象。

  • 过滤(Filteration):根据判断为真或假的组计算,丢弃一些组。

此次我们不讨论Group的分割问题,主要聚焦它的Applying,并结合实际的大数据量的实际表来比较它们的效率问题。采用的数据表为用户与他点击的广告,共100w条:

user_id creative_id
0 30920 567330
1 30920 3072255
2 30920 3072255
3 30920 3879497
4 30920 3751623
... ... ...

Aggregation

当创建了GroupBy对象,根据需求我们可以对分组的数据执行计算。最简单的是我们通过agg()方法来调用一些Python内置函数进行聚合计算,常用的内置函数为:meanmediansumsizecountstddescribeminmax等。

这里我们基于user_id对数据进行划分,简单应用部分内置函数,统计对每个用户他点击过的最大和最小的广告id:

%%time
In[1]: df.groupby('user_id').agg(['count', 'min', 'max'])CPU times: user 72 ms, sys: 0 ns, total: 72 ms
Wall time: 70.9 ms
Out[1]:user_id count min max
31 26 109090 4440651
34 63 3983 4266235
36 19 67988 3999372
310 12 10039 3042631
312 28 24918 3904071
... ... ... ...
363305 16 42555 4187970
363311 19 187530 4151703
363317 15 63052 4307786
363318 41 7400 4079814
363319 167 10257 4389117
30025 rows × 3 columns

以上我们发现索引变为了user_id若不想改变DataFrame的索引,则需要在groupby方法中的参数as_index设置为False

当然如果你需要进一步进行统计运算,则panda允许提供多个lambdas自定义计算。在这种情况下,panda将混淆lambda函数的名称,将_<i>附加到每个后续的lambda。可以通过rename()更改列名。

%%time
In[2]: df.groupby('user_id').agg([lambda x: x.max() - x.min(), lambda x: x.mean() - x.median()])CPU times: user 20.4 s, sys: 0 ns, total: 20.4 s
Wall time: 20.4 s
Out[2]:creative_id
user_id <lambda_0> <lambda_1>
31 4331561 -107949.846154
34 4262252 430452.825397
36 3931384 -16653.473684
310 3032592 -121110.916667
312 3879153 208385.392857
... ... ...
363305 4145415 364584.500000
363311 3964173 524387.842105
363317 4244734 787231.666667
363318 4072414 -110147.829268
363319 4378860 250463.107784
30025 rows × 2 columns

对不同的列使用不同的聚合可以通过字典的方式实现(这里采用文档的Code,CD为列名):

In [3]: grouped.agg({'C': np.sum,....:              'D': lambda x: np.std(x, ddof=1)})....:
Out[3]: C         D
A
bar  0.392940  1.366330
foo -1.796421  0.884785

Transformation

transform方法返回一个与正在分组的对象索引相同(大小相同)的对象。

我们通过对表进行与agg()相同的内置函数进行比较:

%%time
In[4]: df.groupby('user_id').transform('count')CPU times: user 28 ms, sys: 0 ns, total: 28 ms
Wall time: 29 ms
Out[4]:
creative_id
0 42
1 42
2 42
3 42
4 42
... ...
999995 167
999996 167
999997 167
999998 167
999999 167
1000000 rows × 1 columns

我们发现返回了一个与原数据表大小相同的对象,并且把groupby中的by参数给省略了。(并且它不能一次使用多个内置函数)。

实际用途: 如果我们需要为原数据表添加一列count或其他内容,则需要使用该方法。

In[5]: df['ad_count'] = df.groupby('user_id').transform('count')
Out[5]: user_id creative_id ad_count
0    30920  567330 42
1    30920  3072255 42
2    30920  2361327 42
3    30920  3879497 42
4    30920  3751623 42
... ... ... ...
999995 363319 121860 167
999996 363319 413801 167
999997 363319 415805 167
999998 363319 487803 167
999999 363319 655613 167
1000000 rows × 3 columns

Filteration

filter方法是通过一些布尔判断对分组后的内容进行筛选,返回一个原始对象的子集。

假设我们需要过滤得到用户点击广告数小于100的样本:

%%time
In[6]: df.groupby('user_id').filter(lambda x: len(x) < 100)CPU times: user 7.48 s, sys: 0 ns, total: 7.48 s
Wall time: 7.49 s
Out[6]:user_id creative_id ad_count
0    30920  567330 42
1    30920  3072255 42
2    30920  2361327 42
3    30920  3879497 42
4    30920  3751623 42
... ... ... ...
999828 363318 1779084 41
999829 363318 1985010 41
999830 363318 66606  41
999831 363318 1056195 41
999832 363318 1435072 41
838425 rows × 3 columns

可以发现,样本数目减少,每个用户点击广告数大于100的已被去除。

Apply

apply方法相比更加灵活,它可以完成上述的agg、transform以及filter,具体取决于传递给它的是什么。并且它可以使用自定义函数。

例如返回每个用户最大的广告id(即agg方法)。

%%time
def change_ad_count(df):return df['creative_id'].max()
In[7]: df.groupby('user_id').apply(change_ad_count)CPU times: user 4.93 s, sys: 0 ns, total: 4.93 s
Wall time: 4.93 s
Out[7]:
user_id
31        4440651
34        4266235
36        3999372
310       3042631
312       3904071...
363305    4187970
363311    4151703
363317    4307786
363318    4079814
363319    4389117
Length: 30025, dtype: int64

比较

因为上篇文章提到了Word2vec,通过每个用户所点击的广告来构造句子,因此需要将按照user_id进行分组,并将每个用户点击的广告构造一个列表。之前用了for循环,效率极慢,现在改用aggapply方法进行比较。

agg:

%%time
sentences = df.groupby(['user_id'])['creative_id'].agg(lambda x: x.tolist()).tolist()CPU times: user 4.13 s, sys: 64 ms, total: 4.2 s
Wall time: 4.2 s

apply:

%%time
sentences = df.groupby(['user_id'])['creative_id'].apply(lambda x: x.tolist()).tolist()
CPU times: user 4.18 s, sys: 52 ms, total: 4.23 s
Wall time: 4.23 s
%%time
sentences = df.groupby(['user_id']).apply(lambda x: x['creative_id'].tolist()).tolist()
CPU times: user 2.32 s, sys: 44 ms, total: 2.37 s
Wall time: 2.37 s

我们发现apply方法与agg相差无几,但我们若将creative_id放入lambda中,则效率更高。个人认为应该是和处理的对象不同。

df.groupby(['user_id'])['creative_id']<pandas.core.groupby.generic.SeriesGroupBy object at 0x7f1495e8d9e8>df.groupby(['user_id'])<pandas.core.groupby.generic.DataFrameGroupBy object at 0x7f1495e80a20>
## apply中再处理Series对象

总结

groupby是pandas最有效的方法之一,经常与aggtransformfilterapply相结合使用。

往期精彩回顾适合初学者入门人工智能的路线及资料下载机器学习及深度学习笔记等资料打印机器学习在线手册深度学习笔记专辑《统计学习方法》的代码复现专辑
AI基础下载机器学习的数学基础专辑获取一折本站知识星球优惠券,复制链接直接打开:https://t.zsxq.com/662nyZF本站qq群1003271085。加入微信群请扫码进群(如果是博士或者准备读博士请说明):

【Python基础】Pandas笔记---深入Groupby,它的功能没有你想的这么简单相关推荐

  1. Python基础入门笔记(二)

    前言 本文主要为 Python基础入门笔记(一)内容的补充. 一.迭代器和生成器 1.1 Python迭代器 迭代器是一个可以记住遍历的位置的对象. 迭代器对象从集合的第一个元素开始访问,直到所有的元 ...

  2. Python基础学习笔记三

    Python基础学习笔记三 print和import print可以用,分割变量来输出 import copy import copy as co from copy import deepcopy ...

  3. Python基础学习笔记之(二)

    Python基础学习笔记之(二) zouxy09@qq.com http://blog.csdn.net/zouxy09 六.包与模块 1.模块module Python中每一个.py脚本定义一个模块 ...

  4. Python基础学习笔记之(一)

    Python基础学习笔记之(一) zouxy09@qq.com http://blog.csdn.net/zouxy09 前段时间参加微软的windows Azure云计算的一个小培训,其中Pytho ...

  5. python基础操作笔记

    python基础操作笔记 第二章 变量和简单的数据类型 #2.1输出数据hello world print('--------------------------------------------- ...

  6. Python基础学习笔记(一)

    Python基础学习笔记(一) 基本数据类型   整型(int):1.2.10--   浮点型(float):1.2.2.4.10.00--   布尔型(bool):True.False   字符串( ...

  7. Python 基础学习笔记 03

    Python基础系列 Python 基础学习笔记 01 Python 基础学习笔记 02 Python 基础学习笔记 03 Python 基础学习笔记 04 Python 基础学习笔记 05 文章目录 ...

  8. 8.Python基础学习笔记day8-正则表达式、网络编程、进程与线程

    8.Python基础学习笔记day8-正则表达式.网络编程.进程与线程 一.正则表达式 ''' 1. [1,2,3,4]中任意取3个元素排列: A43 = 4x3x2 = 24itertools.pe ...

  9. Python基础知识笔记

    文章目录 Python基础知识 教程:https://www.liaoxuefeng.com/wiki/1016959663602400 内置函数:https://docs.python.org/3/ ...

最新文章

  1. IOS问题汇总:2012-12-18 UIAlertView+UIActionSheet
  2. java 匿名函数_Java 理论与实践,闭包之争
  3. 蚂蚁金服CTO程立:做工程要有“拧螺丝”的精神
  4. OJ1040:(递推思想高阶)数列求和1
  5. 计算几何 —— 二维几何基础 —— 三角形的面积
  6. 开源如何走向商业化?
  7. 堆排序、快速排序、归并排序总结
  8. was日志报检测到cpu饥饿
  9. C# .net实现发送手机短信功能
  10. Python中的字符串详解
  11. m3u8 文件代码片段.
  12. QT 在 macos 上的透明bug Qt::WA_TranslucentBackground
  13. 新手学Python之学习官网教程(二: Using the Python Interpreter)
  14. 神经网络建模的基本思想,人工神经网络建模步骤
  15. 汽车软件开发相关词汇
  16. 核磁共振 Nuclear Magnetic Resonance (NMR), H1-NMR, C13-NMR, DEPT, COSY 等分析技术
  17. 观战朝鲜vs巴西后,给中国足球的两点建议
  18. hdu 1849 nim博弈
  19. word2vec 中的数学原理详解(三)背景知识
  20. 炫“库”行动-人大金仓有奖征文—金仓数据库入门体验

热门文章

  1. 【产品分析】Microsoft MyPhone试用手记
  2. linux把用户添加到组
  3. luogu P5324 [BJOI2019]删数
  4. 给ThinkPad E470C 换个高分屏(1080P)
  5. Android逆向基础----Android Dalvik虚拟机
  6. 浅谈Python中的编码规则
  7. 学渣笔记之矩阵的导数与迹
  8. 图像预处理第8步:紧缩重排数字字符
  9. 虚函数和抽象函数的区别
  10. Latin1_General_BIN