查看原文
其他

Python 教学 | Pandas 函数应用(apply/map)【下】

Python教学 数据Seminar 2023-08-12


Python教学专栏,旨在为初学者提供系统、全面的Python编程学习体验。通过逐步讲解Python基础语言和编程逻辑,结合实操案例,让小白也能轻松搞懂Python!
>>>点击此处查看往期Python教学内容

本文目录

一、前言

二、映射函数——map()

  (一)函数映射

  (二)字典映射

三、全局函数应用——applymap()

  (一)工业数据中数值的清洗

四、总结

五、Python教程

本文共7394个字,阅读大约需要19分钟,欢迎指正!

Part1前言

上一期文章我们介绍了 Pandas 中的函数应用,学习了apply()函数的基本用法,其中重点讲解的是应用函数修改原有数据字段和生成新的数据字段,这是数据处理中使用频率最高的用法。不过我们的数据需求多种多样,很多时候apply()函数不能满足我们的需求,所以本期文章我们就来学习其他的函数应用的工具函数。

本教程基于 pandas 1.5.3 版本书写。本文中所有 Python 代码均在集成开发环境 Visual Studio Code (VScode) 中使用交互式开发环境 Jupyter Notebook 中编写,本文分享的代码请使用 Jupyter Notebook 打开。

💡后台回复关键词“20230714”即可获取本文所有演示代码以及演示用的数据。

Part2映射函数——map()

在 Python 语言中,内置函数map()可以将一个函数应用于可迭代对象的每个元素,并返回一个结果迭代器。它的基本作用是将一个函数应用于可迭代对象的每个元素,生成一个新的可迭代对象。下面的代码简单演示了 Python 内置函数 map()的功能和基本用法。

# 创建一个列表,其中保存若干个数值
资金列表 = [50,120,190,100,60]

def 货币数值转换(Value):
    # 输出金额数字,返回除以汇率 7 的结果(四舍五入取 3 位小数)
    return round(Value/7, 3)

# 使用 map() 函数将上面的自定义函数应用到列表“资金列表”中的每个值中
Result = map(货币数值转换, 资金列表)

# 输出查看结果
print(Result)
# <map object at 0x000002A8C44CCEE0>

# 使用 list() 函数后转为列表后再输出查看
print(list(Result))
# [7.143, 17.143, 27.143, 14.286, 8.571]

可以看出上述代码中map(货币数值转换, 资金列表)的功能就是将可迭代对象资金列表的每一个元素都传入到函数货币数值转换中,并将函数的所有返回结果都存储在一个可迭代的 map 对象中返回。最后我们使用 list() 函数将返回的结果转为列表就可以看到结果中的值了。


💡 什么是可迭代对象?可迭代对象是指可以被逐个访问和处理的对象。通常是一组元素的集合,例如列表、元组、字典等。使用迭代器或者循环语句,可以依次获取可迭代对象中的每个元素,对其进行操作或者处理。换句话说,可迭代对象就像是一个装满东西的盒子,你可以一个一个地取出里面的东西并对其进行操作,也可以将其转为合适的组合数据类型。

Pandas 中也存在一个map()函数,功能和内置的 map()函数十分相似,和上一期介绍的 apply()函数也有一定的相似性,下面我们来学习 Pandas 中map()函数的功能和用法

<函数调用方>.map(arg, na_action)
参数名称参数含义
arg必要的参数,可以是一个函数(内置函数、第三方函数、自定义函数、匿名函数均可),也可以是一个字典或 Series。当 arg 是函数时,表示将传入的函数应用在函数调用方并返回结果;当 arg 是字典时,表示根据传入的字典,将调用方中的值(字典的“键”)映射为字典中的值(字典的“值”)。
na_action不常用的参数,只有当 arg 参数是字典时,该参数才会生效。默认值是 None,表示对调用方中的所有值进行映射;而当参数值为 'ignore' 时,表示直接忽略缺失值,无论映射字典中是否包含缺失值的映射,都返回空值。

