缺失值处理

原文首发于我的博客欢迎关注

当我们拿到一批数据的时候,往往都是“不干净”的,而缺失值是最常见也是最容易发现的。不同的缺失值处理方式对接下来的特征提取,建模等都有巨大影响。那么缺失值的处理是有一套流程的,我在这里总结总结:

发现缺失值

  1. 统计每个特征在所有个体中缺失的个数 / 缺失率
    这一点是查找缺失的特征
    pandas 中 count() 函数为不为空数据的个数,那么将shape与count做差就得到缺失个数,缺失率。
(df.shape[0] - df.count())/df.shape[0]
missing
  1. 对于每个个体所缺失的特征个数
    这一点是查找缺失的个体
    这个简单,对数据 df 转置一下即可
(df.shape[1] - df.T.count())/df.shape[1]
  1. pandas 其他缺失值函数
方法 说明
dropna 根据各标签的值中是否存在缺失数据对轴标签进行过滤, 可通过阀值调节对缺失值的容忍度
fillna 用指定值或插值方法(如ffill或bfill)填充缺失值
isnull 返回一个含有bool型的对象, 这些bool型值表示哪些是缺失值NaN, 该对象的类型与源类型一样
notnull isnull的否定式
  1. 隐藏的缺失值
    这里要理解数据集内容的含义,比如在某些情况下,0代表缺失值。因为有些值为0的变量是无意义的,可以表示为缺失值。例如:身高、体重等。

缺失机制

在对缺失数据进行处理前,了解数据缺失的机制和形式是十分必要的。将数据集中不含缺失值的变量(属性)称为完全变量,数据集中含有缺失值的变量称为不完全变量,Little 和 Rubin定义了以下三种不同的数据缺失机制:

  1. 完全随机缺失(Missing Completely at Random,MCAR)。数据的缺失与不完全变量以及完全变量都是无关的。

  2. 随机缺失(Missing at Random,MAR)。数据的缺失仅仅依赖于完全变量。

  3. 非随机、不可忽略缺失(Not Missing at Random,NMAR,or nonignorable)。不完全变量中数据的缺失依赖于不完全变量本身,这种缺失是不可忽略的。

从缺失值的所属属性上讲,如果所有的缺失值都是同一属性,那么这种缺失成为单值缺失,如果缺失值属于不同的属性,称为任意缺失。另外对于时间序列类的数据,可能存在随着时间的缺失,这种缺失称为单调缺失。

空值语义

对于某个对象的属性值未知的情况,我们称它在该属性的取值为空值(null value)。空值的来源有许多种,因此现实世界中的空值语义也比较复杂。总的说来,可以把空值分成以下三类:

  1. 不存在型空值。即无法填入的值,或称对象在该属性上无法取值,如一个未婚者的配偶姓名等。
  2. 存在型空值。即对象在该属性上取值是存在的,但暂时无法知道。一旦对象在该属性上的实际值被确知以后,人们就可以用相应的实际值来取代原来的空值,使信息趋于完全。存在型空值是不确定性的一种表征,该类空值的实际值在当前是未知的。但它有确定性的一面,诸如它的实际值确实存在,总是落在一个人们可以确定的区间内。一般情况下,空值是指存在型空值。
  3. 占位型空值。即无法确定是不存在型空值还是存在型空值,这要随着时间的推移才能够清楚,是最不确定的一类。这种空值除填充空位外,并不代表任何其他信息。

判断缺失值的重要性

对于包含有缺失值处理的算法,比如XGB或者LGB,我们可以简单的直接把训练数据扔到模型中训练,查看feature_importance。(由于XGB等属于树模型,不需要太多的数据预处理过程,比如归一化等,也能取得较好的效果,且模型参数对特征的重要性程度影响不是很大,我们只需要知道大概的结果,哪些重要,哪些不重要即可)

缺失值较多且不重要的特征

这些特征我们看情况,可以尝试着直接删除,如果不删除,缺失值又多,处理不好,可能会引来噪声。
至于为什么看情况呢,意思是,做个对比试验,一组是删除的,另一组是没删除的,进行交叉验证,如果删除后的结果比较好,那么就进行删除。

