第七章 缺失数据

参考:https://datawhalechina.github.io/joyful-pandas/build/html/%E7%9B%AE%E5%BD%95/ch7.html

一、缺失值的统计和删除

1. 缺失信息的统计 isnaisnull

函数 例子 备注
isna或isnull df.isna().mean() # 查看缺失的比例 isnaisnull (两个函数没有区别)
isna或者notna df[df.Height.isna()] 应用于series,只能统计一列的缺失信息
isna,notnaany, all 的组合 sub_set = df[['Height', 'Weight', 'Transfer']]
df[sub_set.isna().all(1)]# 全部缺失
df[sub_set.isna().any(1)].head() # 至少有一个缺失
df[sub_set.notna().all(1)].head() # 没有缺失
同时对几个列,检索出全部为缺失或者至少有一个缺失或者没有缺失的行

2. 缺失信息的删除

函数 参数 例子 备注
dropna axis(默认为0,即删除行)
how(删除方式)主要有 anyall 两种参数可以选择 df.dropna(how = 'any', subset = ['Height', 'Weight']) 删除身高体重至少有一个缺失的行
thresh(删除的非缺失值个数阈值) res = df.dropna(1, thresh=df.shape[0]-15) 删除超过15个缺失值的列
subset(备选的删除子集) res = df.loc[df[['Height', 'Weight']].notna().all(1)]
res = df.loc[:, ~(df.isna().sum()>15)]
上述的两个操作,也可以使用布尔索引来完成

二、缺失值的填充和插值

1. 利用fillna进行填充

函数 参数 例子
fillna method:填充方法(ffill: 用缺失值前面的元素填充;bfill:用后面的元素填充) s.fillna(method='ffill') # 用前面的值向后填充
limit:连续缺失值的最大填充次数 s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
value:填充值,可以是标量,也可以是索引到元素的字典映射 s.fillna(s.mean()) # value为标量
s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值

练一练

对一个序列以如下规则填充缺失值:如果单独出现的缺失值,就用前后均值填充,如果连续出现的缺失值就不填充,即序列[1, NaN, 3, NaN, NaN]填充后为[1, 2, 3, NaN, NaN],请利用 fillna 函数实现。(提示:利用 limit 参数)

s = pd.Series([1,np.nan,3, np.nan, np.nan])
s1 = s.fillna(method='ffill',limit=1)
s2 = s.fillna(method='bfill',limit=1)
s = pd.Series(list(map(lambda x,y: (x+y)/2 if not np.isnan(x) and not np.isnan(y) else np.nan, s1,s2)))
s
'''
0    1.0
1    2.0
2    3.0
3    NaN
4    NaN
'''

2. 插值函数

这里只讨论比较常用且简单的三类情况,即线性插值、最近邻插值和索引插值。

函数 插值类型 参数 例子 备注
interpolate 线性插值 limit_direction:控制方向,默认为 forward,还有backward或both(双向限制插值) s.interpolate(limit_direction='backward', limit=1)
limit:控制最大连续缺失值插值个数
最近邻插补 method s.interpolate('nearest').values 缺失值的元素和离它最近的非缺失值元素一样
索引插值 s.interpolate(method='index') 根据索引大小进行线性插值

三、Nullable类型

1. 缺失记号及其缺陷

缺失记号 说明
None python 中的缺失值用 None 表示,该元素除了等于自己本身之外,与其他任何元素不相等
np.nan 和所有元素都不相等,包括自己;但在两个序列使用equals进行比较时,会跳过nan来比较
pd.NaT 时间序列对象的缺失值,作用和np.nan一样

np.nan的缺陷:np.nan属于浮点类型,和任何其他类型一列,这一列就会变成其他类型:

  • 和整数一列:整列变成float类型
  • 和bool一列:整列变成object类型
  • 和datetime一列:整列变成object类型

所以出现了pd.NaT。1.0.0版本后,pandas设计了新的缺失类型pd.NA以及3种Nullable序列类型。

2. Nullable类型的性质

序列类型不受nan影响,在声明序列时指定序列的类型为Int, boolean 和 string

  • pd.Series([np.nan, 1], dtype = 'Int64') # "i"是大写的
  • pd.Series([np.nan, True], dtype = 'boolean')
  • pd.Series([np.nan, 'my_str'], dtype = 'string')
