橘智橘智
FakeOrange
预计阅读时间:7分钟17秒

数据分析2.6 —— 更高级的插补,处理缺失数据

缺失数据永远是一个头疼的问题~

0
0

概述


在数据分析和机器学习过程中,缺失数据的处理不仅是数据预处理的一个重要环节,而且直接关系到最终结果的有效性和可靠性。合理地处理缺失数据可以提高数据的质量,增强模型的性能,确保分析结论的准确性,从而在实际应用中获得更好的结果。


下面开整~


具体方式及实现


1. KNN 插补

原理:KNN 插补(K-Nearest Neighbors Imputation)使用 K 最近邻算法,通过计算缺失样本与其他样本之间的距离,选择 K 个最近邻样本,并用这些邻居的均值填补缺失值。

优劣

  • 优点:能够捕捉数据的局部结构,适用于非线性数据。
  • 缺点:计算复杂度高,特别是在数据集较大时;需要选择 K 的值,K 值选择不当会影响结果。


实现示例


import pandas as pd
from sklearn.impute import KNNImputer

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [None, 5, 6, 7],
    'C': [8, 9, 10, None]
})

# 初始化 KNN 插补器,设置 K=2
imputer = KNNImputer(n_neighbors=2)

# 进行插补
data_imputed = imputer.fit_transform(data)

# 将结果转换为 DataFrame
data_imputed = pd.DataFrame(data_imputed, columns=data.columns)
print(data_imputed)

2. 迭代插补


原理:迭代插补使用多种模型对缺失值进行插补。在每一次迭代中,使用现有特征来预测缺失值,并更新缺失值,直到收敛为止。

优劣

  • 优点:能够捕捉特征之间的关系,适用于复杂数据集。
  • 缺点:计算复杂度较高,特别是在特征数量较多时;需要选择合适的模型进行插补。


实现示例



import pandas as pd
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.linear_model import BayesianRidge

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [None, 5, 6, 7],
    'C': [8, 9, 10, None]
})

# 使用迭代插补,选择贝叶斯回归模型
imputer = IterativeImputer(estimator=BayesianRidge(), max_iter=10, random_state=0)

# 进行插补
data_imputed = imputer.fit_transform(data)

# 将结果转换为 DataFrame
data_imputed = pd.DataFrame(data_imputed, columns=data.columns)
print(data_imputed)

3. 线性插补


原理:线性插补通过线性推断,用前后观测值计算插补值。适用于数据变化相对平稳的情况。

优劣

  • 优点:实现简单,计算效率高,适用于线性关系。
  • 缺点:对非线性数据表现较差,可能在某些情况下引入偏差。


实现示例


import pandas as pd

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [None, 5, 6, 7],
    'C': [8, 9, 10, None]
})

# 进行线性插补
data['A'] = data['A'].interpolate(method='linear')
data['B'] = data['B'].interpolate(method='linear')
data['C'] = data['C'].interpolate(method='linear')

print(data)

4. 均值/中位数/众数插补


原理:用特征的均值、中位数或众数填补缺失值,适用于数值型和类别型数据。

优劣

  • 优点:实现简单,计算效率高。
  • 缺点:可能导致数据分布失真,尤其在缺失比例较高时。


实现示例


import pandas as pd

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [None, 5, 6, 7],
    'C': [8, 9, 10, None]
})

# 使用均值插补
data['A'].fillna(data['A'].mean(), inplace=True)
# 使用中位数插补
data['B'].fillna(data['B'].median(), inplace=True)
# 使用众数插补
data['C'].fillna(data['C'].mode()[0], inplace=True)

print(data)

5. 回归插补


原理:通过构建回归模型来预测缺失值,使用其他特征的信息来填补缺失的特征值。

优劣

  • 优点:考虑特征之间的关系,能够得到更合理的插补结果。
  • 缺点:模型不准确时可能导致偏差,计算复杂度较高。


实现示例


import pandas as pd
from sklearn.linear_model import LinearRegression

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [1, 2, 3, 4],
    'C': [None, 1, 2, 3]
})

# 创建线性回归模型
model = LinearRegression()

# 进行插补
# 使用 B 和 C 的已有值来预测 A
data_non_missing = data[data['A'].notnull()]
X = data_non_missing[['B', 'C']]
y = data_non_missing['A']
model.fit(X, y)

# 对缺失值进行预测
missing_values = data[data['A'].isnull()]
X_missing = missing_values[['B', 'C']]
data.loc[data['A'].isnull(), 'A'] = model.predict(X_missing)

print(data)

6. 多重插补


原理:多重插补通过多次插补生成多个不同的插补数据集,在分析时将这些数据集的结果结合起来,从而更好地反映缺失值的不确定性。

优劣

  • 优点:更好地捕捉数据中的不确定性,减少插补引入的偏差。
  • 缺点:计算复杂度高,分析和实现比较麻烦。


