[关闭]
@1007477689 2020-05-18T03:36:51.000000Z 字数 3201 阅读 393

Pandas数据处理三板斧 — map、apply、applymap详解

Python 转载


在日常的数据处理中,经常会对一个 DataFrame 进行逐行、逐列和逐元素的操作,对应这些操作,Pandas 中的 map()apply()applymap() 可以解决绝大部分这样的数据处理需求。这篇文章就以案例附带图解的方式,为大家详细介绍一下这三个方法的实现原理,相信读完本文后,不论是小白还是 Pandas 的进阶学习者,都会对这三个方法有更深入的理解。

本文演示的数据集是模拟生成的,想练手的可以按下方的代码生成。

  1. boolean = [True, False]
  2. gender = ["男", "女"]
  3. color = ["white", "black", "yellow"]
  4. data = {"height": np.random.randint(150, 190, 100),
  5. "weight": np.random.randint(40, 90, 100),
  6. "smoker": [boolean[x] for x in np.random.randint(0, 2, 100)],
  7. "gender": [gender[x] for x in np.random.randint(0, 2, 100)],
  8. "age": np.random.randint(15, 90, 100),
  9. "color": [color[x] for x in np.random.randint(0, len(color), 100)]}
  10. df = pd.DataFrame(data = data)

数据集如下所示,各列分别代表身高、体重、是否吸烟、性别、年龄和肤色。

此处输入图片的描述

I. Series 数据处理

1. map() 用法

如果需要把数据集中 gender 列的“男”替换为“1”,“女”替换为“0”,怎么做呢?绝对不是用 for 循环实现,使用 Series.map() 可以很容易做到,最少仅需一行代码。

  1. #①使用字典进行映射
  2. data["gender"] = data["gender"].map({"男": 1, "女": 0})
  3. #②使用函数
  4. def gender_map(x):
  5. gender = 1 if x == "男" else 0
  6. return gender
  7. #注意这里传入的是函数名,不带括号
  8. data["gender"] = data["gender"].map(gender_map)

map() 在实际过程中是怎么运行的呢?请看下面的图解(为了方便展示,仅截取了前10条数据)

此处输入图片的描述

此处输入图片的描述

不论是利用字典还是函数进行映射,map() 方法都是把对应的数据逐个当作参数传入到字典或函数中,得到映射后的值。

2. apply()

同时 Series 对象还有 apply() 方法,apply() 方法的作用原理和 map() 方法类似,区别在于 apply() 能够传入功能更为复杂的函数。怎么理解呢?一起看看下面的例子。

假设在数据统计的过程中,年龄 age 列有较大误差,需要对其进行调整(加上或减去一个值),由于这个加上或减去的值未知,故在定义函数时,需要加多一个参数 bias,此时用 map() 方法是操作不了的(传入 map() 的函数只能接收一个参数),apply() 方法则可以解决这个问题。

  1. def apply_age(x, bias):
  2. return x + bias
  3. #以元组的方式传入额外的参数
  4. data["age"] = data["age"].apply(apply_age, args = (-3,))

可以看到 age 列都减了3,当然,这里只是简单举了个例子,当需要进行复杂处理时,更能体现 apply() 的作用。

总而言之,对于 Series 而言,map() 可以解决绝大多数的数据处理需求,但如果需要使用较为复杂的函数,则需要用到 apply() 方法。

II. DataFrame 数据处理

1. apply

DataFrame 而言,apply() 是非常重要的数据处理方法,它可以接收各种各样的函数(Python 内置的或自定义的),处理方式很灵活,下面通过几个例子来看看 apply() 的具体使用及其原理。

在进行具体介绍之前,首先需要介绍一下 DataFrameaxis 的概念,在 DataFrame 对象的大多数方法中,都会有 axis 这个参数,它控制了你指定的操作是沿着0轴还是1轴进行。axis = 0 代表操作对列 columns 进行,axis = 1 代表操作对行 row 进行,如下图所示。

此处输入图片的描述

如果还不是很了解,没关系,下面会分别使用 apply() 沿着 轴以及 轴的操作进行讲解,继续往下走。

假设现在需要对 data 中的数值列分别进行取对数和求和的操作,这时可以用 apply() 进行相应的操作,因为是对列进行操作,所以需要指定 axis = 0 ,使用下面的两行代码可以很轻松地解决我们的问题。

  1. # 沿着0轴求和
  2. data[["height", "weight", "age"]].apply(np.sum, axis = 0)
  3. # 沿着0轴取对数
  4. data[["height", "weight", "age"]].apply(np.log, axis = 0)

实现的方式很简单,但调用 apply() 时究竟发生了什么呢?过程是怎么实现的?还是通过图解的方式来一探究竟。(取前五条数据为例)

此处输入图片的描述

此处输入图片的描述

当沿着轴0(axis = 0)进行操作时,会将各列(columns)默认以 Series 的形式作为参数,传入到你指定的操作函数中,操作后合并并返回相应的结果。

那如果在实际使用中需要按行进行操作(axis = 1),那整个过程又是怎么实现的呢?

在数据集中,有身高和体重的数据,所以根据这个,我们可以计算每个人的BMI指数(体检时常用的指标,衡量人体肥胖程度和是否健康的重要标准),计算公式是:体重指数BMI=体重/身高的平方(国际单位kg/㎡),因为需要对每个样本进行操作,这里使用 axis = 1apply() 进行操作,代码如下:

  1. def BMI(series):
  2. weight = series["weight"]
  3. height = series["height"] / 100
  4. BMI = weight / height ** 2
  5. return BMI
  6. data["BMI"] = data.apply(BMI, axis = 1)

还是用图解的方式来看看这个过程到底是怎么实现的(以前5条数据为例)。

此处输入图片的描述

apply() 设置了 axis = 1 对行进行操作时,会默认将每一行数据以 Series 的形式(Series 的索引为列名)传入指定函数,返回相应的结果。

总结一下对 DataFrameapply 操作:

axis = 0 时,对每列columns执行指定函数;当 axis = 1 时,对每行row执行指定函数。
无论 axis = 0 还是 axis = 1,其传入指定函数的默认形式均为 Series ,可以通过设置 raw = True 传入 numpy数组。
对每个Series执行结果后,会将结果整合在一起返回(若想有返回值,定义函数时需要return相应的值)
当然,DataFrameapply()Seriesapply() 一样,也能接收更复杂的函数,如传入参数等,实现原理是一样的,具体用法详见官方文档。

2. applymap()

applymap() 的用法比较简单,会对 DataFrame 中的每个单元格执行指定函数的操作,虽然用途不如 apply() 广泛,但在某些场合下还是比较有用的,如下面这个例子。

为了演示的方便,新生成一个 DataFrame

  1. data = {"A": np.random.randn(5),
  2. "B": np.random.randn(5),
  3. "C": np.random.randn(5),
  4. "D": np.random.randn(5),
  5. "E": np.random.randn(5)}
  6. df = pd.DataFrame(data = data)
  7. df.head()

此处输入图片的描述

现在想将 DataFrame 中所有的值保留两位小数显示,使用 applymap() 可以很快达到你想要的目的,代码和图解如下:

  1. df.applymap(lambda x:"%.2f" % x)
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注