Pandas数据处理三板斧,你会几板?

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

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

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

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

Series数据处理

map

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

#①使用字典进行映射
data["gender"] = data["gender"].map({"男":1, "女":0})

#②使用函数
def gender_map(x):
    gender = 1 if x == "男" else 0
    return gender
#注意这里传入的是函数名,不带括号
data["gender"] = data["gender"].map(gender_map)

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

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

apply

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

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

def apply_age(x,bias):
    return x+bias

#以元组的方式传入额外的参数
data["age"] = data["age"].apply(apply_age,args=(-3,))

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

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

DataFrame数据处理

apply

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

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

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

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

# 沿着0轴求和
data[["height","weight","age"]].apply(np.sum, axis=0)

# 沿着0轴取对数
data[["height","weight","age"]].apply(np.log, axis=0)

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

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

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

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

def BMI(series):
    weight = series["weight"]
    height = series["height"]/100
    BMI = weight/height**2
    return BMI

data["BMI"] = data.apply(BMI,axis=1)

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

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

总结一下对 DataFrameapply 操作:

  1. axis=0 时,对 每列columns 执行指定函数;当 axis=1 时,对 每行row 执行指定函数。

  2. 无论 axis=0 还是 axis=1 ,其传入指定函数的默认形式均为 Series ,可以通过设置 raw=True 传入 numpy数组

  3. 对每个Series执行结果后,会将结果整合在一起返回(若想有返回值,定义函数时需要 return 相应的值)

  4. 当然, DataFrameapplySeriesapply 一样,也能接收更复杂的函数,如传入参数等,实现原理是一样的,具体用法详见官方文档。

applymap

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

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

df = pd.DataFrame(
    {
        "A":np.random.randn(5),
        "B":np.random.randn(5),
        "C":np.random.randn(5),
        "D":np.random.randn(5),
        "E":np.random.randn(5),
    }
)
df

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

df.applymap(lambda x:"%.2f" % x)

数据处理三板斧就介绍到这里,有问题欢迎下方留言板积极留言呀!