5.Pandas处理缺失值

Pandas处理缺失值

我们在现实生活中真正需要处理的数据并不是十全十美的,往往会出现数据缺失的现象,更为甚者,处理不同数据源缺失值的方法还不同

本节将介绍一些处理缺失值的通用规则,Pandas对缺失值的表现形式以及Pandas子代的几个处理缺失值的工具的用法.

一般来说涉及的缺失值有三种形式:null,NaN和NA


如何标记缺失值

缺失值是数据的一种特殊状态,不代表任何数据,因此我们为了将缺失值和正常数字区分开,就需要来标记缺失值.

一般标记缺失值有两种方法:通过全局的掩码和使用标签值

全局掩码

全局掩码的意思是不对原数组进行任何操作,而是重新开辟一个同样大小,同样形状的布尔数组,用对应位的布尔值来表示数据是否缺失.

例如

list_1=[[1,2,3]
        [缺失值,5,6]]
list_1_mask=[[1,1,1],
             [0,1,1]]

但是使用全局掩码却会造成一个额外的布尔数组的开辟,当需要处理的数据较大时,会造成空间浪费

使用标签值

使用标签值来区分缺失值的含义就是在正常表示范围内,取某一个值来表示缺失值

例如,4字节16比特的数字,第一位为符号位,取如下二进制数组为缺失值:1111 1111 1111 1111

使用标签值的问题也很大,将会减少我们数字的表示范围,而且会影响编程逻辑,因为正常来说1111 1111 1111 1111是会被当成正常数据参与运算的,但是如果其表示缺失值的话,那么任何一个数字与缺失值相加都会变成缺失值,这就意味着编程时需要额外的逻辑

一般来说,在大多数情况下是不存在最佳选择的,不同的编程语言与不同的系统使用的标记缺失值的方法不同,例如R语言每种数据类型中保留一个比特作为缺失数据的标签值,SciDB系统会在每个单元后面额外增加一个字节表示缺失状态


Pandas的缺失值

Pandas原本可以按照R语言一样,通过比特位来为每一种数据类型标注缺失值.但是R语言本身只包含4种数据类型,而Numpy却支持14种基本数据类型,想要标记每种数据类型的数据是否缺失,需要分出的比特位就太多了,何况编码会极度的不方便

因此综合考虑下,Pandas最终选择使用标签的方法来表示缺失值,包括:浮点数据类型的NaN,Python的None对象

不过Pandas也支持使用全局掩码的方式来表示缺失值

下面将分别介绍Python的None对象和浮点数据类型的NaN

Python的None对象

None是原生Python内置的一个对象,他经常在代码中表示缺失值

由于None是Python的对象,因此不能作为Numpy和Pandas中数组类型的缺失值,只能在Object类型数组(即由Python对象构成的数组)中表示缺失值

例如

array_1=np.array([1,2,3,4])
array_2=np.array([1,2,None,4])
print(array_1.dtype)
print(array_2.dtype)
>>>
int64
object

这里将array_2中的所有元素视为Python的对象,而非经过Numpy加速过的对象,因此所有的处理都将在Python层面完成,这样就导致了速度会很慢,消耗更多的资源

此外,None由于是Python中的对象,但是Python却并没有定义None对象与任何其他的数据之间的运算,因此如果None对象参与运算,Python就会报错

array_1=np.array([1,2,3,4])
array_2=np.array([1,2,None,4])
print(array_1+array_2)
>>>
Traceback (most recent call last):
  File "TryPandas.py", line 231, in <module>
    print(array_1+array_2)
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'

浮点数据类型的NaN

正如其名,NaN本质上是一个浮点数,不过被选取来表示缺失值,这是IEEE所规定的,全球通用的指定的特殊字符

NaN全称是Not a number,在任何系统中都兼容的特殊浮点数

例如

array_1=np.array([1,2,np.nan,4])
print(array_1.dtype)
>>>
float64

可以看到,原本是整型数组的array_1被自动的转化为float64了

既然NaN是一个特殊的浮点数,因此NaN就可以参与运算而不会让编译器报错,只不过任何数组与NaN进行运算都会变成NaN

array_1=np.array([1,2,np.nan,4])
array_2=np.array([1,2,3,4])
print(array_1+array_2)
print(array_1.sum())
print(array_1.min())
>>>
[ 2.  4. nan  8.]
nan
nan

注意,运算不仅包括四则远算,还包括逻辑运算等等

因此,为了避免NaN的污染效益,Numpy提供了一些特殊的累计函数,又称为NaN安全版本,他们可以忽略nan的影响

例如

array_1=np.array([1,2,np.nan,4])
print(np.nansum(array_1))
print(np.nanmin(array_1))
>>>
7.0
1.0

None与NaN的差异

虽然None与NaN各有各的用处,但是Pandas将两者视为等同的,并在适当的时侯会将两者进行替换.

例如

Series_1=pd.Series(range(4),index=list('abcd'))
print(Series_1)
Series_1['a']=None
print(Series_1)
>>> 
a    0
b    1
c    2
d    3
dtype: int64
a    NaN
b    1.0
c    2.0
d    3.0
dtype: float64

可以发现None被自动转化为NaN

除此以外,虽然Pandas目前只有这两种缺失值,但是GitHub上Pandas的维护人员却有人提议创建Pandas原生的NA来标记缺失值


处理缺失值

我们知道,Pandas基本上把None和NaN看作是可以等价交换的缺失值形式,而Padas提供了用于发现,剔除和替换缺失值的方法,主要包括以下集中

  • isnull()
  • notnull()
  • dropna()
  • fillna()

发现缺失值

发现缺失值有两种方法,isnull()和notnull()

两种方法将会返回布尔类型的全局掩码

