在逛Kaggle的时候发现了一篇不错的Pandas技巧,我将挑选一些有用的并外加一些自己的想法分享给大家。本系列虽基础但带仍有一些奇怪操作,粗略扫一遍,您或将发现一些您需要的技巧。

纸上得来终觉浅,绝知此事要躬行,所谓的熟练使用Pandas是建立在您大致了解每个函数功能上,希望本系列能给您带来些许收获。

本篇所涉及知识点:

  1. map、apply、applymap
  2. groupby
  • MultiIndex DataFrame
  • 统计函数、累计函数
  • agg、transform、filter

map、apply和applymap

先总结一下:

  • map()适用于Series,相当于将Series中的每个元素作为参数带入指定函数,注意是Series中的每个元素
  • apply()适用于DataFrame的行或者列,将整个行列作为参数带入指定函数,注意是DataFrame的行或列,即整个Series
  • applymap()适用于DataFrame中的每个元素,注意是DataFrame中的每个元素

map

Series.map(self,arg,na_action)作用于一个Series上,DataFrame的一行或者一列都是Series。

map()最主要的用途是实现以下两个功能:

  1. 字典替换
  2. 简单的有参函数(有且仅有一个参数,其实就是Series里的每个元素了)
df = pd.DataFrame([['小王', 'm', 80], ['小红', 'f', 90], ['小明', 'x', 85], [                  '小林', np.nan, 83], ], columns=['student', 'sex', 'score'])df

  student sex score0 小王     m   801 小红     f   902 小明     x   853 小林     NaN 83

使用dict对Series进行映射替换。如果Series中的值并没有在给定字典中出现,那该值会被替换为np.nan。

下面案例中我想把sex='m'替换为"男",sex='f'替换为"女",小明的sex='x'我并没有在字典中给出映射,所以map()后直接被替换为np.nan。

my_dict = {'m': '男', 'f': '女'}df['dict'] = df['sex'].map(my_dict)df

  student sex score  dict0 小王     m   80     男1 小红     f   90     女2 小明     x   85     NaN3 小林     NaN 83     NaN

如果对于未给定的键我们也想统一替换,那就要在字典中设置默认值,接下来教大家如何设置字典默认值。

from collections import defaultdict

def ret():  """  describe:因为defaultdict接受一个工厂函数,所以咱们直接创建一个函数,返回Unknown  return  :str  """    return 'Unknown'

# 生成一个新的dict,使用defaultdict函数并传入我们创建的工厂函数# 当然你也可以填入其他的数据类型,具体类型及对应默认值如下:# int->0,set->set(),str->'',list->[]my_hasdefault_dict = defaultdict(ret)

# 将之前的dict再传入新的含有默认值的dictmy_dict = {'m': '男', 'f': '女'}for k, v in my_dict.items():    my_hasdefault_dict[k] = v

# 因为小林的sex=np.nan没有在给定字典中出现,所以也替换成了默认值df['hasdefault_dict'] = df['sex'].map(my_hasdefault_dict)# 在map中设置na_action='ignore'时就会忽略np.nan,不进行任何操作df['ignore_na'] = df['sex'].map(my_hasdefault_dict, na_action='ignore')df

  student sex score  dict   hasdefault_dict   ignore_na0 小王     m   80     男     男                男1 小红     f   90     女     女                女2 小明     x   85     NaN    Unknown          Unknown3 小林     NaN 83     NaN    Unknown          NaN

map()可以应用一些简单的自定义函数。

def my_cut(x):  """  descrieb:对成绩分桶  return  :str  """    if x >= 90:        return 'score>=90'    if 85 <= x 90:        return '85<=score<90'    else:        return 'score<85'

# map里面只要写入函数名就可以,不用括号df['my_cut'] = df['score'].map(my_cut)

# 上面的判断比较简单,所以我们可以改写成lambda的形式# lambda的格式是 lambda x:结果1 if 条件1 else 结果2 if 条件2 else 结果3df['cut_lambda'] = df['score'].map(    lambda x: 'score>=90' if x >= 90 else '85<=score<90' if 85 <= x 90 else 'score<85')

