天天看點

處理缺失值的三個層級的方法總結

作者:deephub

缺失值

缺失值是現實資料集中的常見問題,處理缺失值是資料預處理的關鍵步驟。缺失值可能由于各種原因而發生,例如資料的結構和品質、資料輸入錯誤、傳輸過程中的資料丢失或不完整的資料收集。這些缺失的值可能會影響機器學習模型的準确性和可靠性,因為它們可能會引入偏差并扭曲結果,有些模型甚至在在缺少值的情況下根本無法工作。是以在構模組化型之前,适當地處理缺失值是必要的。

處理缺失值的三個層級的方法總結

本文将展示如何使用三種不同級别的技術中處理這些缺失值:

  • 初級:删除,均值/中值插補,使用領域知識進行估計
  • 中級:回歸插補, K-Nearest neighbors (KNN) 插補
  • 進階:鍊式方程(MICE)的多元插補, MICEforest

檢查缺失的值

首先必須檢查每個特性中有多少缺失值。作為探索性資料分析的一部分,我們可以使用以下代碼來做到這一點:

導入pandas并在資料集中讀取它們,對于下面的示例,我們将使用葡萄酒品質資料集。

import pandas as pd
df = pd.read_csv('Wine_Quality.csv')           

然後可以用下面的代碼行檢查缺失的值。

df.isnull().sum()           
處理缺失值的三個層級的方法總結

可以使用以下方法檢視任何特性中包含缺失值的行:

df_filtered = df[df.isnull().any(axis=1)]
df_filtered.head()           
處理缺失值的三個層級的方法總結

現在我們可以開始處理這些缺失的值了。

初級方法

最簡單的方法是删除行或列(特性)。這通常是在缺失值的百分比非常大或缺失值對分析或結果沒有顯著影響時進行的。

删除缺少值的行。

df_droprows = df.dropna()
df_droprows.isnull().sum()           
處理缺失值的三個層級的方法總結

使用以下方法删除列或特性:

df_dropcols = df.drop(columns=['type', 'fixed acidity', 'citric acid', 'volatile acidity', 'residual sugar', 'chlorides', 'pH', 'sulphates'])
df_dropcols.isnull().sum()           

通過删除行,我們最終得到一個更短的資料集。當删除特征時,我們最終會得到一個完整的資料集,但會丢失某些特征。

print("Shape when dropping rows: ", df_droprows.shape)
print("Shape when dropping features: ", df_dropcols.shape)           
處理缺失值的三個層級的方法總結

這兩種方法都最直接的方法,而且都會導緻丢失有價值的資料——是以一般情況下不建議使用。

均值/中值插補

下一個初級的方法是用特征的平均值或中值替換缺失的值。在這種情況下不會丢失特征或行。但是這種方法隻能用于數值特征(如果使用平均值,我們應該確定資料集沒有傾斜或包含重要的異常值)。

比如下面用均值來計算缺失值:

df = df.fillna(df.mean())           

現在讓我們檢查其中一個估算值:

df[df.index==86]           
處理缺失值的三個層級的方法總結

如果要用中值,可以使用:

df = df.fillna(df.median())
df[df.index==86]           
處理缺失值的三個層級的方法總結

可以看到這裡中值和平均值還是有差別的

衆數

與上面的方法一樣,該方法用特征的模式或最常見的值替換缺失的值。這種方法可以用于分類特征。

首先,讓我們檢查一下是否有一個類别占主導地位。我們可以通過value_counts方法來實作:

df['type'].value_counts()           
處理缺失值的三個層級的方法總結

可以看到有一個“白色”數量最多。是以可以用下面的方式進行填充:

df['type'] = df['type'].fillna(df['type'].mode())           

Scikit-Learn的SimpleImputer類

也可以使用Scikit-learn的SimpleImputer類執行平均值、中值和衆數的插補。将政策設定為“mean”,“median”或“most_frequency”即可

df_numeric = df.drop(columns='type')
imputer_median = SimpleImputer(strategy='median')
imputer_median.fit(df_numeric)
df_imputed_median = pd.DataFrame(imputer_median.transform(df_numeric), columns=df_numeric.columns)
df_imputed_median.head()           

我們也可以将政策設定為' constant ',并指定' fill_value '來填充一個常量值。

均值/中位數/衆數的優點:

  • 簡單和快速實作
  • 它保留了樣本量,并降低了下遊分析(如機器學習模型)的偏差風險。
  • 與更複雜的方法相比,它的計算成本更低。

