第七章 缺失数据

整章知识架构

一 、缺失值的统计和删除

缺失值的统计和删除

通过isna或isnull和sum的组合可以计算出每列缺失值的比例

df = pd.read_csv('../data/learn_pandas.csv', usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight', 'Transfer'])
df.isna().head()
df.isna().sum()/df.shape[0] # 查看缺失的比例

通过dropna函数对缺失值进行删除

res = df.dropna(how = 'any', subset = ['Height', 'Weight'])
res = df.dropna(axis=1, thresh=df.shape[0]-15) # 身高被删除

二、缺失值的填充和插值

缺失值的填充和插值

使用fillna可以对缺失值进行填充,可以通过method参数指定填充方法,有前向填充可后向填充,分别指是用当前位置前面的值进行填充和使用当前位置后面的值进行填充。还可以通过limit参数指定连续缺失值的最大填充次数。也可以进行分组后在进行填充操作。

s.fillna(method='ffill') # 用前面的值向后填充
s.fillna(method='ffill', limit=1) # 连续出现的缺失,最多填充一次
s.fillna(s.mean()) # value为标量
s.fillna({'a': 100, 'd': 200}) # 通过索引映射填充的值
df.groupby('Grade')['Height'].transform(lambda x: x.fillna(x.mean())).head()

【练一练】

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

s = pd.Series([1, np.nan, 3, np.nan, np.nan], index =[1,2,3,4,5])
s.fillna(s.mean(), limit=1)
result

interpolate函数主要有两个常用参数,一个是控制方向的limit_direction,一个是控制最大连续缺失值插值个数的limit

res = s.interpolate(limit_direction='backward', limit=1)
res = s.interpolate(limit_direction='both', limit=1)
# 最近邻插值,即缺失值的元素和离他最近的非缺失值元素一样
s.interpolate('nearest').values
s.interpolate() # 默认的线性插值,等价于计算中点的值
s.interpolate(method='index') # 和索引有关的线性插值,计算相应索引大小对应的值

三、Nullable类型

Nullable类型
  • 在python中,缺失值使用None表示,该元素除了与自己相等之外,和其他任何元素不相等。
  • 在numpy中使用np.nan来表示缺失元素,该元素除了和其他元素不相等之外,和自己也不相等。
  • 在对缺失序列进行元素比较时,nan的对应位置会返回False,但在使用equals函数进行两张表或序列的相同性检验时,会自动跳过两侧表都是缺失值的位置,并返回当前位置的比较结果为True
s1 = pd.Series([1, np.nan])
s2 = pd.Series([1, 2])
s3 = pd.Series([1, np.nan])
s1 == 1
result
  • 在时间序列对象中,pandas使用pd.NaT来指代缺失值,其作用和nan一样。但不同的是,因为nan本身是一种浮点类型,在浮点类型和时间类型混合存储时,如不设计新的内置缺失类型来处理,Series的类型就会变成含糊不清的object
  • 以下三个Nullable类型中存储的缺失值,都将会转化成pandas内置的pd.NA
pd.Series([np.nan, 1], dtype = 'Int64') # "i"是大写的
pd.Series([np.nan, True], dtype = 'boolean')
pd.Series([np.nan, 'my_str'], dtype = 'string')

当调用函数sum和prob使用加法和乘法的时候,缺失数据等价于0和1,不会改变原来的计算结果

四、 练习

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

在数据处理中,含有过多缺失值的列往往会被删除,除非缺失情况与标签强相关。下面有一份关于二分类问题的数据集,其中X_1, X_2为特征变量,y为二分类标签。
事实上,有时缺失值出现或者不出现本身就是一种特征,并且在一些场合下可能与标签的正负是相关的。关于缺失出现与否和标签的正负性,在统计学中可以利用卡方检验来断言它们是否存在相关性。按照特征缺失的正例、特征缺失的负例、特征不缺失的正例、特征不缺失的负例,可以分为四种情况,设它们分别对应的样例数为n_{11}, n_{10}, n_{01}, n_{00}。假若它们是不相关的,那么特征缺失中正例的理论值,就应该接近于特征缺失总数\times总体正例的比例,即:

E_{11} = n_{11} \approx (n_{11}+n_{10})\times\frac{n_{11}+n_{01}}{n_{11}+n_{10}+n_{01}+n_{00}} = F_{11}

其他的三种情况同理。现将实际值和理论值分别记作E_{ij}, F_{ij},那么希望下面的统计量越小越好,即代表实际值接近不相关情况的理论值:

S = \sum_{i\in \{0,1\}}\sum_{j\in \{0,1\}} \frac{(E_{ij}-F_{ij})^2}{F_{ij}}

可以证明上面的统计量近似服从自由度为1的卡方分布,即S\overset{\cdot}{\sim} \chi^2(1)。因此,可通过计算P(\chi^2(1)>S)的概率来进行相关性的判别,一般认为当此概率小于0.05时缺失情况与标签正负存在相关关系,即不相关条件下的理论值与实际值相差较大。

上面所说的概率即为统计学上关于2\times2列联表检验问题的p值, 它可以通过scipy.stats.chi2(S, 1)得到。请根据上面的材料,分别对X_1, X_2列进行检验。

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) # 认为相关

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