# 当然上面只是为了演示map,其实用Pandas自带的cut更方便# right=True是右闭区间,right=False是左闭区间df['cut'] = pd.cut(df['score'], bins=[-np.inf, 85, 90, np.inf],                   labels=['score<85', '85<=score<90', 'score>=90'], right=False)df

  student  sex   score   my_cut        cut_lambda     cut0 小王      m     80     score<85       score<85       score<851 小红      f     90     score>=90      score>=90      score>=902 小明      x     85     85<=score<90   85<=score<90   85<=score<903 小林      NaN   83     score<85       score<85       score<85

apply

  • DataFrame.apply(self, func, axis=0, raw=False, result_type=None, args=(), **kwds)
  • Series.apply(self, func, convert_dtype=True, args=(), **kwds)
  • GroupBy.apply(self, func, args, *kwargs)
  • ... apply()有好多,我们最主要的就是用到上面这三种结构下的apply()

apply()不可以像map()那样使用字典映射替换字符,但是可以通过字典调用函数(起码我还没用到过)。

df = pd.DataFrame([['小王', 'm', 80, '汉族'], ['小红', 'f', 90, '回族'], ['小明', 'm', 85, '汉族'], [                  '小林', 'f', 83, '保安族'], ], columns=['student', 'sex', 'score', 'nationality'])df

  student sex score  nationality0 小王     m   80     汉族1 小红     f   90     回族2 小明     m   85     汉族3 小林     f   83     保安族

为了证明apply是真的可以字典调用函数,我们来做个没有实际意义的测试,结果是可行的。

# 如果sex=m则后面拼接ale,如果sex=f则后面拼接emalefunc_dict = {'m': lambda x: x+'ale', 'f': lambda x: x+'emale'}df['sex'].apply(func_dict)

m  0      male   1      fale   2      male   3      falef  0    memale   1    female   2    memale   3    female

是不是感觉和map出来的效果有点不一样?继续往下看。

# apply可以添加参数,如果用*args接收参数,那就使用元组的方式按位置索引,**kwargs就按照字典方式按key索引

def birth(x, **kwargs):    return f'{x}身高{kwargs["height"]}cm'

# 调用随机函数,指定相同的范围df['apply_birth'] = df['student'].apply(    birth, height=np.random.randint(160, 180))df['map_birth'] = df['student'].map(    lambda x: f'{x}身高{np.random.randint(160,180)}cm')df

  student sex score nationality apply_birth          map_birth0 小王     m   80     汉族       小王身高164cm         小王身高171cm1 小红     f   90     回族       小红身高164cm         小红身高178cm2 小明     m   85     汉族       小明身高164cm         小明身高169cm3 小林     f   83     保安族     小林身高164cm         小林身高175cm

我们来对比一下map()和apply(),同样的想法却出现不一样的效果。apply()出来的身高都是一样的,而map()出来的身高是不一样的。所以就如之前的总结所说一般,map()作用于Series中的每个元素,apply()作用于整个Series

我们看一下apply()是如何作用于多行/列的,下面案例中我们调取多列来实现对少数民族同学加5分的操作。

# 多列选择后使用apply,可以使用位置索引选择列,比如这里x[1]代表的是df['score']df['加分后成绩'] = df[['nationality', 'score']].apply(    lambda x: x[1]+5 if x[0] != '汉族' else x[1], axis=1)df

  student sex score nationality 加分后成绩0 小王     m   80     汉族        801 小红     f   90     回族        952 小明     m   85     汉族        853 小林     f   83     保安族      88

上面案例中为什么要用axis=1呢,下面的例子,请各位意会一下。


  • axis=0是对行计算,返回的行数与原表列索引数相等。
  • axis=1是对列进行计算,返回的行数应该与原表行索引数相等。

applymap

applymap()是DataFrame的方法,只能作用于整个DataFrame。

df = pd.DataFrame([[4, 9]] * 3, columns=['A', 'B'])df

  A B0 4 91 4 92 4 9

df.applymap(lambda x: x+2)

  A B0 6 111 6 112 6 11