apply()函数不同,Pandas 中的map()函数是 Series 类型独有的属性函数,所以其调用方只能是一维 Series 类型,即调用方是一个字段。另外从上表中可以知道,arg参数值可以是函数,也可以是字典,两种参数类型所对应的函数功能和用法也不相同,下面我们分开来介绍。

1函数映射

我们在上期文章中已经介绍过apply()函数,当apply()函数的调用方是Series类型时,也就是使用Series.apply()函数时与使用map()函数的功能和用法几乎一样,下面我们来做一个演示。

# 读取演示用的数据
import pandas as pd
data = pd.read_csv('./农民专业合作社数据样例100条.csv').drop('地址', axis=1)
data

如果需要将注册资金中的值都进行四舍五入并转为整数型,那么我们既可以使用apply()函数,也可以使用map(),使用apply()函数的代码如下。

# 使用 apply() 应用匿名函数
data['注册资金'] = data['注册资金'].apply(lambda x: round(x))  # round() 是四舍五入函数
data     # 查看结果

使用map()函数完成同样操作的代码如下。

# 先重新读取数据
data = pd.read_csv('./农民专业合作社数据样例100条.csv').drop('地址', axis=1)
# 使用 map() 映射匿名函数
data['注册资金'] = data['注册资金'].map(lambda x: round(x))
data

可以看到当在map()函数中应用函数时,其使用方式和效果与Series.apply()函数完全一致。

2字典映射

与 Python 内置的map()函数不同,Series.map()函数还可以根据根据传入的字典进行映射,常用于数据处理过程中将某名称字段中的值替换为名称代码。例如在上述演示用的数据中,我们可以把行业门类字段中的行业门类名称映射为行业门类的代码,这样不仅可以使数据更加规整,在数据量特别大的情况下还可以在一定程度上减少数据占用空间的大小。

如果使用apply()函数完成上述操作,那么我们需要在函数中一 一判断行业门类的名称,然后替换成对应的行业门类代码。这样就会产生一个含有多分支结构的函数,书写起来比较麻烦。如果是用 Pandas 的map()函数来处理,我们只需生成一个“键”是行业门类名称,“值”是行业门类代码的字典,那么剩下的任务再交给map()函数就会变得十分简单。下面我们就演示将行业门类字段中的行业门类名称映射为行业门类代码。

# 已知数据中共包含 5 种行业门类名称,那么需要先创建包含这 5 种映射的字典
Industry_category = {'农、林、牧、渔业':'A',
                     '批发和零售业':'F',
                     '租赁和商务服务业':'L',
                     '文化、体育和娱乐业':'R',
                     '信息传输、软件和信息技术服务业':'I',}
# 然后使用 map() 函数根据字典进行映射(直接在原字段上修改)
data['行业门类'] = data['行业门类'].map(Industry_category)  # 直接将与函数调用方对应的映射字典传入 map() 函数即可
# 查看映射后的数据
data

如上图所示,map()函数会根据传入的字典将调用map()函数的字段data['行业门类']中的值映射为字典的“值”。需要注意的是,如果当传入的字典不能覆盖字段中所有的数据值时,无法进行映射的数据值会直接被替换为空值NaN,示例如下。

# 重新读取数据
data = pd.read_csv('./农民专业合作社数据样例100条.csv').drop('地址', axis=1)
# 已知数据中共包含 5 种行业门类名称,定义映射字典时只书写其中四种情况的映射
Industry_category = {'农、林、牧、渔业':'A',
                     '批发和零售业':'F',
                     '租赁和商务服务业':'L',
                     '文化、体育和娱乐业':'R'}
# 然后使用 map() 函数根据字典进行映射(直接在原字段上修改)
data['行业门类'] = data['行业门类'].map(Industry_category)  # 直接将与函数调用方对应的映射字典传入 map() 函数即可
# 查看映射后的数据
data