KNN是一种监督式学习模型,既可以解决回归问题,又可以解决分类问题。对于分类变量,利用KNN分类模型可以实现其缺失值的插补,思路是度量缺失样本的特征与所有其他样本特征的距离,当给定了模型参数n_neighbors=n时,计算离该样本距离最近的n个样本点中最多的那个类别,并把这个类别作为该样本的缺失预测类别,具体如下图所示,未知的类别被预测为黄色:

Ex2

上面有色点的特征数据提供如下:
已知待预测的样本点为X_1=0.8, X_2=-0.2,那么预测类别可以如下写出:

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=6)
clf.fit(df.iloc[:,:2], df.Color)
clf.predict([[0.8, -0.2]])
  1. 对于回归问题而言,需要得到的是一个具体的数值,因此预测值由最近的n个样本对应的平均值获得。请把上面的这个分类问题转化为回归问题,仅使用KNeighborsRegressor来完成上述的KNeighborsClassifier功能。
  2. 请根据第1问中的方法,对audit数据集中的Employment变量进行缺失值插补。
color2int = {'Blue':1, 'Yellow':2, 'Green':3}
int2color = {1:'Blue', 2:'Yellow', 3:'Green'}
# 将str转换成int
df['Color'] = df['Color'].apply(lambda x: color2int[x])
 # 仅使用KNeighborsRegressor完成
from sklearn.neighbors import KNeighborsRegressor
clf = KNeighborsRegressor(n_neighbors=6)
clf.fit(df.iloc[:,:2], df.Color)
# 预测均值,并通过四舍五入的方式确定值
int2color[np.around(clf.predict([[0.8, -0.2]])).astype(np.int)[0]]

res_df = df.copy()
df = pd.concat([pd.get_dummies(df[['Marital', 'Gender']]), df[['Age','Income','Hours']].apply(lambda x:(x-x.min())/(x.max()-x.min())), df.Employment],1)
X_train = df.query('Employment.notna()')
X_test = df.query('Employment.isna()')
df_dummies = pd.get_dummies(X_train.Employment)
stack_list = []
for col in df_dummies.columns:
    clf = KNeighborsRegressor(n_neighbors=6)
    clf.fit(X_train.iloc[:,:-1], df_dummies[col])
    res = clf.predict(X_test.iloc[:,:-1]).reshape(-1,1)
    stack_list.append(res)
code_res = pd.Series(np.hstack(stack_list).argmax(1))
cat_res = code_res.replace(dict(zip(list(range(df_dummies.shape[0])),df_dummies.columns)))
res_df.loc[res_df.Employment.isna(), 'Employment'] = cat_res.values
res_df.isna().sum()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容