# 错误示范df['A'].applymap(lambda x: x+2)

AttributeError: 'Series' object has no attribute 'applymap' 

不得不会的groupby

敲黑板!pandas中相当重要的一环!

DataFrame.groupby(self, by=None, axis=0, level=None, as_index: bool = True, sort: bool = True, group_keys: bool = True, squeeze: bool = False, observed: bool = False)

返回的是DataFrameGroupBy对象,groupby的基本流程如下:

  1. 根据某些标准将数据分成组。
  2. 对每个组独立应用一个函数。
  3. 将结果组合到数据结构中。
import random# 以下所有案例皆基于这个DataFramecat = ['product1', 'product2', 'product3']df = pd.DataFrame(list(zip(pd.date_range(start='2020/1/1',end='2020/1/10'),random.choices('ABC', k=10), random.choices(cat, k=10), random.choices(    range(1, 100), k=10))), columns=['date','salesman', 'product', 'volume'])df

  date       salesman  product  volume0 2020-01-01 C         product3 491 2020-01-02 C         product3 302 2020-01-03 B         product1 353 2020-01-04 A         product3 634 2020-01-05 B         product2 265 2020-01-06 A         product3 376 2020-01-07 B         product1 507 2020-01-08 C         product2 168 2020-01-09 A         product2 459 2020-01-10 B         product3 22

看到ABC,偷偷教大家一个Python快速打出26个英文字母的方法。

from string import ascii_uppercase # 26个大写字母

from string import ascii_lowercase # 26个小写字母

from string import ascii_uppercase # 52个字母,前26小写,后26大写

只要直接print(ascii_uppercase/ascii_lowercase/ascii_uppercase)就可以了。

我们想看一下销售员的总销量。

df.groupby(by='salesman').sum()

         volumesalesman A         145B         133C         95

再看一下各销售员每类产品的总销量。

groupby可以根据多个标签分组,但是注意,默认情况下多标签分组后获得的DataFrameGroupBy是含有MultiIndex的。

# 默认情况下sort=True,Pandas会将分组标签自动排序,也可以设置为False,加快groupby速度,但大多数情况还是让它排序吧df_mulitindex = df.groupby(by=['salesman','product'],sort=True).sum()df_mulitindex

                     volumesalesman  product A         product2   45          product3   100B         product1   85          product2   26          product3   22C         product2   16          product3   79

即使是MultiIndex DataFrame也一样可以groupby的,你可以用level也可以用by来控制分组的标签。

  • 如果原DataFrame是普通索引,那只能设置by参数
  • 如果是MultiIndex DataFrame,那by和level都可以设置,但是更推荐用level,别问为什么,问就是不知道
df_mulitindex.groupby(level='product',sort=False).sum()

         volumeproduct product2 87product3 201product1 85

我想先不进行聚合运算,看看分好组的样子可以吗?接下来几个函数可以帮到你。

  • df.groupby().first()返回每个分组的第一行
  • df.groupby().last()返回每个分组的最后一行
  • df.groupby().nth(n)返回每个分组的第n行
  • df.groupby().head(n)返回每个分组前n行
  • df.groupby().tail(n)返回每个分组后n行

groupby的第一步操作,是按照指定字段分组,所以首先我将原DataFrame对列索引进行排序,方便大家理解。

# ascending=False降序/True升序df.sort_values(by=['salesman','product','volume'],ascending=[True,True,False])

上述函数我就不一一展示,就举一例子,查询各业务员各类产品销量最高的那行数据。

df.sort_values(by='volume',ascending=False).groupby(by=['salesman','product']).first()

MultiIndex

多了几个标签作为行索引而已,跟普通的Index没什么的,只是索引方式有那么一点点的小不同,看下去就知道。

# agg不知道是什么不要紧,下面会讲df_multi =df.groupby(by=['salesman','product']).agg(vol_sum=('volume','sum'),vol_max=('volume','max'))df_multi

                  vol_sum   vol_maxsalesman  product  A         product2 45       45          product3 100      63B         product1 85       50          product2 26       26          product3 22       22C         product2 16       16          product3 79       49