Series_1=pd.Series([1,2,None,np.nan],index=list('abcd'))
print(Series_1)
print(Series_1.isnull())
print(Series_1.notnull())
>>>
a    1.0
b    2.0
c    NaN
d    NaN
dtype: float64
a    False
b    False
c     True
d     True
dtype: bool
a     True
b     True
c    False
d    False
dtype: bool

结合我们之前讲的,可以直接将布尔类型的掩码数组作为花哨的索引使用

Series_1=pd.Series([1,2,None,np.nan],index=list('abcd'))
print(Series_1)
print(Series_1[Series_1.isnull()])
print(Series_1[Series_1.notnull()])
>>>
a    1.0
b    2.0
c    NaN
d    NaN
dtype: float64
c   NaN
d   NaN
dtype: float64
a    1.0
b    2.0
dtype: float64

剔除缺失值

Pandas提供了很好的剔除缺失值的方法,就是dropna()方法

对于Series对象,直接调用即可

Series_1=pd.Series([1,None,3,np.nan],index=list('abcd'))
print(Series_1)
print(Series_1.dropna())
>>>
a    1.0
b    NaN
c    3.0
d    NaN
dtype: float64
a    1.0
c    3.0
dtype: float64

可以发现,所有具有NaN的连带索引都被丢弃了,最终返回一个新的Series对象

对于DataFrame对象,在真实情况中我们可能需要删除NaN所在的行或者列,因此对于DataFrame对象,dropna()方法就有一些参数可以配置

默认情况下,将会剔除NaN所在的整行数据,但是我们可以指定axis参数来剔除列

我们也可以指定how参数来指定整行或整列只要有NaN就丢弃还是整行整列都是NaN才会丢弃

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(4,5)),index=list('abcd'),columns=list('abcde'))
DataFrame_1.iloc[:,2]=np.nan
DataFrame_1.iloc[0,0]=np.nan
+
print(DataFrame_1)
print('')
print(DataFrame_1.dropna())
print('')
print(DataFrame_1.dropna(axis='columns'))
print('')
print(DataFrame_1.dropna(axis=1))
print('')
print(DataFrame_1.dropna(how='any'))
print('')
print(DataFrame_1.dropna(axis='columns',how='all'))
>>>
     a  b   c  d  e
a  NaN  5 NaN  5  4
b  0.0  4 NaN  5  3
c  8.0  0 NaN  1  6
d  2.0  8 NaN  9  9

Empty DataFrame
Columns: [a, b, c, d, e]
Index: []

   b  d  e
a  5  5  4
b  4  5  3
c  0  1  6
d  8  9  9

   b  d  e
a  5  5  4
b  4  5  3
c  0  1  6
d  8  9  9

Empty DataFrame
Columns: [a, b, c, d, e]
Index: []

     a  b  d  e
a  NaN  5  5  4
b  0.0  4  5  3
c  8.0  0  1  6
d  2.0  8  9  9

填充缺失值

有的时候我们并不想剔除缺失值,因为这样会导致我们的数组形状发生改变.

因此我们有的时候想要保持数组的形状在不改变的基础上,对缺失值进行填充,例如全部填充为0或者均值

我们可以使用isnull()来获取掩码数组,然后利用花哨的索引来直接填充所有的缺失值

Pandas提供了更加高效的fillna()方法来根据需求填充缺失值

对于Series对象,我们可以指定填充值

也可以指定method参数来指定填充方法,可以是从前往后填充ffill(forward-fill),也可以是从后往前bfill(back-fill)

Series_1=pd.Series([0,1,2,None,3,4,np.nan],index=list('abcdefg'))
print(Series_1)
print(Series_1.fillna(0))
print(Series_1.fillna(method='ffill'))
print(Series_1.fillna(method='bfill'))
>>>
a    0.0
b    1.0
c    2.0
d    NaN
e    3.0
f    4.0
g    NaN
dtype: float64
    
a    0.0
b    1.0
c    2.0
d    0.0
e    3.0
f    4.0
g    0.0
dtype: float64
    
a    0.0
b    1.0
c    2.0
d    2.0
e    3.0
f    4.0
g    4.0
dtype: float64
    
a    0.0
b    1.0
c    2.0
d    3.0
e    3.0
f    4.0
g    NaN
dtype: float64

需要注意的是如果填充的前一个或者后一个也是NaN,或者是第一个NaN,填充的结果也是NaN

针对DataFrame对象的填充则可以指定填充的行列

DataFrame_1=pd.DataFrame(np.random.randint(0,10,(4,5)),\
    index=list('abcd'),columns=list('abcde'))
DataFrame_1.iloc[:,3]=np.nan
DataFrame_1.iloc[2,2]=None
print(DataFrame_1)
print(DataFrame_1.fillna(0))
print(DataFrame_1.fillna(axis=1,method='ffill'))
print(DataFrame_1.fillna(axis=0,method='bfill'))
>>>
   a  b    c   d  e
a  8  9  6.0 NaN  6
b  6  4  2.0 NaN  5
c  1  1  NaN NaN  5
d  7  1  4.0 NaN  1

   a  b    c    d  e
a  8  9  6.0  0.0  6
b  6  4  2.0  0.0  5
c  1  1  0.0  0.0  5
d  7  1  4.0  0.0  1

     a    b    c    d    e
a  8.0  9.0  6.0  6.0  6.0
b  6.0  4.0  2.0  2.0  5.0
c  1.0  1.0  1.0  1.0  5.0
d  7.0  1.0  4.0  4.0  1.0

   a  b    c   d  e
a  8  9  6.0 NaN  6
b  6  4  2.0 NaN  5
c  1  1  4.0 NaN  5
d  7  1  4.0 NaN  1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值