缺點:

  • 沒有說明資料的可變性或分布,可能會導緻估算值不能代表真實值。
  • 可能會低估或高估缺失值,特别是在具有極端值或異常值的資料集中。
  • 減少方差和人為誇大相關系數在估算資料集。
  • 它假設缺失的值是完全随機缺失(MCAR),這可能并不總是這樣

使用領域知識進行評估

處理缺失資料的另一種可能方法是使用基于領域知識或業務規則的估計來替換缺失的值。可以通過咨詢相關領域的專家,讓他們提供專業的見解,這樣能夠估算出合理和可信的缺失值。

但是這種方法并不一定在現實中就能夠很好的實施,因為我們需要專業的人士來確定它産生準确和可靠的估算,但是這樣的領域專家并不多。是以我們這裡把它歸在初級方法中。

中級方法

還有一些稍微進階一些的技術來填充那些缺失的值,我們将使用預測模型來解決問題。但在此之前需要更好地了解缺失值的性質。

缺失值的類型

在我們繼續使用更進階的技術之前,需要考慮一下在資料集中可能遇到的缺失類型。資料集中有不同類型的缺失,了解缺失類型有助于确定合适的方法。以下是一些常見的類型:

完全随機缺失( Missing Completely at Random):在這種類型中,缺失的值是完全随機的,這意味着一個值缺失的機率不依賴于任何觀察到的或未觀察到的變量。例如,如果一個受訪者在調查中不小心跳過了一個問題,這就是MCAR。

随機丢失(Missing at Random):在這種類型中,一個值缺失的機率取決于觀察到的變量,而不是值本身。例如,如果調查對象不太可能回答敏感問題,但不回答問題的傾向取決于可觀察到的變量(如年齡、性别和教育),那麼這就是MAR。

非随機丢失(Missing Not at Random):在這種類型中,一個值缺失的機率取決于未觀察到的變量,包括缺失值值本身。例如,如果抑郁程度較高的個體不太可能報告他們的抑郁水準,而不報告的傾向在資料中是無法觀察到的,那麼這就是MNAR。

回歸插補

我們将使用一個回歸模型來對那些缺失的值進行有根據的猜測,通過分析資料集中的其他特征,并使用它們的相關性來填補。

在處理遵循某種模式(MAR或MCAR)的缺失資料時,回歸插補特别有用。 因為當特征之間存在很強的相關性時,這種方法很有效。

我們這裡将建立一個不包含分類特征的資料版本。然後以為每一列的缺失值拟合線性回歸模型。這裡就需要使用Scikit-learn的線性回歸子產品。

import pandas as pd
from sklearn.linear_model import LinearRegression
# Read data
df = pd.read_csv('Wine_Quality.csv')
# Make sub dataframe with only numeric features
df = df.drop(columns='type')
# Separate the columns with missing values
missing_cols = df.columns[df.isna().any()].tolist()
non_missing_cols = list(set(df.columns) - set(missing_cols))
print(missing_cols)
# loop over each column with missing values
for col in missing_cols:
# Create a copy of the dataframe without missing values in the current column
df_temp = df.dropna(subset=[col] + non_missing_cols)

# Split the dataframe into features (X) and target variable (y)
X = df_temp[non_missing_cols]
y = df_temp[col]

# Create and fit a linear regression model
lr = LinearRegression()
lr.fit(X, y)

# Impute missing values in the current column using the fitted model
df.loc[df[col].isna(), col] = lr.predict(df.loc[df[col].isna(), non_missing_cols])           
處理缺失值的三個層級的方法總結

回歸插補的優點:

  • 可以處理大量缺失值。
  • 可以保留資料集的統計屬性,例如均值、方差和相關系數。
  • 可以通過減少偏差和增加樣本量來提高下遊分析(例如機器學習模型)的準确性。

回歸插補的缺點:

  • 它假設缺失變量和觀察到的變量之間存線上性關系。
  • 如果缺失值不是随機缺失 (MAR) 或完全随機缺失 (MCAR),則可能會引入偏差。
  • 可能不适用于分類或有序變量。
  • 在計算上昂貴且耗時,尤其是對于大型資料集。

(KNN) 插補

另一種方法是聚類模型,例如K-最近鄰 (KNN) 來估計那些缺失值。 這與回歸插補類似,隻是使用不同的算法來預測缺失值。

import pandas as pd
from sklearn.impute import KNNImputer
# Read data
df = pd.read_csv('Wine_Quality.csv')
# Make sub dataframe with only numeric features
df = df.drop(columns='type')
# create a KNN imputer object
imputer = KNNImputer(n_neighbors=5)
# impute missing values using KNN
df = pd.DataFrame(imputer.fit_transform(df), columns=df.columns)           
處理缺失值的三個層級的方法總結

