目录
1. 学习内容
1. 学习如何发现缺失值并统计缺失值的情况
2. 学习pandas中对缺失值的各种表示方式
3. 利用填充、删除和插值方法来处理缺失值
2. 准备工作
import pandas as pd
import numpy as np
df = pd.read_csv('data/table_missing.csv')
df.head()
School Class ID Gender Address Height Weight Math Physics
0 S_1 C_1 NaN M street_1 173 NaN 34.0 A+
1 S_1 C_1 NaN F street_2 192 NaN 32.5 B+
2 S_1 C_1 1103.0 M street_2 186 NaN 87.2 B+
3 S_1 NaN NaN F street_2 167 81.0 80.4 NaN
4 S_1 C_1 1105.0 NaN street_4 159 64.0 84.8 A-
3. 发现缺失值并统计缺失值的情况
3.1 观察具体位置上是否出现缺失值
isna()和notna()将会统计表格每一列的缺失值情况。前者缺失值记为True,非缺失值记为False;后者恰好相反。
df['Physics'].isna().head()
0 False
1 False
2 False
3 True
4 False
Name: Physics, dtype: bool
3.2 缺失值的统计
由于布尔值True等于1,False等于0。因此可以在isna()和notna()后面再加上sum()方法来统计各个列的缺失值个数。
df.isna().sum()
School 0
Class 4
ID 6
Gender 7
Address 0
Height 0
Weight 13
Math 5
Physics 4
dtype: int64
或者还可以使用info()方法来进行统计。
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 35 entries, 0 to 34
Data columns (total 9 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 School 35 non-null object
1 Class 31 non-null object
2 ID 29 non-null float64
3 Gender 28 non-null object
4 Address 35 non-null object
5 Height 35 non-null int64
6 Weight 22 non-null float64
7 Math 30 non-null float64
8 Physics 31 non-null object
dtypes: float64(3), int64(1), object(5)
memory usage: 2.6+ KB
3.3 缺失值数据的筛选与过滤
3.3.1 找出某列缺失值所在的行数据
df[df['Physics'].isna()]
School Class ID Gender Address Height Weight Math Physics
3 S_1 NaN NaN F street_2 167 81.0 80.4 NaN
8 S_1 C_2 1204.0 F street_5 162 63.0 33.8 NaN
13 S_1 C_3 1304.0 NaN street_2 195 70.0 85.2 NaN
22 S_2 C_2 2203.0 M street_4 155 91.0 73.8 NaN
df[df.loc[:, ['Weight', 'Math']].isna().all(1)]
School Class ID Gender Address Height Weight Math Physics
20 S_2 C_2 2201.0 M street_5 193 NaN NaN B
3.3.2 找出所有值都非缺失的行数据
df[df.notna().all(1)]
School Class ID Gender Address Height Weight Math Physics
5 S_1 C_2 1201.0 M street_5 159 68.0 97.0 A-
6 S_1 C_2 1202.0 F street_4 176 94.0 63.5 B-
12 S_1 C_3 1303.0 M street_7 188 82.0 49.7 B
17 S_2 C_1 2103.0 M street_4 157 61.0 52.5 B-
21 S_2 C_2 2202.0 F street_7 194 77.0 68.5 B+
25 S_2 C_3 2301.0 F street_4 157 78.0 72.3 B+
27 S_2 C_3 2303.0 F street_7 190 99.0 65.9 C
28 S_2 C_3 2304.0 F street_6 164 81.0 95.5 A-
29 S_2 C_3 2305.0 M street_4 187 73.0 48.9 B
3.4 缺失值符号介绍
3.4.1 旧体系的符号
在pandas 1.0版本之前,pandas中对于缺失值的表示有三种不同的方式。它们分别是:np.nan,None和pd.NaT。
np.nan是一种很特殊的空值,它不等于任何其他常见的空值(0,None),甚至也不等于它自己。对于表格而言,可以用equals()方法来判断两个表格是否相等。其中,在遇到同一位置的值均为np.nan时会自动忽略,从而避免对结果造成影响。np.nan虽然是空值,但是在numpy中,它是属于浮点类型的。因此,只要读入的数据集中某整数类型的列存在np.nan,则这一列数据会被强制转换为浮点型。另外,np.nan对应的布尔值为True。如果刚读入的数据中某列为布尔类型,且其中存在np.nan则np.nan会被记为True而非False。但是,如果数据在导入后某位置被修改为np.nan,则该位置会变成np.nan而非True。在所有的表格读取后,无论列是存放什么类型的数据,默认的缺失值全为np.nan类型。因此整型列转为浮点;而字符由于无法转化为浮点,因此只能归并为object类型('O'),原来是浮点型的则类型不变。
None等于自身且布尔值等于False。布尔类型值无论是导入时就是None还是之后被修改为None,都会将其自动变为False。数值型无论是导入时就是None还是之后被修改为None,都会将其自动变为np.nan。只有对象类型不会对None进行修改。equals()方法不会略过None。
pd.NaT是针对时间序列的缺失值,是Pandas的内置类型,可以完全看做时序版本的np.nan(因此一些性质与np.nan是一样的)。它与自己不等,而且使用equals是也会被跳过。datetime类型的数据在被修改为None后,会将其自动变为pd.NaT。
s_time = pd.Series([pd.Timestamp('20120101')] * 5)
s_time[2] = None
s_time[3] = np.nan
s_time
0 2012-01-01
1 2012-01-01
2 NaT
3 NaT
4 2012-01-01
dtype: datetime64[ns]
s = pd.Series([True,False],dtype='bool')
s[1] = pd.NaT
s
0 True
1 True
dtype: bool
这些缺失值符号都有自己独有的特点而且在一些情况下会彼此相互转化。这样就使很多数据分析工作变得混乱。
3.4.2 新体系的数据类型和符号
从pandas 1.0开始,诞生了一个新的数据类型Nullable和新的缺失值符号pd.NA。这么做的目的就是为了应对上述空值彼此存在的混乱情况。在新的数据类型体系中,数据若被修改为空值(np.nan,None和pd.NaT)都会被一律转换为pd.NA。
Nullable类型中又包含新的数据类型,它们是:整型(Int),布尔型(boolean)和字符串类型(string)。
其中,string类型是pandas 1.0的一大创新,目的之一就是为了区分开原本含糊不清的object类型。它本质上也属于Nullable类型,因为它不会因为含有缺失而改变类型。此外,和object类型的一点重要区别就在于,在调用字符方法后,string类型返回的是Nullable类型,而object则会根据缺失类型和数据类型而改变。
pd.NA具有以下性质:
首先,对于逻辑运算,如果运算结果依赖pd.NA的取值,则结果为pd.NA,否则的话可以视为pd.NA并没有参与运算。
print(True & pd.NA)
print(True | pd.NA)
print(False & pd.NA)
print(False | pd.NA)
<NA>
True
False
<NA>
另外,pd.NA是不能被强制转化为bool类型的,否则会报错。
最后,对于算数运算,除了以下两种情况,所有的算数运算(加减乘除,比较大小,指对数,幂运算)结果都是pd.NA。
print(pd.NA ** 0)
print(1 ** pd.NA)
1
1
3.5 convert_dtypes()方法
这个方法在读取数据时,会把数据列转为Nullable类型,是配合新体系诞生的方法。
pd.read_csv('data/table_missing.csv').dtypes
School object
Class object
ID float64
Gender object
Address object
Height int64
Weight float64
Math float64
Physics object
dtype: object
pd.read_csv('data/table_missing.csv').convert_dtypes().dtypes
School string
Class string
ID Int64
Gender string
Address string
Height Int64
Weight Int64
Math float64
Physics string
dtype: object
4. 缺失数据的运算与分组
4.1 运算
加法视为0,乘法视为1,累计计算则无视。
4.2 分组(groupby)
缺失值被自动忽略,不论该列是否为Nullable类型。
df_g = pd.DataFrame({'one':['A','B','C','D', np.nan], 'two':np.random.randn(5)})
df_g
one two
0 A -0.274225
1 B 0.682027
2 C 0.556510
3 D 0.993260
4 NaN 0.350107
df_g.groupby('one').groups
{'A': Int64Index([0], dtype='int64'),
'B': Int64Index([1], dtype='int64'),
'C': Int64Index([2], dtype='int64'),
'D': Int64Index([3], dtype='int64')}
5. 缺失值的填充与删除
5.1 填充fillna()方法
5.1.1 值填充与前后向填充
fillna()如果传入某个值则会将所有空值替换为该值。fillna()还可以调整method参数变为前向覆盖填充和后向覆盖填充。另外,前向填充和后向填充也有专门的方法ffill()和bfill()。
df['Physics'].fillna('missing', inplace = False).head()
0 A+
1 B+
2 B+
3 missing
4 A-
Name: Physics, dtype: object
df['Physics'].fillna(method = 'ffill', inplace = False).head()
0 A+
1 B+
2 B+
3 B+
4 A-
Name: Physics, dtype: object
df['Physics'].fillna(method = 'bfill', inplace = False).head()
0 A+
1 B+
2 B+
3 A-
4 A-
Name: Physics, dtype: object
df['Physics'].ffill(inplace = False).head()
0 A+
1 B+
2 B+
3 B+
4 A-
Name: Physics, dtype: object
df['Physics'].bfill(inplace = False).head()
0 A+
1 B+
2 B+
3 A-
4 A-
Name: Physics, dtype: object
5.1.2 用统计量进行填充
df_f = pd.DataFrame({'A':[1, 3, np.nan], 'B':[2, 4, np.nan], 'C':[3, 5, np.nan]})
df_f.fillna(df_f.mean(), inplace = False)
A B C
0 1.0 2.0 3.0
1 3.0 4.0 5.0
2 2.0 3.0 4.0
df_f.fillna(df_f.mean()[['A', 'B']], inplace = False)
A B C
0 1.0 2.0 3.0
1 3.0 4.0 5.0
2 2.0 3.0 NaN
5.1.3 删除dropna()方法
df_d = pd.DataFrame({'A':[np.nan, np.nan, np.nan], 'B':[np.nan, 3, 2], 'C':[3, 2, 1]})
df_d
A B C
0 NaN NaN 3
1 NaN 3.0 2
2 NaN 2.0 1
dropna()方法有3个常用的参数:axis(控制删除行还是列),how(指定逻辑使用all还是any)和subset(指定删除范围)。
df_d.dropna(axis = 0)
Empty DataFrame
Columns: [A, B, C]
Index: []
df_d.dropna(axis = 1)
C
0 3
1 2
2 1
df_d.dropna(axis = 1, how = 'all')
B C
0 NaN 3
1 3.0 2
2 2.0 1
df_d.dropna(axis = 0, subset = ['B', 'C'])
A B C
1 NaN 3.0 2
2 NaN 2.0 1
6. 插值方法interpolate()
6.1 线性插值
6.1.1 与索引无关的线性插值(默认)
s = pd.Series([1, 10, 15, -5, -2, np.nan, np.nan, 28])
s
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()
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
6.1.2 与索引有关的插值
s = pd.Series([1, 10, 15, -5, -2, np.nan, np.nan, 28])
s.index = np.sort(np.random.randint(50, 300, 8))
s.interpolate(method = 'index').plot()
6.1.3 与时序有关的插值
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
2012-05-01 0.0
2012-05-07 NaN
2012-06-03 10.0
dtype: float64
s_t.interpolate().plot()
s_t.interpolate(method = 'time').plot()
6.2 高级插值法(了解)
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()
6.3 interpolate()中的限制参数
常用的限制参数有:limit参数(指定插入几个值), limit_direction参数(指定插值方向)和 limit_area参数(限制插值区域)。limit_direction可以选为forward,backward和both,默认为前向。limit_area可选为inside和outside,默认为None。
7. 问题与练习
7.1 问题
【问题一】 如何删除缺失值占比超过25%的列?
在统计缺失值个数的时候将sum()中传入参数axis=0。然后再将结果除以表格长度就能得到每列缺失值占比。将满足条件的列删除即可。
【问题二】 什么是Nullable类型?请谈谈为什么要引入这个设计?
Nullable类型是为了应对旧体系的缺失值在使用时存在的混乱情况而诞生的类型。这样在将某些值修改为缺失值后就会防止将整个列进行强制类型转换。
【问题三】 对于一份有缺失值的数据,可以采取哪些策略或方法深化对它的了解?
统计缺失值比例以及分布情况,必要时还需要作图。
7.2 练习
略。