还记得loc吗!标签索引,忘记了回头看一下第一篇。

# 最基本的索引其实就是加上括号,把元组作为索引条件df_multi.loc[('B','product3')]# 这样返回的是Series

vol_sum    32vol_max    32Name: (B, product3), dtype: int64

df_multi.loc[[('B','product3')]]# 这样返回的是DataFrame

                  vol_sum vol_maxsalesman product  B        product3 32      32

# 切片,一样的df_multi.loc[('B','product3'):('C','product3'),'vol_max'] # 这样返回的是Series

salesman  product B         product3    32C         product2    96          product3    72Name: vol_max, dtype: int64

df_multi.loc[('B','product3'):('C','product3'),['vol_max']] # 这样返回的是DataFrame

                     vol_maxsalesman  product B         product3   32C         product2   96          product3   72

MultiIndex DataFrame怎么变为一维表?

  • 分组时加入as_index=False使返回的DataFrame直接变为一维表
  • 或使用reset_index()将含有MultiIndex的DataFrame变为一维表
df.groupby(by=['salesman','product'],as_index=False).sum()df.groupby(by=['salesman','product']).sum().reset_index()

出来的效果都是这样

  salesman product    volume0 A        product2   451 A        product3   1002 B        product1   853 B        product2   264 B        product3   225 C        product2   166 C        product3   79

MultiIndex DataFrame怎么变成二维表?

  • unstack()
df.groupby(by=['salesman','product']).sum().unstack()

          volumeproduct   product1 product2 product3salesman   A         NaN      45.0     100.0B         85.0     26.0     22.0C         NaN      16.0     79.0

DataFrameGroupBy可以用的统计函数

Function Description
mean() 平均值
sum() 求和
size() 返回group的大小
count() 计数
std() 标准差
var() 方差
sem() 标准误
describe() 超级方便的数据表述函数,包括了很多常见统计量
first() 组中的第一个值
last() 组中的最后一个值
nth() 取n个值
min() 最小值
max() 最大值

如果确定要对某一列进行聚合运算,强烈建议先指出该列再使用聚合函数,可以提高效率。

df.groupby(by='salesman').describe()

         volume          count mean        std       min   25%   50%  75%   maxsalesman        A         1.0   3.000000    NaN       3.0   3.00  3.0  3.0    3.0B         6.0   46.166667   28.596620 2.0   32.75 47.5 66.0   80.0C         3.0   77.666667   16.258331 65.0  68.50 72.0 84.0   96.0

累计函数

累计函数,相信大家看一下案例就能理解了!

方法名 函数功能
cumsum() 依次给出前1、2、…  、n个数的和
cumprod() 依次给出前1、2、…  、n个数的积
cummax() 依次给出前1、2、…  、n个数的最大值
cummin() 依次给出前1、2、…  、n个数的最小值
d = {    'salesperson': [        'Nico',        'Carlos',        'Juan',        'Nico',        'Nico',        'Juan',        'Maria',        'Carlos',    ],    'item': ['Car', 'Truck', 'Car', 'Truck', 'cAr', 'Car', 'Truck', 'Moto'],}df = pd.DataFrame(d)df

  salesperson  item0 Nico         Car1 Carlos       Truck2 Juan         Car3 Nico         Truck4 Nico         cAr5 Juan         Car6 Maria        Truck7 Carlos       Moto

df['count_by_person'] = df.groupby(by='salesperson').cumcount() + 1df['count_by_item'] = df.groupby(by='item').cumcount() + 1df['count_by_both'] = df.groupby(by=['salesperson', 'item']).cumcount() + 1df

agg、transfrom、filter

agg

agg()是aggregate()的别称,就像isna()是isnull()的别称一样。

agg()主要用在DataFrameGroupBy上,当然也可以用在DataFrame上,是用来在多列上计算不同函数的。

# 用法一# 直接使用列表列出所要使用的统计函数,具体可用函数已经在上面表中列出# 可以用字符串形式表示函数,也可以直接打函数名不加引号,如果报错就打np.函数名(毕竟pandas是基于numpy)df.groupby(by='salesman')['volume'].agg(['sum','max'])

          sum   maxsalesman  A         145   63B         133   50C         95    49

