機器學(xué)習(xí)中處理缺失值的9種方法
數(shù)據(jù)科學(xué)就是關(guān)于數(shù)據(jù)的。它是任何數(shù)據(jù)科學(xué)或機器學(xué)習(xí)項目的關(guān)鍵。在大多數(shù)情況下,當(dāng)我們從不同的資源收集數(shù)據(jù)或從某處下載數(shù)據(jù)時,幾乎有95%的可能性我們的數(shù)據(jù)中包含缺失的值。我們不能對包含缺失值的數(shù)據(jù)進行分析或訓(xùn)練機器學(xué)習(xí)模型。這就是為什么我們90%的時間都花在數(shù)據(jù)預(yù)處理上的主要原因。我們可以使用許多技術(shù)來處理丟失的數(shù)據(jù)。在這個文章中,我將分享處理數(shù)據(jù)缺失的9種方法,但首先讓我們看看為什么會出現(xiàn)數(shù)據(jù)缺失以及有多少類型的數(shù)據(jù)缺失。
不同類型的缺失值
缺失的值主要有三種類型。
- 完全隨機缺失(MCAR):當(dāng)數(shù)據(jù)為MCAR時,如果所有觀測的缺失概率都相同,則一個變量完全隨機缺失,這意味著數(shù)據(jù)缺失與數(shù)據(jù)集中任何其他觀察到的或缺失的值完全沒有關(guān)系。換句話說,那些缺失的數(shù)據(jù)點是數(shù)據(jù)集的一個隨機子集。
- 丟失數(shù)據(jù)不是隨機的(MNAR):顧名思義,丟失的數(shù)據(jù)和數(shù)據(jù)集中的任何其他值之間存在某種關(guān)系。
- 隨機丟失(MAR):這意味著數(shù)據(jù)點丟失的傾向與丟失的數(shù)據(jù)無關(guān),但與數(shù)據(jù)集中其他觀察到的數(shù)據(jù)有關(guān)。
數(shù)據(jù)集中缺少值的原因有很多。例如,在數(shù)據(jù)集的身高和年齡,會有更多年齡列中缺失值,因為女孩通常隱藏他們的年齡相同的如果我們準(zhǔn)備工資的數(shù)據(jù)和經(jīng)驗,我們將有更多的薪水中的遺漏值因為大多數(shù)男人不喜歡分享他們的薪水。在更大的情況下,比如為人口、疾病、事故死亡者準(zhǔn)備數(shù)據(jù),納稅人記錄通常人們會猶豫是否記下信息,并隱藏真實的數(shù)字。即使您從第三方資源下載數(shù)據(jù),仍然有可能由于下載時文件損壞而丟失值。無論原因是什么,我們的數(shù)據(jù)集中丟失了值,我們需要處理它們。讓我們看看處理缺失值的9種方法。
這里使用的也是經(jīng)典的泰坦尼克的數(shù)據(jù)集
讓我們從加載數(shù)據(jù)集并導(dǎo)入所有庫開始。
- import pandas as pd
- df=pd.read_csv("data/titanic.csv",usecols=['Age','Cabin','Survived'])
- df.isnull().mean()
- df.dtypes
運行上述代碼塊后,您將看到Age、Cabin和裝載裝載包含空值。Age包含所有整數(shù)值,而Cabin包含所有分類值。
1、均值、中值、眾數(shù)替換
在這種技術(shù)中,我們將null值替換為列中所有值的均值/中值或眾數(shù)。
平均值(mean):所有值的平均值
- def impute_nan(df,column,mean):
- df[column+'_mean']=df[column].fillna(mean) ##NaN -> mean
- impute_nan(df,'Age',df.Age.mean()) ##mean of Age(29.69)

中值(median):所有值的中心值
- def impute_nan(df,column,median):
- df[column+'_mean']=df[column].fillna(median)
- impute_nan(df,'Age',df.Age.median()) ##median of Age(28.0)

眾數(shù)(mode):最常見的值
- def impute_nan(df,column,mode):
- df[column+'_mean']=df[column].fillna(mode)
- impute_nan(df,'Age',df.Age.mode()) ##mode of Age(24.0)

優(yōu)點
- 易于實現(xiàn)(對異常值健壯)
- 獲得完整數(shù)據(jù)集的更快方法
缺點
- 原始方差的變化或失真
- 影響相關(guān)性
- 對于分類變量,我們需要眾數(shù)。平均值和中位數(shù)都不行。
2、隨機樣本估算
在這種技術(shù)中,我們用dataframe中的隨機樣本替換所有nan值。它被用來輸入數(shù)值數(shù)據(jù)。我們使用sample()對數(shù)據(jù)進行采樣。在這里,我們首先取一個數(shù)據(jù)樣本來填充NaN值。然后更改索引,并將其替換為與NaN值相同的索引,最后將所有NaN值替換為一個隨機樣本。
優(yōu)點
- 容易實現(xiàn)
- 方差失真更小
缺點
- 我們不能把它應(yīng)用于每一種情況
用隨機樣本注入替換年齡列NaN值
- def impute_nan(df,variable):
- df[variable+"_random"]=df[variable]
- ##It will have the random sample to fill the na
- random_sample=df[variable].dropna().sample(df[variable].isnull().sum(),random_state=0)
- ##pandas need to have same index in order to merge the dataset
- random_sample.index=df[df[variable].isnull()].index #replace random_sample index with NaN values index
- #replace where NaN are there
- df.loc[df[variable].isnull(),variable+'_random']=random_sample
- col=variable+"_random"
- df = df.drop(col,axis=1)
- impute_nan(df,"Age")