使用map()函数 + 字典进行字段内的数据映射非常简单,麻烦的是创建映射用的字典,如果一个字段中包含多种数据值,那么纯靠手动输入来创建字典的话效率很低。一般情况下我们都有一张名称与代码的对照表,如果有对照表,我们就可以使用 Pandas 根据对照表去生成一个映射字典,例如这里有一个行业门类名称与行业门类代码的对照表,文件名为行业门类代码对照表(部分).xlsx,表格内容如下图所示。

下面我们使用 Pandas 读取上表并快速创建一个映射字典,代码和结果如下。

## Pandas 快速创建映射字典
Map_dict = pd.read_excel('./行业门类代码对照表(部分).xlsx').set_index('行业门类名称').to_dict()['行业门类代码']
Map_dict     # 结果如下
"""
{'农、林、牧、渔业': 'A',
 '采矿业': 'B',
 '制造业': 'C',
 '电力、热力、燃气及水生产和供应业': 'D',
 '批发和零售业': 'F',
 '交通运输、仓储和邮政业': 'G',
 '住宿和餐饮业': 'H',
 '信息传输、软件和信息技术服务业': 'I',
 '租赁和商务服务业': 'L',
 '科学研究和技术服务业': 'M',
 '水利、环境和公共设施管理业': 'N',
 '居民服务、修理和其他服务业': 'O',
 '文化、体育和娱乐业': 'R'}
"
""

上述代码中,pd.read_excel('./行业门类代码对照表(部分).xlsx')表示读取对照表;set_index('行业门类名称')表示将“行业门类名称”字段设置为行索引;to_dict()则表示将表格数据转成一个包含行索引与各个字段之间映射的嵌套字典,这个嵌套字典如下。

## 生成包含行索引与各个字段之间映射的嵌套字典
pd.read_excel('./行业门类代码对照表(部分).xlsx').set_index('行业门类名称').to_dict()
# 结果如下
"""
{'行业门类代码': {'农、林、牧、渔业': 'A',
  '采矿业': 'B',
  '制造业': 'C',
  '电力、热力、燃气及水生产和供应业': 'D',
  '批发和零售业': 'F',
  '交通运输、仓储和邮政业': 'G',
  '住宿和餐饮业': 'H',
  '信息传输、软件和信息技术服务业': 'I',
  '租赁和商务服务业': 'L',
  '科学研究和技术服务业': 'M',
  '水利、环境和公共设施管理业': 'N',
  '居民服务、修理和其他服务业': 'O',
  '文化、体育和娱乐业': 'R'}}
"
""

最后再根据字典键“行业门类代码”取出字典即可,对应的代码为['行业门类代码']

如果觉得以上代码复杂不易懂,那么我们也可以不生成字典,因为map()函数还可以接收一维 Series 作为映射用的对象,我们只需传给map()函数一个索引是“行业门类名称”,数据值是“行业门类代码”的 Series 即可。完整代码如下。

## 使用 Series 映射
# 重新读取数据
data = pd.read_csv('./农民专业合作社数据样例100条.csv').drop('地址', axis=1)
# 根据对照表生成 Series 进行映射
Industry_category = pd.read_excel('./行业门类代码对照表(部分).xlsx').set_index('行业门类名称')['行业门类代码']
# 然后使用 map() 函数根据字典进行映射(直接在原字段上修改)
data['行业门类'] = data['行业门类'].map(Industry_category)  # 直接将与函数调用方对应的映射字典传入 map() 函数即可
# 查看映射后的数据
data

以上就是 Pandas 中用于字段值映射的 map() 函数详细使用方法。

Part3全局函数应用——applymap()

已经介绍的apply()函数和map()函数都是针对数据表中的字段进行处理的函数,当我们需要一次性对数据表中每个数据值都执行某种操作时就可以使用 Pandas 中的全局应用函数——applymap()函数。下面我们以一个简单的案例来介绍一下这个函数的功能和用法。

