Python语言处理各类空值的方法研究
作者: 蔡黎亚 李淑萍
摘要:在各类编程语言中的空值表述都有所不同。对于数据分析而言,筛选出当前表内的空值并对其进行有效填充或进行其他操作,是数据清洗过程中的重要一环。该文对各类编程语言中可能出现的空值表述方法进行了梳理汇总,然后通过Python中的pandas扩展库相关函数操作阐述了Python语言处理这些不同语境中空值的常用方法。
关键词:数据处理;Python;Pandas;空值;None;NaN;NaT;Null;NA
中图分类号:TP311 文献标识码:A
文章编号:1009-3044(2023)34-0038-03
开放科学(资源服务)标识码(OSID)
0 引言
在数据处理过程中会遇到形态各异的空值运算,比如Java中的Null,NumPy扩展库中的NaN或者pandas扩展库中的NA或者Python原生语言中的None类型等,都是各类编程语言对空值的描述。它们无论在表述方式和运算方法上都有所不同。要在数据清洗处理阶段准确找出不同来源表格中的空值项,就需要掌握这些语言对空值项的表述方法。Python作为数据分析领域的重要开发语言,在数据处理扩展库Pandas中有很多便捷的函数方法来处理各类的空值运算。
1 各类编程语言中的空值表述
在Python语言中的空值(也称之为缺失值)是用None表示,其数据类型就是NoneType。None不支持任何运算——和其他数据类型的运算只返回False,但可以把None赋值给任何变量以表示为空[1],而两个None进行比较时是相等关系。
在Python的NumPy扩展库中,空值是用NaN(也可以写作nan) 表示,它的数据类型是浮点型float。nan在NumPy库中的含义是not a number[2]。任何与nan的运算结果都是nan,无穷大减无穷大会产生nan,而无穷大乘以零或无穷小除以无穷大等运算都会导致产生nan。两个nan比较并不相等,这些nan的运算特点与Python中的None是有所不同的。
而Pandas的运算是基于NumPy库构建的,因此通过NumPy库运算的数值类空值往往用NaN表示(数值类型的空值)[3],也可能存在None(如表示字符串类型的空值)或者NaT(表示时间类型的空值)等。当None被运算时可能会触发NumPy的运算机制而转换为NaN。
另外,在其他类编程语言中,如Java、PHP、C++等语言的空值往往是用Null表示。Null在Python语言中是一个普通的字符串,但Python中存在一个与Null相关的比较函数isnull()可判断各类其他语言运算后所存在的空值Null。
而如果存在类似a=’ ’这样的赋值方式,则并不会被系统认定为空,而会被认为是非空的字符串。这些不同语言及扩展库对空值的不同描述会在用户进行数据清洗或整理时,带来不小的困扰[4]。
在Python的Pandas扩展库中推出了一个顶层的缺失值容器pd.NA来承载和处理Pandas库内出现的各类空值,它尝试把诸多类型的空值统一在pd.NA这个顶层的容器内,并为此创建了一个数据类型NAType[5]。因此,很多教程中也会把NA作为Pandas库对空值类型的一种专属描述。
Pandas库中具有后缀名为na的各种函数,如df.fillna(), df.dropna(),df.isna()等,它们都可直接操作和处理不同类型的空值。
随着Pandas库的版本更新,相关na函数的功能也越来越完善,但依然与np.NaN等处理方法还存在一些差异。比如pd.NA是整数,np.NaN是浮点数。而pd.NA常用于整数、布尔型或字符串类型。在与1和0比较时,pd.NA的输出是NA类型,而np.nan与1和0比较的结果是False。这些处理上的细微差异稍不留意就可能影响最终数据分析的准确性。
2 过滤空值
分析数据时会遇到许多数据不完整的情况,为了让分析结果更接近真实,往往需要在空值处填充既定数据(如均值、中位数等),或者干脆丢弃这些少量而不完整数据。这时Pandas库中的dropna()或者fillna()就可以方便地完成既定的任务。
通过函数dropna()可删除数组对象DataFrame中存在的一个或多个空值列或行数据,因此常用于数据清洗中的过滤缺失值任务。
使用df.dropna()方法可以通过轴axis来指明删除的是行还是列,其主要语法格式如下所示:
df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False) [3]
ü axis:可选参数,是指删除空值所在的行或列,默认为0(行)。
ü how:可选参数,是指删除的条件,默认为any(含有空值的任意行或列),还可选择all (全是空值的行或列)。
ü thresh:可选参数,是指删除空值后保留的行或列数,默认为空(全部都保留)。
ü subset:可选参数,是指定义在哪些列集合中查找空值,默认为空(全部列都检索)。
ü inplace:可选参数,是指是否就地修改原数据对象,默认为False。
在该语法中,不存在必选参数,单独使用df.dropna()即可删除所有存在空值的行数据。下例所示为通过df.dropna()删除数据对象中各类空值的程序实例。
在下例中,使用四个Series创建了一个数据对象df。其中salary列由NaN、None、<NA>这三种空值类型组成,而other列则是由pd.Series([''])所创建。它有一个看上去并不存在任何值却并非空值的一个空字符串作为列首的数据。
import pandas as pd
import numpy as np
d1 = pd.Series([101, 102, 103, 104])
d2 = pd.Series(['Alex', 'Peter', 'Lisa'])
d3 = pd.Series([np.NaN, None, pd.NA, None])
d4 = pd.Series([''])
df = pd.DataFrame({'id': d1, 'name': d2, 'salary': d3, 'other': d4})
print('原数组->\n', df)
原数组->
id name salary other
0 101 Alex NaN
1 102 Peter None NaN
2 103 Lisa <NA> NaN
3 104 NaN None NaN
使用df.dropna()) 语句即可删除所有存在空值的行数据,但介于df的每行都有空值,因此返回为空对象。
print('dropna()->\n', df.dropna())
dropna()->
Empty DataFrame
Columns: [id, name, salary, other]
Index: []
在df.dropna(subset=['name', 'other'])) 语句中使用了参数subset来指定需检索的列名称name与other。检索的结果是除了第一行外,在这两列内的其他行数据都存在空值(被删除)。这里需要注意的是,pd.Series([''])所创建的空字符串并不被认为是空值。
print('dropna(subset=[])->\n', df.dropna(subset=['name', 'other']))
dropna(subset=[])->
id name salary other
0 101 Alex NaN
在df.dropna(axis=1)) 语句中使用了轴参数axis=1指明删除所有存在空值的列,因此只输出了id列内容。
print('dropna(axis=1)->\n', df.dropna(axis=1))
dropna(axis=1)->
id
0 101
1 102
2 103
3 104
在df.dropna(axis=1, how='all') 语句中,除了通过轴参数axis=1来确定删除轴的方向为纵向之外,还使用了参数how=all来指明删除全部是空值的列。由于只有salary列数据全部都是空值,因此输出的结果为不包含salary列的其他列内容。
print("drop.na(axis=1,how='all')->\n", df.dropna(axis=1, how='all'))
drop.na(axis=1,how='all')->
id name other
0 101 Alex
1 102 Peter NaN
2 103 Lisa NaN
3 104 NaN NaN
3 填充空值
外部获取到的数据经常会存在数据缺失的情况,如何填充缺失值,填充哪些类的缺失值是数据清洗过程中常见的而又重要的一类操作[6]。Pandas库中的fillna()就可以完成此类任务。
填充操作的函数使用方法会比删除操作复杂一些,它拥有更多的参数。
df.fillna(value=None, method=None, axis=None, inplace=False, limit=None, downcast=None) [3]
ü value:可选参数,是指填充值,可以是标量、字典、Series或DataFrame。默认为空。
ü method:可选参数,是指填充方法,可以是向后(backfill、bfill) 或向前填充(ffill、pad) ,默认为空(不指定)。
ü axis:可选参数,是指行方向还是列方向填充,默认为空。
ü inplace:可选参数,是指是否就地修改原数据对象。
ü limit:可选参数,是指填充的数量限制,默认为空(不限定填充数量,全部填充)。