# 用法二# 可以自定义列名,格式是agg(name=(column,func))df.groupby(by='salesman').agg(vol_max=('volume','max'),vol_sum=('volume','sum'))

          vol_max  vol_sumsalesman  A         63       145B         50       133C         49       95

# 方法三# 您细细品一下效果df.groupby(by='salesman').agg({'volume':['max','mean'],'date':['min','max']})

          volume         date          max  mean      min        maxsalesman    A         63   48.333333 2020-01-04 2020-01-09B         50   33.250000 2020-01-03 2020-01-10C         49   31.666667 2020-01-01 2020-01-08

当然,agg()也可以自定义函数。

# 每个人的销量乘以100再求和df.groupby(by='salesman').agg({"volume":lambda x:sum([i*100 for i in x])})

      volumesales man A     14500B     13300C     9500

agg也是可以用在DataFrame或Series上,但个人感觉没样啥用,还不如直接用apply。

df[['volume']].agg(['max','min'])

    volumemax 63min 16

transform

转换,有时候还是有点用的,我们看一下实例。

# 我们重新创建一个DataFrame,适用于transform和filter的df_temp = pd.DataFrame([['2020/1/1', 'A', 100], ['2020/2/1', 'A', 50], ['2020/3/1', 'A', np.nan], ['2020/1/1',                                                                                                   'B', 1000], ['2020/2/1', 'B', 3000], ['2020/3/1', 'B', 5000]], columns=['date', 'salesman', 'volume'])df_temp

  date       salesman volume0 2020/1/1   A        100.01 2020/2/1   A        50.02 2020/3/1   A        NaN3 2020/1/1   B        1000.04 2020/2/1   B        3000.05 2020/3/1   B        5000.0

我们可以看到A业务员2020/3/1的销量为np.nan,没有记录,如果我们需要对数据进行清洗该怎么办呢?

直接删除吗,这不是最佳的选项。如果要填充,直接用volume整列的平均值?那这样对B业务员是不是很吃亏?(只是举例使用,真实情况下我们要和数据提供方确认数据质量)

所以我们可以尝试用A的均值来填充她的缺失值。

df_temp.volume = df_temp.groupby(by='salesman')['volume'].transform(lambda x:x.fillna(x.mean()))df_temp

  date     salesman volume0 2020/1/1 A        100.01 2020/2/1 A        50.02 2020/3/1 A        75.03 2020/1/1 B        1000.04 2020/2/1 B        3000.05 2020/3/1 B        5000.0

filter

看名字就知道是过滤,可以自定义函数。

def filter_loser(x):    """    describe:筛选掉总销售量小于300的业务员    return    """    return x['volume'].sum()>=300

df_temp.groupby(by='salesman').filter(filter_loser)

 date      salesman volume3 2020/1/1 B        1000.04 2020/2/1 B        3000.05 2020/3/1 B        5000.0

DataFrameGroupBy是可以使用apply()的,用法如之前所述一致。

往期回顾

一分钟一个Pandas小技巧(一)

臭不要脸环节

听说关注这个公众号的人都...都...都怎么样你自己说!

?分享、点赞、在看,给个三连呗!?

