原文首發于我的博客歡迎關注
當我們拿到一批數據的時候,往往都是“不干凈”的,而缺失值是最常見也是最容易發現的。不同的缺失值處理方式對接下來的特征提取,建模等都有巨大影響。那么缺失值的處理是有一套流程的,我在這里總結總結:
發現缺失值
- 統計每個特征在所有個體中缺失的個數 / 缺失率
這一點是查找缺失的特征
pandas 中 count() 函數為不為空數據的個數,那么將shape與count做差就得到缺失個數,缺失率。
(df.shape[0] - df.count())/df.shape[0]
- 對于每個個體所缺失的特征個數
這一點是查找缺失的個體
這個簡單,對數據 df 轉置一下即可
(df.shape[1] - df.T.count())/df.shape[1]
- pandas 其他缺失值函數
方法 | 說明 |
---|---|
dropna | 根據各標簽的值中是否存在缺失數據對軸標簽進行過濾, 可通過閥值調節對缺失值的容忍度 |
fillna | 用指定值或插值方法(如ffill或bfill)填充缺失值 |
isnull | 返回一個含有bool型的對象, 這些bool型值表示哪些是缺失值NaN, 該對象的類型與源類型一樣 |
notnull | isnull的否定式 |
- 隱藏的缺失值
這里要理解數據集內容的含義,比如在某些情況下,0代表缺失值。因為有些值為0的變量是無意義的,可以表示為缺失值。例如:身高、體重等。
缺失機制
在對缺失數據進行處理前,了解數據缺失的機制和形式是十分必要的。將數據集中不含缺失值的變量(屬性)稱為完全變量,數據集中含有缺失值的變量稱為不完全變量,Little 和 Rubin定義了以下三種不同的數據缺失機制:
完全隨機缺失(Missing Completely at Random,MCAR)。數據的缺失與不完全變量以及完全變量都是無關的。
隨機缺失(Missing at Random,MAR)。數據的缺失僅僅依賴于完全變量。
非隨機、不可忽略缺失(Not Missing at Random,NMAR,or nonignorable)。不完全變量中數據的缺失依賴于不完全變量本身,這種缺失是不可忽略的。
從缺失值的所屬屬性上講,如果所有的缺失值都是同一屬性,那么這種缺失成為單值缺失,如果缺失值屬于不同的屬性,稱為任意缺失。另外對于時間序列類的數據,可能存在隨著時間的缺失,這種缺失稱為單調缺失。
空值語義
對于某個對象的屬性值未知的情況,我們稱它在該屬性的取值為空值(null value)。空值的來源有許多種,因此現實世界中的空值語義也比較復雜??偟恼f來,可以把空值分成以下三類:
- 不存在型空值。即無法填入的值,或稱對象在該屬性上無法取值,如一個未婚者的配偶姓名等。
- 存在型空值。即對象在該屬性上取值是存在的,但暫時無法知道。一旦對象在該屬性上的實際值被確知以后,人們就可以用相應的實際值來取代原來的空值,使信息趨于完全。存在型空值是不確定性的一種表征,該類空值的實際值在當前是未知的。但它有確定性的一面,諸如它的實際值確實存在,總是落在一個人們可以確定的區間內。一般情況下,空值是指存在型空值。
- 占位型空值。即無法確定是不存在型空值還是存在型空值,這要隨著時間的推移才能夠清楚,是最不確定的一類。這種空值除填充空位外,并不代表任何其他信息。
判斷缺失值的重要性
對于包含有缺失值處理的算法,比如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()
插補法
- 隨機插補法----從總體中隨機抽取某個樣本代替缺失樣本
- 多重插補法----通過變量之間的關系對缺失數據進行預測,利用蒙特卡洛方法生成多個完整的數據集,在對這些數據集進行分析,最后對分析結果進行匯總處理
- 熱平臺插補----指在非缺失數據集中找到一個與缺失值所在樣本相似的樣本(匹配樣本),利用其中的觀測值對缺失值進行插補
優點:簡單易行,準去率較高
缺點:變量數量較多時,通常很難找到與需要插補樣本完全相同的樣本。但我們可以按照某些變量將數據分層,在層中對缺失值實用均值插補 - 拉格朗日差值法和牛頓插值法
用預測值填充
將缺失的數據當成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
缺失值比較多的樣本
當樣本很多的時候,而缺失值比較多的樣本,且它們數目不多時,直接刪掉。