【特征选择】单变量特征选择
·
特征选择入门:单变量特征选择|原理+方法+Python实战全攻略
单变量特征选择是机器学习中最简单、最高效的特征筛选方法,核心逻辑是“逐个评估每个特征与目标变量的相关性”,筛选出对预测结果最有用的特征。它无需复杂模型,仅通过统计检验就能完成筛选,适合作为特征选择的“第一步”或“快速粗筛工具”。
一、单变量特征选择是什么?一句话看懂
单变量特征选择 = 对每个特征“单独打分”,只留分数高的特征。
核心逻辑:
- 不考虑特征之间的相互关系,只看“单个特征”与“目标变量”的关联强度
- 用统计方法给每个特征评一个“重要性分数”
- 按分数排序,筛选出Top K个或分数达标的特征
形象比喻:招聘筛选简历
目标变量是“是否录用”,每个特征是简历上的一项(学历、工作年限、技能证书等):
- 单变量特征选择就像HR逐个看每项指标:“学历是否达标?”“工作年限够不够?”
- 只保留“单项表现好”的指标(特征),忽略单项表现差的,最终形成“合格简历清单”(筛选后的特征集)
二、3种核心方法(必学)
根据特征类型(分类/连续)和任务类型(分类/回归),选择对应的统计方法:
1. 卡方检验(Chi-square Test)
适用场景
- 特征是分类变量(如性别:男/女、学历:本科/硕士)
- 目标变量是分类变量(如是否购买:是/否、疾病类型:A/B/C)
核心原理
检验“特征类别”与“目标类别”是否独立:
- 假设特征与目标独立(无关联),计算“实际观测频数”与“理论期望频数”的差异
- 差异越大(卡方统计量越大),说明特征与目标关联越强
公式(简单理解)
χ2=∑(Oij−Eij)2Eij \chi^2 = \sum \frac{(O_{ij} - E_{ij})^2}{E_{ij}} χ2=∑Eij(Oij−Eij)2
- OijO_{ij}Oij:实际观测频数(如“男性+购买”的人数)
- EijE_{ij}Eij:理论期望频数(假设独立时,“男性+购买”的预期人数),计算方式:
Eij=行合计×列合计总样本数E_{ij} = \frac{行合计 \times 列合计}{总样本数}Eij=总样本数行合计×列合计
选择规则
- 卡方统计量越大 → 特征越重要
- p值 < 0.05 → 特征与目标显著相关,保留;反之剔除
2. F检验(F-test/ANOVA)
适用场景
- 特征是连续变量(如收入、年龄、成绩)
- 目标变量是分类变量(如客户等级:高/中/低、疾病是否发生:是/否)
核心原理
通过方差分析判断“不同类别下,特征的均值是否有显著差异”:
- 组间方差:不同类别间的特征均值差异(如高、中、低客户的收入均值差异)
- 组内方差:同一类别内的特征波动(如高客户内部的收入波动)
- F值 = 组间方差 / 组内方差 → F值越大,说明类别间差异越显著,特征越重要
公式(简单理解)
F=组间方差组内方差 F = \frac{组间方差}{组内方差} F=组内方差组间方差
- 组间方差:衡量类别间差异,∑i=1kni(Xˉi−Xˉ)2k−1\frac{\sum_{i=1}^k n_i (\bar{X}_i - \bar{X})^2}{k-1}k−1∑i=1kni(Xˉi−Xˉ)2(k=类别数,nin_ini=第i类样本数)
- 组内方差:衡量类别内波动,∑i=1k∑j=1ni(Xij−Xˉi)2N−k\frac{\sum_{i=1}^k \sum_{j=1}^{n_i} (X_{ij} - \bar{X}_i)^2}{N-k}N−k∑i=1k∑j=1ni(Xij−Xˉi)2(N=总样本数)
选择规则
- F值越大 → 特征越重要
- p值 < 0.05 → 特征与目标显著相关,保留;反之剔除
3. 互信息(Mutual Information)
适用场景
- 特征和目标变量不限类型(分类/连续均可)
- 存在非线性关系(如特征与目标是U型、对数关系)
核心原理
衡量两个变量之间的“信息共享程度”:
- 互信息越大,说明特征包含的“关于目标的信息”越多,关联越强
- 不依赖线性假设,能捕捉复杂非线性关系
公式(简单理解)
I(X;Y)=∑x∈X∑y∈Yp(x,y)logp(x,y)p(x)p(y) I(X;Y) = \sum_{x \in X} \sum_{y \in Y} p(x,y) \log \frac{p(x,y)}{p(x)p(y)} I(X;Y)=x∈X∑y∈Y∑p(x,y)logp(x)p(y)p(x,y)
- p(x,y)p(x,y)p(x,y):特征X和目标Y的联合概率(同时取x和y的概率)
- p(x)p(x)p(x)、p(y)p(y)p(y):特征X和目标Y的边缘概率(各自取某值的概率)
选择规则
- 互信息值越大 → 特征越重要,按值排序选择Top K个
方法选择速查表(直接照选)
| 特征类型 | 目标类型 | 推荐方法 |
|---|---|---|
| 分类特征 | 分类目标 | 卡方检验 |
| 连续特征 | 分类目标 | F检验(ANOVA) |
| 任意特征 | 任意目标(含非线性) | 互信息 |
| 连续特征 | 回归目标 | 皮尔逊相关系数(单变量扩展) |
三、单变量特征选择完整流程(必背)
- 数据预处理:
- 区分特征类型(分类/连续)和目标类型(分类/回归)
- 处理缺失值(填充/删除)
- 连续特征标准化(F检验、互信息等方法对尺度敏感)
- 选择统计方法:按上述速查表选择卡方检验、F检验或互信息
- 计算特征分数:对每个特征单独计算统计量(卡方值、F值、互信息值)和p值
- 筛选特征:
- 按分数排序,选择Top K个(如Top 10)
- 按p值筛选,保留p < 0.05的特征(卡方/F检验)
- 模型训练与评估:用筛选后的特征训练模型,对比筛选前后的性能
四、Python 完整实战:葡萄酒分类特征选择
1. 导入库+加载数据
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectKBest, f_classif, chi2, mutual_info_classif
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
# 加载葡萄酒数据集(13个特征,3类目标)
wine = load_wine()
X = pd.DataFrame(wine.data, columns=wine.feature_names)
y = pd.Series(wine.target, name='wine_type')
print("样本数:", X.shape[0])
print("特征数:", X.shape[1])
print("特征名:", list(X.columns))
print("目标类别:", list(wine.target_names))
X.head()
2. 数据预处理(拆分+标准化)
# 1. 拆分训练集/测试集(7:3)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
# 2. 标准化连续特征(F检验、互信息需标准化)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# 转换为DataFrame(方便后续查看)
X_train_scaled = pd.DataFrame(X_train_scaled, columns=X.columns)
X_test_scaled = pd.DataFrame(X_test_scaled, columns=X.columns)
3. 方法1:F检验(连续特征+分类目标)
# 1. 初始化选择器:选择Top 10个最重要特征
selector_f = SelectKBest(score_func=f_classif, k=10)
X_train_f = selector_f.fit_transform(X_train_scaled, y_train)
X_test_f = selector_f.transform(X_test_scaled)
# 2. 查看结果
selected_features_f = X.columns[selector_f.get_support()]
feature_scores_f = pd.Series(selector_f.scores_[selector_f.get_support()], index=selected_features_f)
feature_pvalues_f = pd.Series(selector_f.pvalues_[selector_f.get_support()], index=selected_features_f)
print("=== F检验筛选的特征 ===")
print("选择的特征:", list(selected_features_f))
print("\n特征分数(F值):")
print(feature_scores_f.sort_values(ascending=False))
print("\n特征p值:")
print(feature_pvalues_f.sort_values(ascending=True))
4. 方法2:互信息(捕捉非线性关系)
# 1. 初始化选择器:选择Top 10个最重要特征
selector_mi = SelectKBest(score_func=mutual_info_classif, k=10)
X_train_mi = selector_mi.fit_transform(X_train_scaled, y_train)
X_test_mi = selector_mi.transform(X_test_scaled)
# 2. 查看结果
selected_features_mi = X.columns[selector_mi.get_support()]
feature_scores_mi = pd.Series(selector_mi.scores_[selector_mi.get_support()], index=selected_features_mi)
print("\n=== 互信息筛选的特征 ===")
print("选择的特征:", list(selected_features_mi))
print("\n特征互信息值:")
print(feature_scores_mi.sort_values(ascending=False))
5. 特征分数可视化
# 可视化F检验特征分数
plt.figure(figsize=(12, 6))
feature_scores_f.sort_values().plot(kind='barh', color='skyblue')
plt.title("F检验特征重要性分数", fontsize=12)
plt.xlabel("F值(越大越重要)")
plt.grid(alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
# 可视化互信息特征分数
plt.figure(figsize=(12, 6))
feature_scores_mi.sort_values().plot(kind='barh', color='lightcoral')
plt.title("互信息特征重要性分数", fontsize=12)
plt.xlabel("互信息值(越大越重要)")
plt.grid(alpha=0.3, axis='x')
plt.tight_layout()
plt.show()
6. 模型训练与性能对比
# 定义训练评估函数
def train_evaluate(X_train, X_test, y_train, y_test, title):
model = LogisticRegression(max_iter=1000, random_state=42)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"\n=== {title} ===")
print(f"准确率: {acc:.4f}")
print("分类报告:")
print(classification_report(y_test, y_pred, target_names=wine.target_names))
return acc
# 1. 原始特征(13个)训练
acc_original = train_evaluate(X_train_scaled, X_test_scaled, y_train, y_test, "原始特征模型")
# 2. F检验筛选后(10个)训练
acc_f = train_evaluate(X_train_f, X_test_f, y_train, y_test, "F检验筛选后模型")
# 3. 互信息筛选后(10个)训练
acc_mi = train_evaluate(X_train_mi, X_test_mi, y_train, y_test, "互信息筛选后模型")
# 对比结果
print("\n=== 性能对比 ===")
print(f"原始特征准确率: {acc_original:.4f}")
print(f"F检验筛选准确率: {acc_f:.4f}")
print(f"互信息筛选准确率: {acc_mi:.4f}")
7. 优化:选择最优特征数量K
# 测试不同K值(5-13)的性能
k_values = list(range(5, X.shape[1]+1))
acc_scores = []
for k in k_values:
# F检验选择K个特征
selector = SelectKBest(score_func=f_classif, k=k)
X_train_k = selector.fit_transform(X_train_scaled, y_train)
X_test_k = selector.transform(X_test_scaled)
# 训练模型
model = LogisticRegression(max_iter=1000, random_state=42)
model.fit(X_train_k, y_train)
acc = accuracy_score(y_test, model.predict(X_test_k))
acc_scores.append(acc)
# 可视化K值与准确率关系
plt.figure(figsize=(10, 6))
plt.plot(k_values, acc_scores, marker='o', color='green', linewidth=2)
plt.title("特征数量K与模型准确率关系", fontsize=12)
plt.xlabel("选择的特征数量K")
plt.ylabel("测试集准确率")
plt.xticks(k_values)
plt.grid(alpha=0.3)
plt.tight_layout()
plt.show()
# 最优K值
best_k = k_values[np.argmax(acc_scores)]
best_acc = max(acc_scores)
print(f"\n最优特征数量K: {best_k}")
print(f"最优准确率: {best_acc:.4f}")
8. 混淆矩阵可视化(最优模型)
# 用最优K值训练模型
selector_best = SelectKBest(score_func=f_classif, k=best_k)
X_train_best = selector_best.fit_transform(X_train_scaled, y_train)
X_test_best = selector_best.transform(X_test_scaled)
model_best = LogisticRegression(max_iter=1000, random_state=42)
model_best.fit(X_train_best, y_train)
y_pred_best = model_best.predict(X_test_best)
# 绘制混淆矩阵
cm = confusion_matrix(y_test, y_pred_best)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
xticklabels=wine.target_names, yticklabels=wine.target_names)
plt.title(f"最优模型混淆矩阵(K={best_k})", fontsize=12)
plt.xlabel("预测类别")
plt.ylabel("真实类别")
plt.tight_layout()
plt.show()
五、单变量特征选择的优缺点(必背)
优点
- 简单易懂:原理基于基础统计检验,本科生也能快速理解
- 计算高效:逐个评估特征,无复杂迭代,适合中小型数据集
- 适用性广:支持分类/回归任务,可搭配卡方、F检验、互信息等多种方法
- 快速粗筛:能快速剔除完全无关的特征,减少后续建模复杂度
- 无模型依赖:不依赖复杂模型,仅通过统计量就能筛选,结果稳定
缺点
- 忽略特征交互:独立评估每个特征,无法捕捉“特征A+特征B”的协同作用(如“高学历+多年经验”才重要)
- 无法处理多重共线性:若两个特征高度相关且都重要,可能误删其中一个
- 线性假设局限:卡方检验、F检验依赖线性关系,对非线性关系捕捉能力弱(需用互信息弥补)
- 可能丢失关键信息:过度依赖单个特征的相关性,可能遗漏“单独看不重要但组合重要”的特征
六、与其他特征选择方法对比
| 方法 | 核心逻辑 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 单变量特征选择 | 逐个评估特征与目标的相关性 | 简单、快速、无模型依赖 | 忽略特征交互、不处理共线性 | 快速粗筛、特征独立、中小型数据 |
| L1正则化(Lasso) | 惩罚系数绝对值,使部分系数为0 | 嵌入建模、处理高维数据 | 对共线性敏感、随机删除相关特征 | 高维数据、稀疏建模 |
| 递归特征消除(RFE) | 训练模型逐步删除最不重要特征 | 考虑特征交互、精度高 | 计算量大、依赖模型 | 特征交互强、对精度要求高 |
| 树模型特征重要性(RF/XGBoost) | 基于节点分裂增益评估重要性 | 捕捉非线性、抗共线性 | 计算量大、可能过拟合 | 非线性数据、复杂特征关系 |
七、适用场景与替代方案
优先用单变量特征选择的情况
- 数据预处理阶段快速粗筛(剔除完全无关特征)
- 特征数量较多(几十到几百个)且特征间独立
- 任务是线性关系为主(如逻辑回归、线性回归)
- 对计算效率要求高,无需复杂建模
考虑其他方法的情况
- 特征间存在强交互作用(如“年龄+收入”共同影响消费):用RFE或树模型
- 特征高度相关(多重共线性):用L2正则化或ElasticNet
- 数据存在复杂非线性关系:用互信息或树模型
- 高维数据(上千个特征):用L1正则化或树模型
八、最简单总结(背诵版)
- 单变量特征选择 = 逐个给特征打分,只留高分特征
- 核心方法:分类特征用卡方检验,连续特征用F检验,非线性用互信息
- 优点:简单、快速、高效,适合快速粗筛
- 缺点:忽略特征交互,不处理共线性
- 最佳用法:作为特征选择的第一步,后续用更复杂方法精筛
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)