pandas apply lambda_一分钟一个Pandas小技巧(二)相关推荐

  1. pandas apply lambda_数据分析必备!Pandas实用手册(PART III)

    这一系列的对应代码,大家可以在我共享的colab上把玩, ? https://colab.research.google.com/drive/1WhKCNkx6VnX1TS8uarTICIK2ViPz ...

  2. 3分钟学会python_3分钟学会一个Python小技巧

    Python时间日期转换在开发中是非常高频的一个操作,你经常会遇到需要将字符串转换成 datetime 或者是反过来将 datetime 转换成字符串. datetime 分别提供了两个方法 strp ...

  3. 程序员的反击!每天一个离职小技巧

    作者 | 梦想橡皮擦 来源 | 非本科程序员(ID:htmlhttp) 写在前面 俗话说的好,代码写的少,离职少不了. 最近畅游互联网,发现一些离职小技巧,读后,内心被深深的打动了,但是细细的品过之后 ...

  4. vob转mp4,每天一个实用小技巧

    vob转mp4,vob的英文全称是Video Object,它是DVD视频媒体使用的容器格式,vob格式擅长将数字视频.音频.字幕.菜单等多个元素复用在流格式中.而且vob格式的文件可以被加密保护.经 ...

  5. 每天一个前端小技巧——生成gif动图下载

    每天一个前端小技巧--生成gif动图下载 动态热图的展现,分别展现某个时间段的热图时间变化,例如:最近一周七天内,每天的热图分布变化图:这个动态变化的图生成一个gif图提供下载是否可行? 实现方案: ...

  6. 每天一个脱发小技巧 | Eclipse环境下spotbugs的安装配置和详细使用方法

    每天一个脱发小技巧 | Eclipse环境下spotbugs的安装配置和详细使用方法 SpotBugs介绍 Eclipse环境下SpotBugs安装 SpotBugs的使用 其他 SpotBugs介绍 ...

  7. linux pandas教程_十分钟入门 Pandas

    # 十分钟入门 Pandas 本节是帮助 Pandas 新手快速上手的简介.烹饪指南里介绍了更多实用案例. 本节以下列方式导入 Pandas 与 NumPy: In [1]: import numpy ...

  8. python unique函数_每30秒学会一个Python小技巧,GitHub星数4600+

    作者 | xiaoyu,数据爱好者来源 | Python数据科学(ID:PyDataScience)很多学习Python的朋友在项目实战中会遇到不少功能实现上的问题,有些问题并不是很难的问题,或者已经 ...

  9. word关闭未响应_写了两小时的文档没保存?!一个word小技巧来拯救

    不知道大家有没有遇到过这样的情况: 写了好久的word文档没来的及保存突然崩溃了 刚保存好的文档不小心被删除.被覆盖 老Y相信不管是哪种情况,一旦遇上想死的心都有了. 其实word中有一个小技巧,可以 ...

最新文章

  1. 10行代码实现目标检测
  2. 测试服务器最大链接数_JMeter压力测试集合点教程
  3. 十个不可不看的Matlab GUI
  4. mysql 监控 开源_强大的开源企业级数据库监控利器Lepus
  5. MySQL数据库MyISAM和InnoDB存储引擎的比较
  6. redis 学习笔记(6)-cluster集群搭建
  7. CAD迷你画图 for mac
  8. photoshop cs3 无法复制文字的解决方案
  9. xshell网站打不开
  10. C#语言实例源码系列-加密解密RAR文件
  11. 基于java+springboot+mysql的中小型超市进销存管理系统
  12. 我的世界学园都市java_我的世界学园都市地图整合包
  13. ionic start myApp 报错
  14. 如何用计算机学唱歌,男孩学唱歌教程 男生如何练习唱歌?
  15. linux图形加速驱动下载,Linux 安装emby 并开启nvidia nvenc 硬件加速转码
  16. 二字动词 复盘赋能_落地、赋能、共创、共建、复盘,互联网圈的漂亮词儿,你懂几个?...
  17. 30套最实用JAVA学习视频教程合集
  18. 上网部署(锐捷交换机)
  19. unity 输入框弹出输入法_国产输入法那么多,我为什么选择了「不接地气」的 Gboard?...
  20. 汇编语言知识点总结之五:第五章《[bx]和loop指令》

热门文章

  1. 【深度学习】基于Numpy实现的神经网络进行手写数字识别
  2. 【EOS】EOS环境基础
  3. CIDR的IP地址与可分配的IP地址辨析
  4. 关于DMA的两个小知识点
  5. 微程序控制器的组成及原理总结
  6. Tensor的填充与复制
  7. tensorflow : 队列管理 FIFOQueue amp;amp; RandomShuffleQueue
  8. 图解操作系统系列-概述
  9. Freeswitch mod 安装
  10. apache worker性能调优