pandas 缺失值处理
1.缺失值种类
pandas中缺失值有三种,np.nan,None,及针对时间的缺失值类型,NaT,下面分开讲解这三种类型。
1.1 令人头秃的np.nan
首先是np.nan,由于pandas是基于Numpy的,Numpy的缺失值类型就是pandas最常见的缺失值类。但这个np.nan有大量问题,分下面几点。
- 1.条件判断问题。np.nan什么也不等于,其实就是说它不等于自己,一个字符,‘a’只等于‘a’,但碰到np.nan,好了,你想循环一个列表,找出值为np.nan的列,使用 if i==np.nan:,不好意思,这个找不到,不可能有值等于np.nan的元素,你永远找不到,白瞎。
- 2.数据类型变化问题。np.nan会改变该列的数据类型,np.nan是带有数据类型的,为float(64)。如果整形含有np.nan缺失值,那么该列会变为浮点型。如果是bool类型数据列,含有np.nan的地方,会自动填充为True。(正常来说应该变为False才对,这一点真的很难理解pandas的设计原理)。但当修改一个布尔列表时,将一个值赋值为np.nan的话,由于np.nan本身为浮点型数据,这样会改变列表类型为浮点型,原来True变为1.0,但赋值的地方为np.nan。对于字符型数据列,含有np.nan的话,由于字符型无法转为浮点型,所以该列的数据会变为Object类型。这是导致数据类型混乱的一大原因。
1.2 问题少一点的None
None比np.nan好一点,有如下几点:
- 1.它可以等于自己。使用None==None,得到True
- 2.本身布尔值为False,赋值给bool数据类型时,不会改变原数据类型。
None空值类型,在数据列有数值类型数据时,None会变为np.nan
只有当数据类型为object时,None会保持其数据类型。
- None只有人为命名时,才会出现,因为pandas默认空值为np.nan
1.3 时间缺失项NaT
在时间类型中出现缺失值时,不管是np.nan还是None,都会转为NaT,这是时间缺失值的特殊缺失值类型。
2.新特性
2.1 Nullable数据类型
为了解决原pandas中缺失值数据类型造成的问题,pandas 1.0中新引进Int64,boolean,string,三种新数据类型,这三种数据类型叫做Nullable型数据,对缺失值有统一管理。
s_new = pd.Series([1, 2], dtype="Int64")
s_new[1] = np.nan
s_new
0 1
1 <NA>
dtype: Int64
在新数据类型下,出现缺失值会统一显示为,且数值类型不会改变
s_new[1] = None
s_new
0 1
1 <NA>
dtype: Int64
这三种数据类型的特点在于,有缺失值并不会改变其数据类型。
此外,对于stringl类型数据,和object类型的一点重要区别就在于,在调用字符方法后,string类型返回的是Nullable类型,object则会根据缺失类型和数据类型而改变
2.2 引入的新函数以进行数据转换
convert_dtypes方法
这个函数的功能往往就是在读取数据时,就把数据列转为Nullable类型,是1.0的新函数,使用十分方便。
pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes
Out[73]:
School string
Class string
ID Int64
Gender string
Address string
Height Int64
Weight Int64
Math float64
Physics string
dtype: object
3.缺失值的运算特性及填充
3.1 缺失值的统计运算特性
在进行数据统计时,使用求和函数,缺失值会被当为0计算,乘法则会当为1计算,其他情况会跳过缺失值。
3.2 缺失值的填充
- fillna方法
(a)值填充与前后向填充(分别与ffill方法和bfill方法等价)
值填充
df['Physics'].fillna('missing').head()
0 A+
1 B+
2 B+
3 missing
4 A-
Name: Physics, dtype: object
前后填充
#ffill 表示使用缺失值前一个非缺失值的数据填充,bfill则表示后一个值
df['Physics'].fillna(method='ffill').head()
(b)填充中的对齐特性
这里的对齐特性,指的是只填充返回结果中的列。
2.dropna方法
dropna方法有三个参数,轴的方向axis,删除方法how,和判断区域subset
(a)axis=0,1表示的按行看还是按列看
df_d = pd.DataFrame({
'A':[np.nan,np.nan,np.nan],'B':[np.nan,3,2],'C':[3,2,1]})
df_d.dropna(axis=0)
A B C
0 NaN NaN 3
1 NaN 3.0 2
2 NaN 2.0 1
A B C
返回结果为空,因为每一行都有缺失值
(b) how参数(可以选all或者any,表示全为缺失去除和存在缺失去除)
In [89]:
df_d.dropna(axis=1,how='all')
Out[89]:
B C
0 NaN 3
1 3.0 2
2 2.0 1
(C)subset参数(即在某一组列范围中搜索缺失值)
In [90]:
df_d.dropna(axis=0,subset=['B','C'])
Out[90]:
A B C
1 NaN 3.0 2
2 NaN 2.0 1
3.3线性插值及其他插值
(a)索引无关的线性插值
默认状态下,interpolate会对缺失的值进行线性插值
In [91]:
s = pd.Series([1,10,15,-5,-2,np.nan,np.nan,28])
s
Out[91]:
0 1.0
1 10.0
2 15.0
3 -5.0
4 -2.0
5 NaN
6 NaN
7 28.0
dtype: float64
这种插值方法与索引无关,会造成并不是线性的结果
s.interpolate()
Out[92]:
0 1.0
1 10.0
2 15.0
3 -5.0
4 -2.0
5 8.0
6 18.0
7 28.0
dtype: float64
加入时间索引或者索引,会取得真的线性插值结果
s.interpolate(method='index').plot()
索引为时间时
s_t = pd.Series([0,np.nan,10]
,index=[pd.Timestamp('2012-05-01'),pd.Timestamp('2012-05-07'),pd.Timestamp('2012-06-03')])
s_t.interpolate(method='time')
- 高级插值方法
此处的高级指的是与线性插值相比较,例如样条插值、多项式插值、阿基玛插值等(需要安装Scipy)。
ser = pd.Series(np.arange(1, 10.1, .25) ** 2 + np.random.randn(37))
missing = np.array([4, 13, 14, 15, 16, 17, 18, 20, 29])
ser[missing] = np.nan
methods = ['linear', 'quadratic', 'cubic']
df = pd.DataFrame({
m: ser.interpolate(method=m) for m in methods})
df.plot()
插值的一些参数设置
(a)limit表示最多插入多少个
s = pd.Series([1,np.nan,np.nan,np.nan,5])
s.interpolate(limit=2)
0 1.0
1 2.0
2 3.0
3 NaN
4 5.0
dtype: float64
(b)limit_direction表示插值方向,可选forward,backward,both,默认前向
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_direction='backward')
0 1.0
1 1.0
2 1.0
3 2.0
4 3.0
5 4.0
6 5.0
7 NaN
8 NaN
dtype: float64
(c)插值区域参数设置
s = pd.Series([np.nan,np.nan,1,np.nan,np.nan,np.nan,5,np.nan,np.nan,])
s.interpolate(limit_area='inside')
0 NaN
1 NaN
2 1.0
3 2.0
4 3.0
5 4.0
6 5.0
7 NaN
8 NaN
dtype: float64
``None’’:没有填充限制。‘inside’:仅填充有效值包围的NaN(插)。‘外部’:仅在有效值之外填充NaN(外推)。