类型 计算 NA类型返回 序列类型
Int pd.Series([np.nan, 0], dtype = 'Int64') + 1 NA Int64
pd.Series([np.nan, 0], dtype = 'Int64') == 0 NA boolean
pd.Series([np.nan, 0], dtype = 'Int64') * 0.5 NA Float64

boolean和bool的区别:

s = pd.Series(['a', 'b'])
s_bool = pd.Series([True, np.nan])
s_boolean = pd.Series([True, np.nan]).astype('boolean')
bool bool结果 boolean boolean结果
s[s_bool] 报错; nan不能作为索引器的选择 s[s_boolean] 0 a
dtype: object 把nan当成False
s_bool & True bool类型在缺失处返回的永远是False
0 True
1 False
s_boolean & True boolean 会根据逻辑运算是否能确定唯一结果来返回相应的值
0 True
1
s_bool | True 0 True
1 False
s_boolean | True 0 True
1 True
~s_bool 0 False
1 False
~s_boolean 0 False
1

一般在实际数据处理时,可以在数据集读入后,先通过 convert_dtypes 转为 Nullable 类型:

df = df.convert_dtypes()

3. 缺失数据的计算和分组

sum, prob 使用加法和乘法的时候,缺失数据等价于被分别视作0和1,即不改变原来的计算结果。当使用累计函数时,会自动跳过缺失值所处的位置。

单个标量运算:

运算 结果
np.nan ** 0 1.0
1 ** np.nan 1.0
pd.NA ** 0 1
1 ** pd.NA 1
其他所有涉及np.nan的运算 False
其他所有涉及pd.NA的运算 NA

对于一些函数而言,缺失可以作为一个类别处理,例如在 groupby, get_dummies 中可以设置相应的参数来进行增加缺失类别:

  • df_nan.groupby('category', dropna=False)['value'].mean() # pandas版本大于1.1.0
  • pd.get_dummies(df_nan.category, dummy_na=True)

四、练习

Ex1:缺失值与类别的相关性检验

统计学的概念不太记得了,题目不太读得懂。。。Orz

答案:

df = pd.read_csv('../data/missing_chi.csv')
cat_1 = df.X_1.fillna('NaN').mask(df.X_1.notna()).fillna("NotNaN")
cat_2 = df.X_2.fillna('NaN').mask(df.X_2.notna()).fillna("NotNaN")
df_1 = pd.crosstab(cat_1, df.y, margins=True)
df_2 = pd.crosstab(cat_2, df.y, margins=True)
def compute_S(my_df):
    S = []
    for i in range(2):
        for j in range(2):
            E = my_df.iat[i, j]
            F = my_df.iat[i, 2]*my_df.iat[2, j]/my_df.iat[2,2]
            S.append((E-F)**2/F)
    return sum(S)
res1 = compute_S(df_1)
res2 = compute_S(df_2)
from scipy.stats import chi2
chi2.sf(res1, 1) # X_1检验的p值 # 不能认为相关,剔除
chi2.sf(res2, 1) # X_2检验的p值 # 认为相关,保留

Ex2:用回归模型解决分类问题

对KNN的类不太了解,题目好难。。。Orz

  1. 对于回归问题而言,需要得到的是一个具体的数值,因此预测值由最近的 n 个样本对应的平均值获得。请把上面的这个分类问题转化为回归问题,仅使用 KNeighborsRegressor 来完成上述的 KNeighborsClassifier 功能。

答案:

from sklearn.neighbors import KNeighborsRegressor
df = pd.read_excel('../data/color.xlsx')
df_dummies = pd.get_dummies(df.Color)
stack_list = []
for col in df_dummies.columns:
    clf = KNeighborsRegressor(n_neighbors=6)
    clf.fit(df.iloc[:,:2], df_dummies[col])
    res = clf.predict([[0.8, -0.2]]).reshape(-1,1)
    stack_list.append(res)
code_res = pd.Series(np.hstack(stack_list).argmax(1))
df_dummies.columns[code_res[0]]

你可能感兴趣的:(第七章 缺失数据)