缺失值较少的特征

统计量填充

这一类特征,我们可以简单使用统计量比如:均值、中位数、众数 进行填充;
对于连续值,推荐使用 中位数 ,可以排除一些特别大或者特别小的异常值造成的影响;
对于离散值,推荐使用 众数 ,均值和中位数用不了吧,那就用众数好了。。。

df = df.fillna(df.median(axis=0))

特殊值填充

我们可以填一个不在正常取值范围内的数值,比如 -999 ,0 等来表示缺失值。

df.fillna(-999) 

不处理

大家可能都有一个疑惑,为什么对很多人说XGB或者LGB对缺失值不敏感呢,当用缺失值的训练XGB时,算法不会报错,其实这个不能叫不敏感,而是算法本身自己有一套缺失值处理算法,比如XGB,它会把含有缺失值的数据分别分到左右两个子节点,然后计算着两种情况的损失,最后,选取较好的划分结果和对应的损失。XGB缺失值具体算法可以参考我之前的文章XGBoost笔记
所以,如果遇到有缺失值的情况,最好还是根据缺失的情况,自己处理比较好。

分类别填充

我们还可以根据label的类别,取值范围进行更高级的统计量填充(当然这个只适用于知道label的训练集),即取在该label下数据的中位数、均值等进行填充。

插值填充

使用线性,多项式等差值方法,对于时间序列的缺失问题,可以使用此方法。

df.interpolate()
插值填充

插补法

  1. 随机插补法----从总体中随机抽取某个样本代替缺失样本
  2. 多重插补法----通过变量之间的关系对缺失数据进行预测,利用蒙特卡洛方法生成多个完整的数据集,在对这些数据集进行分析,最后对分析结果进行汇总处理
  3. 热平台插补----指在非缺失数据集中找到一个与缺失值所在样本相似的样本(匹配样本),利用其中的观测值对缺失值进行插补
    优点:简单易行,准去率较高
    缺点:变量数量较多时,通常很难找到与需要插补样本完全相同的样本。但我们可以按照某些变量将数据分层,在层中对缺失值实用均值插补
  4. 拉格朗日差值法和牛顿插值法

用预测值填充

将缺失的数据当成label,没缺失的作为训练集,缺失的作为测试集,通过某种机器学习算法进行预测,填补缺失值。下面代码以lgb为例:

import lightgbm as lgb

def set_missing(df, predict_list):
    for predict_feature in predict_list:

        # 原始数据分为已知和未知的
        known = df[df[predict_feature].notnull()]
        unknown = df[df[predict_feature].isnull()]

        # 训练集构造,从已知的部分构造
        y = known[predict_feature].as_matrix()
        X = known.drop(predict_feature, axis=1).as_matrix()

        # 测试集,从未知的部分构造
        test_X = unknown.drop(predict_feature, axis=1).as_matrix()

        # 用lgb模型进行训练
        predicted_feature = _model_predict(X, y, test_X)

        # 用得到的预测结果填补原缺失数据
        df.loc[(df[predict_feature].isnull()), predict_feature] = predicted_feature

    return df


def _model_predict(X, y, test_X):
    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1)

    lgb_train = lgb.Dataset(X_train, y_train)
    lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)

    params = {
        'boosting': 'gbdt',
        'objective': 'regression',
        'metric': 'rmse',
        'num_leaves': 31,
        'min_data_in_leaf': 20,
        'learning_rate': 0.015,
        'cat_smooth': 10,
        'feature_fraction': 0.8,
        'bagging_freq': 5,
        'verbosity': 0
    }

    gbm = lgb.train(params,
                    lgb_train,
                    num_boost_round=1000,
                    valid_sets=lgb_eval,
                    early_stopping_rounds=70)

    # 用得到的模型进行未知年龄结果预测
    predicted_feature = gbm.predict(test_X, num_iteration=gbm.best_iteration)
    print("---------best_iteration: ", gbm.best_iteration)
    return predicted_feature

缺失值比较多的样本

当样本很多的时候,而缺失值比较多的样本,且它们数目不多时,直接删掉。

参考资料

缺失值的处理
机器学习中如何处理缺失数据?
缺失数据

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容