1. 程式人生 > >##############缺失值填充的幾種方法

##############缺失值填充的幾種方法

常見的缺失值填充方法有填充預設值、均值、眾數、KNN填充、以及把缺失值作為新的label通過模型來預測等方式,為了介紹這幾種填充方法的使用以及填充效果,本文將在真實資料集上進行簡單比較。

1. 資料集介紹

資料集來源於 天池精準醫療大賽——人工智慧輔助糖尿病遺傳風險預測。該資料集共有1000條資料,特徵共83維,加上id和label共85列,每維特徵缺失數量範圍為0~911。為了簡單比較各種填充方法的效果,我們選取最簡單的二分類模型(邏輯迴歸),選取F1 score作為評測指標。

讀取資料集程式碼如下:

train_data = pd.read_csv('train_data.csv', encoding='gbk') # 讀取資料集
 
filter_feature = ['id','label'] # 過濾無用的維度
features = []
for x in train_data.columns: # 取特徵
    if x not in filter_feature:
        features.append(x)
 
train_data_x = train_data[features] 
train_data_y = train_data['label']
X_train, X_test, y_train, y_test = train_test_split(train_data_x, train_data_y, random_state=1) # 劃分訓練集、測試集
2. 常見的填充方法

(1)填充固定值

選取某個固定值/預設值填充缺失值。

train_data.fillna(0, inplace=True) # 填充 0
(2)填充均值

對每一列的缺失值,填充當列的均值。

train_data.fillna(train_data.mean(),inplace=True) # 填充均值
(3)填充中位數

對每一列的缺失值,填充當列的中位數。

train_data.fillna(train_data.median(),inplace=True) # 填充中位數
(4)填充眾數

對每一列的缺失值,填充當列的眾數。由於存在某列缺失值過多,眾數為nan的情況,因此這裡取的是每列刪除掉nan值後的眾數。

train_data.fillna(train_data.mode(),inplace=True) # 填充眾數,該資料缺失太多眾數出現為nan的情況
features_mode = {}
for f in features:
    print f,':', list(train_data[f].dropna().mode().values)
    features_mode[f] = list(train_data[f].dropna().mode().values)[0]
train_data.fillna(features_mode,inplace=True)
(5)填充上下條的資料

對每一條資料的缺失值,填充其上下條資料的值。

train_data.fillna(method='pad', inplace=True) # 填充前一條資料的值,但是前一條也不一定有值
train_data.fillna(0, inplace=True)
 
train_data.fillna(method='bfill', inplace=True) # 填充後一條資料的值,但是後一條也不一定有值
train_data.fillna(0, inplace=True)
(6)填充插值得到的資料

用插值法擬合出缺失的資料,然後進行填充。

for f in features: # 插值法填充
    train_data[f] = train_data[f].interpolate()
    
train_data.dropna(inplace=True)
(7)填充KNN資料

填充近鄰的資料,先利用knn計算臨近的k個數據,然後填充他們的均值。(安裝fancyimpute)除了knn填充,fancyimpute還提供了其他填充方法。

from fancyimpute import KNN
 
train_data_x = pd.DataFrame(KNN(k=6).fit_transform(train_data_x), columns=features)
(8)填充模型預測的值

把缺失值作為新的label,建立模型得到預測值,然後進行填充。這裡選擇某個缺失值數量適當的特徵採用隨機森林RF進行擬合,其他缺失特徵採用均值進行填充。

new_label = 'SNP46'
new_features = []
for f in features:
    if f != new_label:
        new_features.append(f)
        
new_train_x = train_data[train_data[new_label].isnull()==False][new_features]
new_train_x.fillna(new_train_x.mean(), inplace=True) # 其他列填充均值
new_train_y = train_data[train_data[new_label].isnull()==False][new_label]
 
new_predict_x = train_data[train_data[new_label].isnull()==True][new_features]
new_predict_x.fillna(new_predict_x.mean(), inplace=True) # 其他列填充均值
new_predict_y = train_data[train_data[new_label].isnull()==True][new_label]
 
rfr = RandomForestRegressor(random_state=666, n_estimators=10, n_jobs=-1)
rfr.fit(new_train_x, new_train_y)
new_predict_y = rfr.predict(new_predict_x)
 
new_predict_y = pd.DataFrame(new_predict_y, columns=[new_label], index=new_predict_x.index)
new_predict_y = pd.concat([new_predict_x, new_predict_y], axis=1)
new_train_y = pd.concat([new_train_x, new_train_y], axis=1)
new_train_data = pd.concat([new_predict_y,new_train_y]) 
 
train_data_x = new_train_data[features]
train_data_y = train_data['label']
3. 實驗對比

(1)評測指標

選取F1 score進行評測。

def countF1(train, predict):
    count = 0 # 統計預測的正確的正樣本數
    for i in range(len(train)):
        if predict[i] == 1 and train[i] == 1:
            count += 1
    pre =  count * 1.0 / sum(predict) # 準確率
    recall =  count * 1.0 / sum(train) # 召回率
    return 2 * pre * recall / (pre + recall)
(2)對比結果

填充方式    訓練集_F1    測試集_F1
預設值0    0.70516717    0.59689922
均值(mean)    0.70186335    0.67768595
中位數(median)    0.70826833    0.67479675
眾數(mode)    0.70479134    0.68852459
上一個資料(pad)    0.70409712    0.62711864
下一個資料(bfill)    0.66981132    0.60169492
插值    0.69018405    0.61333333
KNN_3    0.71076923    0.66393443
KNN_6    0.70897833    0.68852459
KNN_10    0.70479134    0.68032787
隨機森林_feature3    0.571428571    0.4
隨機森林_feature46    0.585139319    0.41509434
(3)實驗小結

對於缺失值的處理,除了直接刪除缺失嚴重的特徵外,還可以選擇各種各樣的填充方法。對於每一種填充方式而言,都有其適用的場景,沒有絕對的好壞之分,因此在做資料預處理時,要多嘗試幾種填充方法,選擇表現最佳的即可。

 

本文完整程式碼已上傳至git(https://github.com/AHNU/fill_missing_values)

參考文獻

1. 訓練模型填充空值(fill null)的幾種方法

2. https://www.kaggle.com/pmarcelino/comprehensive-data-exploration-with-python