3、用新特性獲取NAN值
這種技術(shù)在數(shù)據(jù)不是完全隨機丟失的情況下最有效。在這里,我們在數(shù)據(jù)集中添加一個新列,并將所有NaN值替換為1。
優(yōu)點
- 容易實現(xiàn)
- 獲取了了NaN值的重要性
缺點
- 創(chuàng)建額外的特性(維度詛咒)
- import numpy as np
- df['age_nan']=np.where(df['Age'].isnull(),1,0)
- ## It will create one new column that contains value 1 in the rows where Age value is NaN, otherwise 0.
4、End of Distribution
在這種技術(shù)中,我們用第3個標(biāo)準(zhǔn)偏差值(3rd standard deviation)替換NaN值。它還用于從數(shù)據(jù)集中刪除所有異常值。首先,我們使用std()計算第3個標(biāo)準(zhǔn)偏差,然后用該值代替NaN。優(yōu)點
- 容易實現(xiàn)。
- 抓住了缺失值的重要性,如果有的話。
缺點
- 使變量的原始分布失真。
- 如果NAN的數(shù)量很大。它將掩蓋分布中真正的異常值。
- 如果NAN的數(shù)量較小,則替換后的NAN可以被認為是一個離群值,并在后續(xù)的特征工程中進行預(yù)處理。
- def impute_nan(df,variable,median,extreme):
- df[variable+"_end_distribution"]=df[variable].fillna(extreme)
- extreme=df.Age.mean()+3*df.Age.std() ##73.27--> 3rd std deviation
- impute_nan(df,'Age',df.Age.median(),extreme)



5、任意值替換
在這種技術(shù)中,我們將NaN值替換為任意值。任意值不應(yīng)該更頻繁地出現(xiàn)在數(shù)據(jù)集中。通常,我們選擇最小離群值或最后離群值作為任意值。
優(yōu)點
- 容易實現(xiàn)
- 獲取了缺失值的重要性,如果有的話
缺點
- 必須手動確定值。
- def impute_nan(df,var):
- df[var+'_zero']=df[var].fillna(0) #Filling with 0(least outlier)
- df[var+'_hundred']=df[var].fillna(100) #Filling with 100(last)
- impute_nan(df,'Age')


6、頻繁類別歸責(zé)
該技術(shù)用于填充分類數(shù)據(jù)中的缺失值。在這里,我們用最常見的標(biāo)簽替換NaN值。首先,我們找到最常見的標(biāo)簽,然后用它替換NaN。
優(yōu)點
- 容易實現(xiàn)
缺點
- 由于我們使用的是更頻繁的標(biāo)簽,所以如果有很多NaN值,它可能會以一種過度表示的方式使用它們。
- 它扭曲了最常見的標(biāo)簽之間的關(guān)系。
- def impute_nan(df,variable):
- most_frequent_category=df[variable].mode()[0] ##Most Frequent
- df[variable].fillna(most_frequent_category,inplace=True)
- for feature in ['Cabin']: ##List of Categorical Features
- impute_nan(df,feature)



7、nan值視為一個新的分類
在這種技術(shù)中,我們只需用一個新的類別(如Missing)替換所有NaN值。
- df['Cabin']=df['Cabin'].fillna('Missing') ##NaN -> Missing
8、使用KNN填充
在這項技術(shù)中,我們使用sklearn創(chuàng)建一個KNN imputer模型,然后我們將該模型與我們的數(shù)據(jù)進行擬合,并預(yù)測NaN值。它被用來計算數(shù)值。這是一個5步的過程。
- 創(chuàng)建列列表(整數(shù)、浮點)
- 輸入估算值,確定鄰居。
- 根據(jù)數(shù)據(jù)擬合估算。
- 轉(zhuǎn)換的數(shù)據(jù)
- 使用轉(zhuǎn)換后的數(shù)據(jù)創(chuàng)建一個新的數(shù)據(jù)框架。
優(yōu)點
- 容易實現(xiàn)
- 結(jié)果一般情況下會最好
缺點
- 只適用于數(shù)值數(shù)據(jù)
我們在上篇文章中已經(jīng)有過詳細的介紹,這里就不細說了
在python中使用KNN算法處理缺失的數(shù)據(jù)
9、刪除所有NaN值
它是最容易使用和實現(xiàn)的技術(shù)之一。只有當(dāng)NaN值小于10%時,我們才應(yīng)該使用這種技術(shù)。
優(yōu)點:
- 容易實現(xiàn)
- 快速處理
缺點:
- 造成大量的數(shù)據(jù)丟失
- df.dropna(inplace=True) ##Drop all the rows that contains NaN
總結(jié)
還有更多處理丟失值的其他技術(shù)。我們的目標(biāo)是找到最適合我們的問題的技術(shù),然后實施它。處理丟失的值總是一個更好的主意,但有時我們不得不刪除所有的值。它基本上取決于數(shù)據(jù)的類型和數(shù)量。
最有,所有的代碼在這里都能找到:https://github.com/Abhayparashar31/feature-engineering