Python案例:如何用Scikit-learn做卡方选择?特征筛选实战指南
📚 目录导读
- 卡方选择是什么?为什么它重要?
- Scikit-learn中的
SelectKBest与chi2核心用法 - 从零开始的完整Python案例(鸢尾花数据集)
- 案例扩展:文本分类中的卡方选择
- 常见问题与调优技巧(Q&A)
- 最佳实践:如何结合卡方选择提升模型性能
卡方选择是什么?为什么它重要?
卡方检验(Chi-Square Test)是一种统计学方法,用于判断两个分类变量之间是否存在显著相关性,在机器学习特征工程中,卡方选择被用来筛选出与目标变量(类别)最相关的特征,剔除无关或弱相关的特征。

它的核心作用包括:
- 减少特征维度,防止过拟合
- 提升模型训练速度
- 保留最有解释力的特征,提高模型可解释性
适用场景:分类问题,且特征必须是离散型(非负整数),例如词频、标签计数等。
Scikit-learn中的SelectKBest与chi2核心用法
Sklearn提供了feature_selection模块,其中的SelectKBest与chi2配合,可以轻松实现卡方特征选择。
核心函数解析:
from sklearn.feature_selection import SelectKBest, chi2 selector = SelectKBest(score_func=chi2, k=5) # k: 选择前5个最佳特征 X_new = selector.fit_transform(X, y)
参数说明:
score_func: 评分函数,常用chi2、f_classif等k: 保留的特征数量(整数)或百分比(如k='all')fit_transform: 返回筛选后的特征矩阵
从零开始的完整Python案例(鸢尾花数据集)
下面用一个真实的分类案例演示完整流程,我们使用经典的鸢尾花(Iris)数据集,但注意鸢尾花特征为连续值,需要先进行离散化处理才能使用卡方检验。
步骤1:导入库与加载数据
import pandas as pd import numpy as np from sklearn.datasets import load_iris from sklearn.feature_selection import SelectKBest, chi2 from sklearn.preprocessing import KBinsDiscretizer from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score # 加载数据 iris = load_iris() X = iris.data y = iris.target feature_names = iris.feature_names
步骤2:连续特征离散化(必须步骤)
# 将连续特征分箱为3个区间(变成离散值) discretizer = KBinsDiscretizer(n_bins=3, encode='ordinal', strategy='uniform') X_discrete = discretizer.fit_transform(X) X_discrete = X_discrete.astype(int) # 转为整数
步骤3:卡方特征选择
# 选择k=2个最佳特征
selector = SelectKBest(score_func=chi2, k=2)
X_selected = selector.fit_transform(X_discrete, y)
# 查看每个特征的卡方得分和p值
scores = selector.scores_
p_values = selector.pvalues_
feature_scores = pd.DataFrame({
'特征': feature_names,
'卡方得分': scores,
'p值': p_values
}).sort_values('卡方得分', ascending=False)
print(feature_scores)
输出示例:
特征 卡方得分 p值
2 petal length (cm) 286.49 1.25e-62
3 petal width (cm) 279.50 1.08e-60
0 sepal length (cm) 91.26 2.08e-20
1 sepal width (cm) 38.44 4.87e-09
可见花瓣长度和宽度与类别相关性最强,萼片宽度最弱。
步骤4:对比模型性能
# 原始数据训练
X_train, X_test, y_train, y_test = train_test_split(X_discrete, y, test_size=0.3, random_state=42)
rf_orig = RandomForestClassifier(n_estimators=100).fit(X_train, y_train)
acc_orig = accuracy_score(y_test, rf_orig.predict(X_test))
# 筛选后训练
X_train_sel, X_test_sel, _, _ = train_test_split(X_selected, y, test_size=0.3, random_state=42)
rf_sel = RandomForestClassifier(n_estimators=100).fit(X_train_sel, y_train)
acc_sel = accuracy_score(y_test, rf_sel.predict(X_test_sel))
print(f"原始特征准确率: {acc_orig:.3f}")
print(f"卡方筛选后准确率: {acc_sel:.3f}")
通过这个对比,你可以清楚看到特征筛选对模型精度的影响。
案例扩展:文本分类中的卡方选择
在NLP中,卡方选择常用于特征词降维,例如对垃圾邮件分类:
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.datasets import fetch_20newsgroups
# 加载数据(示例使用二分类)
categories = ['rec.sport.baseball', 'sci.space']
data = fetch_20newsgroups(subset='train', categories=categories)
X_text = data.data
y_text = data.target
# 词频统计(得到离散特征)
vectorizer = CountVectorizer(max_features=5000) # 先限制词汇量
X_counts = vectorizer.fit_transform(X_text)
# 卡方选择1000个最佳词
selector_text = SelectKBest(chi2, k=1000)
X_sel_text = selector_text.fit_transform(X_counts, y_text)
# 查看最有区分力的词
feature_array = vectorizer.get_feature_names_out()
selected_indices = selector_text.get_support(indices=True)
selected_words = [feature_array[i] for i in selected_indices]
print("Top words:", selected_words[:20])
这能快速找出区分不同新闻组的关键词,如“baseball”、“space”等。
常见问题与调优技巧(Q&A)
❓ Q1:卡方选择只能用整数特征吗?
答:必须是非负数值(如0、1、2...)的离散特征,连续特征需要先离散化,如分箱、二值化,分类变量的值通常表示为计数或频率。
❓ Q2:卡方得分高就一定好?
答:不一定,卡方检验只衡量相关性,但特征之间可能存在冗余或过拟合风险,建议结合交叉验证选择最佳k值。
❓ Q3:如何确定保留多少特征(k值)?
答:常用方法有:
- 使用
SelectPercentile代替SelectKBest,按百分比选择 - 绘制特征得分曲线,选择拐点
- 用交叉验证遍历不同k值,选择最优准确率的k
# 示例:遍历k=1到8
from sklearn.model_selection import cross_val_score
for k in range(1, 9):
selector = SelectKBest(chi2, k=k)
X_k = selector.fit_transform(X_discrete, y)
scores = cross_val_score(RandomForestClassifier(), X_k, y, cv=5)
print(f"k={k}, 平均准确率={scores.mean():.3f}")
❓ Q4:我的特征是二值(0/1)可以用吗?
答:完全可以,二值化特征是卡方检验的典型输入,是否包含某关键词”。
❓ Q5:卡方选择和互信息(mutual_info_classif)有什么区别?
答:互信息能捕捉非线性关系,但计算成本较高;卡方检验假设特征之间独立,更适合大规模稀疏数据(如文本)。
最佳实践:如何结合卡方选择提升模型性能
- 数据预处理:确保特征为非负整数,必要时离散化
- 特征初筛:先用卡方选择快速剔除低分特征(如保留前10%)
- 交叉验证选k:找到保留特征数量的最优值
- 模型训练:在选定的特征空间上训练最终模型
- 结果解释:利用
get_support()保存选中特征,便于业务解读
进阶技巧:
- 组合管道:将卡方选择放入
Pipeline中,避免数据泄露:from sklearn.pipeline import Pipeline pipe = Pipeline([ ('discretizer', KBinsDiscretizer(n_bins=3, encode='ordinal')), ('selector', SelectKBest(chi2, k=2)), ('classifier', RandomForestClassifier()) ]) - 多模型对比:在使用卡方选择前后,分别训练SVM、Logistic回归、XGBoost,评估特征降维的普适性。
通过本篇文章的案例与问答,你应该能够独立使用Scikit-learn完成卡方特征选择任务,核心是记住:卡方选择只适合分类问题中的离散特征,而连续特征必须先转化为类别形式,动手实践是掌握它的最佳方式,建议立即用你手头的分类数据运行一遍上面的代码。