机器学习特征预处理之分类缺失填充

📅 2026/7/15 21:44:49 👁️ 阅读次数 📝 编程学习
机器学习特征预处理之分类缺失填充

示例

import pandas as pd import numpy as np from sklearn.impute import SimpleImputer def categorical_imputation(X_train, X_test, cat_cols, strategy='most_frequent', fill_value=None, verbose=True): """ 分类缺失填充 - 必须在编码前执行! """ if not cat_cols: return X_train, X_test # ⭐ 关键修复:统一将 None 转换为 np.nan X_train = X_train.replace({None: np.nan}) X_test = X_test.replace({None: np.nan}) def is_categorical_column(col): if col not in X_train.columns: return False if (pd.api.types.is_object_dtype(X_train[col]) or pd.api.types.is_categorical_dtype(X_train[col]) or pd.api.types.is_string_dtype(X_train[col])): return True if pd.api.types.is_integer_dtype(X_train[col]): n_unique = X_train[col].nunique() n_total = len(X_train[col]) if n_total > 0 and n_unique / n_total < 0.05: if verbose: print(f" ℹ️ '{col}' 识别为整数编码分类 (唯一值: {n_unique})") return True return False valid_cat_cols = [col for col in cat_cols if is_categorical_column(col)] ignored_cols = set(cat_cols) - set(valid_cat_cols) if ignored_cols and verbose: print(f" ⚠️ 忽略以下列: {ignored_cols}") if not valid_cat_cols: if verbose: print(f" ⚠️ 没有有效的分类列需要填充") return X_train, X_test valid_strategies = ['most_frequent', 'constant'] if strategy not in valid_strategies: if verbose: print(f" ⚠️ 策略 '{strategy}' 不适用于分类数据,切换为 'most_frequent'") strategy = 'most_frequent' if strategy == 'constant' and fill_value is None: fill_value = 'UNKNOWN' if verbose: print(f" ℹ️ fill_value未指定,使用默认值: '{fill_value}'") for col in valid_cat_cols: train_vals = set(X_train[col].dropna().unique()) test_vals = set(X_test[col].dropna().unique()) new_cats = test_vals - train_vals if new_cats and verbose: print(f" ⚠️ 测试集新类别 '{col}': {new_cats}") try: if strategy == 'constant': imputer = SimpleImputer(strategy=strategy, fill_value=fill_value) else: imputer = SimpleImputer(strategy=strategy) X_train.loc[:, valid_cat_cols] = imputer.fit_transform(X_train[valid_cat_cols]) X_test.loc[:, valid_cat_cols] = imputer.transform(X_test[valid_cat_cols]) if verbose: print(f" ✅ 分类填充完成: {len(valid_cat_cols)} 列 (策略: {strategy})") except Exception as e: print(f" ❌ 分类填充失败: {e}") print(f" 问题列: {valid_cat_cols}") print(f" 数据类型: {X_train[valid_cat_cols].dtypes}") raise return X_train, X_test

调用示例

print("=" * 60) print("测试场景1:基本众数填充") print("=" * 60) # 创建训练集 X_train = pd.DataFrame({ 'color': ['red', 'blue', None, 'red', 'green', None], 'size': ['M', 'L', 'M', None, 'S', 'M'], 'category': ['A', 'B', 'A', 'C', None, 'A'], 'price': [100, 200, 150, 300, 250, 180] # 数值列 }) # 创建测试集 X_test = pd.DataFrame({ 'color': ['blue', None, 'red'], 'size': ['L', 'M', None], 'category': ['B', 'C', 'A'], 'price': [220, 190, 210] }) print("原始训练集:") print(X_train) print("\n原始测试集:") print(X_test) # 执行填充 cat_cols = ['color', 'size', 'category'] X_train_filled, X_test_filled = categorical_imputation( X_train, X_test, cat_cols, strategy='most_frequent' ) print("\n填充后的训练集:") print(X_train_filled) print("\n填充后的测试集:") print(X_test_filled) # 验证填充结果 print("\n验证填充结果:") for col in cat_cols: mode_val = X_train[col].mode()[0] print(f" {col}: 众数 = '{mode_val}'") print(f" 训练集缺失值: {X_train[col].isna().sum()} → 填充为 '{mode_val}'") print(f" 测试集缺失值: {X_test[col].isna().sum()} → 填充为 '{mode_val}'")

数据缺失填充策略

1. Mean(均值)

  • 操作:用该特征(列)的算术平均数来填充缺失值。

  • 适用场景:数据呈正态分布对称分布,且没有明显极端值时。

  • 优点:计算简单,能保持数据的整体均值不变。

  • 致命缺点极易受异常值影响。如果数据里有极端大或极端小的数,均值会被“拉偏”,填充出来的结果可能失真。

2. Median(中位数)

  • 操作:用该特征(列)的中位数(即按大小排序后位于中间的那个数)来填充缺失值。

  • 适用场景:数据分布明显偏斜(如收入数据,大部分人收入低,极少数人收入极高),或者存在异常值时。

  • 优点稳健性极强。因为只看位置,所以极端值的大小对中位数毫无影响,比均值更“抗造”。

3. Most_frequent(众数)

  • 操作:用该特征(列)中出现频率最高的那个值来填充缺失值。

  • 适用场景:专门用于分类(离散型)数据,比如性别(男/女)、颜色(红/蓝/绿)。

  • 注意:如果缺失值很多,或者众数占比不高,填充后可能会过度强化该类别,导致数据多样性降低。在连续型数据中基本不用这个方法。

4. Constant(常量)

  • 操作:用一个自定义的固定值(通常是“0”、“未知”、“-999”或“None”)来填充所有缺失值。

  • 适用场景

    • 业务上缺失就是有特殊含义的(比如“该用户未消费”,金额填0);

    • 作为机器学习中的占位符,告诉算法“这个值是人为补的,不代表真实水平”。

  • 缺点:如果随意填一个毫无意义的常数(比如用0填年龄),会严重干扰数据分析,必须谨慎使用。


简单粗暴的速记口诀

  • 数据像钟形曲线(对称)→ 用Mean

  • 数据有暴发户(极端值)→ 用Median

  • 数据是类别标签(男女/颜色)→ 用Most_frequent

  • 数据缺失本身就是信息(填无/填0)→ 用Constant