這裡我們就要介紹一個包fancyimpute,它包含了各種插補方法:

pip install fancyimpute           

使用的方法如下:

# Import the necessary libraries
import numpy as np
import pandas as pd
from fancyimpute import KNN
# Load the dataset
df = pd.read_csv('Wine_Quality.csv')
# Drop non-numeric features
df = df.drop(columns='type')
# Get list of columns with missing values
missing_cols = df.columns[df.isna().any()].tolist()
# Create an instance of the KNN imputer
imputer = KNN()
# Fit and transform the imputer to the dataset
imputed_array = imputer.fit_transform(df[missing_cols])
# Replace the missing values in the original dataset
df[missing_cols] = imputed_array
# View the imputed dataset
df           

KNN 插補的優點:

  • 可以捕獲變量之間複雜的非線性關系。
  • 不對資料的分布或變量之間的相關性做出假設。
  • 比簡單的插補方法(例如均值或中值插補)更準确,尤其是對于中小型資料集。

缺點:

  • 計算上可能很昂貴,尤其是對于大型資料集或高維資料。
  • 可能對距離度量的選擇和選擇的最近鄰居的數量敏感,這會影響準确性。
  • 對于高度傾斜或稀疏的資料表現不佳。

進階方法

通過鍊式方程 (MICE) 進行多元插補

MICE 是一種常用的估算缺失資料的方法。 它的工作原理是将每個缺失值替換為一組基于模型的合理值,該模型考慮了資料集中變量之間的關系。

該算法首先根據其他完整的變量為資料集中的每個變量建立一個預測模型。 然後使用相應的預測模型估算每個變量的缺失值。 這個過程重複多次,每一輪插補都使用前一輪的插補值,就好像它們是真的一樣,直到達到收斂為止。

然後将多個估算資料集組合起來建立一個最終資料集,其中包含所有缺失資料的估算值。 MICE 是一種強大而靈活的方法,可以處理具有許多缺失值和變量之間複雜關系的資料集。 它已成為許多領域(包括社會科學、健康研究和環境科學)中填補缺失資料的流行選擇。

fancyimpute包就包含了這個方法的實作,我們可以直接拿來使用

import numpy as np
import pandas as pd
from fancyimpute import IterativeImputer
# Read data
df = pd.read_csv('Wine_Quality.csv')
# Convert type column to category (so that miceforest can handle as a categorical attribute rather than string)
df= df.drop(columns='type')
# Get list of columns with missing values
missing_cols = df.columns[df.isna().any()].tolist()
# Create an instance of the MICE algorithm
imputer = IterativeImputer()
# Fit the imputer to the dataset
imputed_array = imputer.fit_transform(df[missing_cols])
# Replace the missing values in the original dataset
df[missing_cols] = imputed_array
# View the imputed dataset
df           

這個實作沒法對分類變量進行填充,那麼對于分類變量怎麼辦呢?

MICEforest

MICEforest 是 MICE的變體,它使用 lightGBM 算法來插補資料集中的缺失值,這是一個很奇特的想法,對吧。

我們可以使用 miceforest 包來實作它

pip install miceforest 
#或
conda install -c conda-forge miceforest           

使用也很簡單:

import pandas as pd
import miceforest as mf
# Read data
df = pd.read_csv('Wine_Quality.csv')
# Convert type column to category (so that miceforest can handle as a categorical attribute rather than string)
df['type'] = df['type'].astype('category')
# Create an instance of the MICE algorithm
imputer = mf.ImputationKernel(data=df, 
save_all_iterations=True, 
random_state=42)
# Fit the imputer to the dataset. Set number of iterations to 3
imputer.mice(3, verbose=True)
# Generate the imputed dataset
imputed_df = imputer.complete_data()
# View the imputed dataset
imputed_df           

可以看到,分類變量 'type' 的缺失值已經被填充了

總結

我們這裡介紹了三個層級的缺失值的處理方法,這三種方法的選擇将取決于資料集、缺失資料的數量和分析目标。也需要仔細考慮輸入缺失資料對最終結果的潛在影響。處理缺失資料是資料分析中的關鍵步驟,使用合适的填充方法可以幫助我們解鎖隐藏在資料中的見解,而從主題專家那裡尋求輸入并評估輸入資料的品質有助于確定後續分析的有效性。

作者:Honzik Jurza

繼續閱讀