实现示例


import pandas as pd
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
from sklearn.linear_model import BayesianRidge

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, 2, None, 4],
    'B': [None, 5, 6, 7],
    'C': [8, 9, 10, None]
})

# 使用多重插补
imputer = IterativeImputer(estimator=BayesianRidge(), max_iter=10, random_state=0)
data_imputed = imputer.fit_transform(data)

# 将结果转换为 DataFrame
data_imputed = pd.DataFrame(data_imputed, columns=data.columns)
print(data_imputed)

7. 时序插补


原理:在时间序列数据中,可以用前一个或后一个观测值进行插补。常见的方法有向前填充和向后填充。

优劣

  • 优点:简单易懂,适合时间序列数据。
  • 缺点:对波动较大的时间序列数据,可能引入偏差。


实现示例


import pandas as pd

# 创建示例数据集
data = pd.DataFrame({
    'Time': [1, 2, 3, 4],
    'Value': [1, None, 3, None]
})

# 向前填充
data['Value'].fillna(method='ffill', inplace=True)
print(data)

8. 分类器插补


原理:对缺失的类别特征,使用分类器(如决策树、随机森林等)进行预测,利用其他特征的信息填补缺失值。

优劣

  • 优点:能够捕捉复杂的特征关系,提高插补的准确性。
  • 缺点:需要训练模型,计算复杂度相对较高。


实现示例


import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# 创建示例数据集
data = pd.DataFrame({
    'A': ['a', None, 'b', 'a'],
    'B': [1, 2, 3, 4]
})

# 训练模型填补缺失值
model = RandomForestClassifier()

# 将特征转换为数值形式(标签编码)
data['A'] = data['A'].astype('category').cat.codes  # 将类别特征转换为数值

# 提取缺失值和非缺失值的数据
data_non_missing = data[data['A'].notnull()]
data_missing = data[data['A'].isnull()]

# 训练模型
X_train = data_non_missing[['B']]  # 特征
y_train = data_non_missing['A']     # 目标变量
model.fit(X_train, y_train)

# 对缺失值进行预测
X_missing = data_missing[['B']]
data.loc[data['A'].isnull(), 'A'] = model.predict(X_missing)

# 还原类别编码
data['A'] = data['A'].astype('category').cat.rename_categories(['a', 'b', None])  # 还原为原始类别
print(data)


9. 数据插值

原理:数据插值使用不同的插值算法(如样条插值、拉格朗日插值)在已知数据点之间进行平滑插补,适合于数值型数据。

优劣

  • 优点:产生平滑的插补结果。
  • 缺点:对于边界值和非线性数据可能产生不合理的插补。


实现示例


import pandas as pd

# 创建示例数据集
data = pd.DataFrame({
    'X': [1, 2, None, 4, 5],
    'Y': [2, None, 3, 4, 5]
})

# 使用样条插值进行插补
data['Y'] = data['Y'].interpolate(method='spline', order=2)  # 二次样条插值
print(data)

10. 随机插补

原理:随机插补根据已有数据的分布随机选择一个值来填补缺失值,这种方法在某些情况下可以引入更多的随机性,从而更好地模拟真实数据的变动。

优劣

  • 优点:保留数据的分布特性,能够引入随机性。
  • 缺点:可能会引入噪声,尤其在缺失值比例较高的情况下。


实现示例


import pandas as pd
import numpy as np

# 创建示例数据集
data = pd.DataFrame({
    'A': [1, None, 3, None, 5],
    'B': [5, 6, None, 8, 9]
})

# 对缺失值进行随机插补
data['A'] = data['A'].fillna(np.random.choice(data['A'].dropna(), size=data['A'].isna().sum()))
data['B'] = data['B'].fillna(np.random.choice(data['B'].dropna(), size=data['B'].isna().sum()))

print(data)

总结


在处理缺失值时,可以根据数据的特性和分析目标选择合适的插补方法。以下是对不同插补方法的优缺点的总结:

  • KNN 插补:适用于非线性数据,但计算复杂度高。
  • 迭代插补:能够捕捉复杂关系,计算复杂度较高。
  • 线性插补:简单高效,适合线性关系,可能对非线性数据不适用。
  • 均值/中位数/众数插补:实现简单,但可能导致数据失真。
  • 回归插补:能够合理插补缺失值,但模型不准确时风险较高。
  • 多重插补:捕捉不确定性,但计算复杂度高。
  • 时序插补:适合时间序列数据,可能引入偏差。
  • 分类器插补:提高插补准确性,但训练成本高。
  • 数据插值:平滑结果,但可能对边界值不合理。
  • 随机插补:保留分布特性,可能引入噪声。


选择合适的插补方法通常需要尝试多种方法并结合模型性能评估结果,以确定最佳插补策略。


评论