在Python中使用KNN算法處理缺失的數(shù)據(jù)
處理缺失的數(shù)據(jù)并不是一件容易的事。 方法的范圍從簡單的均值插補(bǔ)和觀察值的完全刪除到像MICE這樣的更高級的技術(shù)。 解決問題的挑戰(zhàn)性是選擇使用哪種方法。 今天,我們將探索一種簡單但高效的填補(bǔ)缺失數(shù)據(jù)的方法-KNN算法。
KNN代表" K最近鄰居",這是一種簡單算法,可根據(jù)定義的最接近鄰居數(shù)進(jìn)行預(yù)測。 它計(jì)算從您要分類的實(shí)例到訓(xùn)練集中其他所有實(shí)例的距離。
正如標(biāo)題所示,我們不會(huì)將算法用于分類目的,而是填充缺失值。 本文將使用房屋價(jià)格數(shù)據(jù)集,這是一個(gè)簡單而著名的數(shù)據(jù)集,僅包含500多個(gè)條目。
這篇文章的結(jié)構(gòu)如下:
- 數(shù)據(jù)集加載和探索
- KNN歸因
- 歸因優(yōu)化
- 結(jié)論
數(shù)據(jù)集加載和探索
如前所述,首先下載房屋數(shù)據(jù)集。 另外,請確保同時(shí)導(dǎo)入了Numpy和Pandas。 這是前幾行的外觀:

默認(rèn)情況下,數(shù)據(jù)集缺失值非常低-單個(gè)屬性中只有五個(gè):

讓我們改變一下。 您通常不會(huì)這樣做,但是我們需要更多缺少的值。 首先,我們創(chuàng)建兩個(gè)隨機(jī)數(shù)數(shù)組,其范圍從1到數(shù)據(jù)集的長度。 第一個(gè)數(shù)組包含35個(gè)元素,第二個(gè)數(shù)組包含20個(gè)(任意選擇):
- i1 = np.random.choice(a=df.index, size=35)
- i2 = np.random.choice(a=df.index, size=20)
這是第一個(gè)數(shù)組的樣子:

您的數(shù)組將有所不同,因?yàn)殡S機(jī)化過程是隨機(jī)的。 接下來,我們將用NAN替換特定索引處的現(xiàn)有值。 這是如何做:
- df.loc[i1, 'INDUS'] = np.nan
- df.loc[i2, 'TAX'] = np.nan
現(xiàn)在,讓我們再次檢查缺失值-這次,計(jì)數(shù)有所不同:

這就是我們從歸因開始的全部前置工作。 讓我們在下一部分中進(jìn)行操作。
KNN歸因
整個(gè)插補(bǔ)可歸結(jié)為4行代碼-其中之一是庫導(dǎo)入。 我們需要sklearn.impute中的KNNImputer,然后以一種著名的Scikit-Learn方式創(chuàng)建它的實(shí)例。 該類需要一個(gè)強(qiáng)制性參數(shù)– n_neighbors。 它告訴冒充參數(shù)K的大小是多少。
首先,讓我們選擇3的任意數(shù)字。稍后我們將優(yōu)化此參數(shù),但是3足以啟動(dòng)。 接下來,我們可以在計(jì)算機(jī)上調(diào)用fit_transform方法以估算缺失的數(shù)據(jù)。
最后,我們將結(jié)果數(shù)組轉(zhuǎn)換為pandas.DataFrame對象,以便于解釋。 這是代碼:
- from sklearn.impute import KNNImputer
- imputer = KNNImputer(n_neighbors=3)
- imputed = imputer.fit_transform(df)
- df_imputed = pd.DataFrame(imputed, columns=df.columns)
非常簡單。 讓我們現(xiàn)在檢查缺失值:

盡管如此,仍然存在一個(gè)問題-我們?nèi)绾螢镵選擇正確的值?
歸因優(yōu)化
該住房數(shù)據(jù)集旨在通過回歸算法進(jìn)行預(yù)測建模,因?yàn)槟繕?biāo)變量是連續(xù)的(MEDV)。 這意味著我們可以訓(xùn)練許多預(yù)測模型,其中使用不同的K值估算缺失值,并查看哪個(gè)模型表現(xiàn)最佳。
但首先是導(dǎo)入。 我們需要Scikit-Learn提供的一些功能-將數(shù)據(jù)集分為訓(xùn)練和測試子集,訓(xùn)練模型并進(jìn)行驗(yàn)證。 我們選擇了"隨機(jī)森林"算法進(jìn)行訓(xùn)練。 RMSE用于驗(yàn)證:
- from sklearn.model_selection import train_test_split
- from sklearn.ensemble import RandomForestRegressor
- from sklearn.metrics import mean_squared_error
- rmse = lambda y, yhat: np.sqrt(mean_squared_error(y, yhat))
以下是執(zhí)行優(yōu)化的必要步驟:
迭代K的可能范圍-1到20之間的所有奇數(shù)都可以
- 使用當(dāng)前的K值執(zhí)行插補(bǔ)
- 將數(shù)據(jù)集分為訓(xùn)練和測試子集
- 擬合隨機(jī)森林模型
- 預(yù)測測試集
- 使用RMSE進(jìn)行評估
聽起來很多,但可以歸結(jié)為大約15行代碼。 這是代碼段:
- def optimize_k(data, target):
- errors = []
- for k in range(1, 20, 2):
- imputer = KNNImputer(n_neighbors=k)
- imputed = imputer.fit_transform(data)
- df_imputed = pd.DataFrame(imputed, columns=df.columns)
- X = df_imputed.drop(target, axis=1)
- y = df_imputed[target]
- X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
- model = RandomForestRegressor()
- model.fit(X_train, y_train)
- preds = model.predict(X_test)
- error = rmse(y_test, preds)
- errors.append({'K': k, 'RMSE': error})
- return errors
現(xiàn)在,我們可以使用修改后的數(shù)據(jù)集(在3列中缺少值)調(diào)用optimize_k函數(shù),并傳入目標(biāo)變量(MEDV):
- k_errors = optimize_k(data=df, target='MEDV')
就是這樣! k_errors數(shù)組如下所示:

以視覺方式表示:

看起來K = 15是給定范圍內(nèi)的最佳值,因?yàn)樗鼘?dǎo)致最小的誤差。 我們不會(huì)涵蓋該錯(cuò)誤的解釋,因?yàn)樗隽吮疚牡姆秶?讓我們在下一節(jié)中總結(jié)一下。
總結(jié)
編寫處理缺少數(shù)據(jù)歸因的代碼很容易,因?yàn)橛泻芏喱F(xiàn)有的算法可以讓我們直接使用。 但是我們很難理解里面原因-了解應(yīng)該推定哪些屬性,不應(yīng)該推算哪些屬性。 例如,可能由于客戶未使用該類型的服務(wù)而缺失了某些值,因此沒有必要執(zhí)行估算。
最終確定是否需要進(jìn)行缺失數(shù)據(jù)的處理,還需要有領(lǐng)域的專業(yè)知識(shí),與領(lǐng)域?qū)<疫M(jìn)行咨詢并研究領(lǐng)域是一種很好的方法。