1工业数据中数值的清洗

在不少统计数据或者统计年鉴数据表中,由于处理方式不正确等原因导致很多指标的值被记录为浮点数,即使是一些描述数量的指标中也会出现小数,以工业企业数据为例,下面我们先读取数据。

## 读取 100 条工业企业样例数据
Industry = pd.read_csv('./2005年工业数据100条.csv', dtype=str)
Industry

如上图所示,即使在读取数据时设置了所有数据的格式为字符型(dtype=str),但是在大量的存储数值的字段中仍含有类似99.00.0的数值,此时我们完全可以将这些数值清洗为整数990,同时又不影响正常的小数。如果使用apply()函数或map()函数,那么工业数据中的 100 多个字段都要进行同样的处理,不仅操作起来十分麻烦,而且代码冗余度非常高。这个时候applymap()函数就派上用场了,我们可以定义一个去除数值中末尾“.0”的函数,然后借用applymap()将这个自定义函数应用在工业数据的每一个单元格中,就可以快速达到目的,实现代码如下。

import re     # 导入正则表达式
def Clean_Number(value):
    # 先判断是否是空值,如果是空值,则返回空值。不做处理
    if pd.isna(value):
        return None     # 或者 return np.nan
    else:
        # 若不是空值,则去除数值末尾的  .0
        return re.sub('\.0$''', value)

# 最后借助 applymap() 应用在表中所有数据值
Industry = Industry.applymap(Clean_Number)
Industry    # 查看处理后的结果

以上是使用applymap()全局应用自定义函数的示范,如果你想让代码更简洁,也可以改用匿名函数,代码如下。

import re     # 导入正则表达式
Industry = Industry.applymap(lambda x: re.sub('\.0$''', x) if not pd.isna(x) else None)

可以看到经过处理后,表中所有的数据值中末尾的“.0”都被清洗掉。根据笔者的经验,使用全局应用函数applymap()需要注意以下几点:

  1. applymap()是表格全局应用函数,所以函数调用方必须是 DataFrame 类型。
  2. applymap()函数的功能是返回一个处理后的数据,并不会直接修改调用方,所以使用时一定要将结果赋值给变量,否则计算量比较大时白忙活一场。
  3. 由于是对表中单元格内的数据进行操作,所以要应用的函数最好是一个输入,一个输出。
  4. 使用applymap()时一定要注意,大部分时候我们表格中的数据类型是不一致的,数据的含义、形式也是不一致的,应用函数时要考虑待应用的函数会不会修改不相关的数据,造成不必要的麻烦。

好了,以上就是 Pandas 中全局应用函数applymap()的使用方法。

Part4总结

本文详细介绍了 Pandas 中两个实用性很强的函数——map()applymap(),相比于apply()函数,本期介绍的两个函数的使用方法要简单不少,但是他们的实用性却一点都没有打折扣。函数应用是 Python 处理表格数据的一个利器,掌握函数应用的同学,应该都能轻松解决常见的数据需求,所以掌握函数应用十分关键。

Part5Python 教程

 (向下滑动查看)



星标⭐我们不迷路!想要文章及时到,文末“在看”少不了!

点击搜索你感兴趣的内容吧

往期推荐


Python 教学 | Pandas 函数应用(apply/map)【上】

Python 教学 | Pandas 数据匹配(含实操案例)

Python 教学 | Pandas 数据合并(含目录文件合并案例)

Python 教学 | Pandas 表格字段类型精讲(含类型转换)

Python 教学 | Pandas 表格数据行列变换

Python 教学 | Pandas 缺失值与重复值的处理方法





数据Seminar




这里是大数据、分析技术与学术研究的三叉路口


文 | 《社科领域大数据治理实务手册》


    欢迎扫描👇二维码添加关注    

点击下方“阅读全文”了解更多

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存