Python案例如何用Scikit-learn做卡方选择

wen python案例 2

Python案例:如何用Scikit-learn做卡方选择?特征筛选实战指南

📚 目录导读

  1. 卡方选择是什么?为什么它重要?
  2. Scikit-learn中的SelectKBestchi2核心用法
  3. 从零开始的完整Python案例(鸢尾花数据集)
  4. 案例扩展:文本分类中的卡方选择
  5. 常见问题与调优技巧(Q&A)
  6. 最佳实践:如何结合卡方选择提升模型性能

卡方选择是什么?为什么它重要?

卡方检验(Chi-Square Test)是一种统计学方法,用于判断两个分类变量之间是否存在显著相关性,在机器学习特征工程中,卡方选择被用来筛选出与目标变量(类别)最相关的特征,剔除无关或弱相关的特征。

Python案例如何用Scikit-learn做卡方选择

它的核心作用包括:

  • 减少特征维度,防止过拟合
  • 提升模型训练速度
  • 保留最有解释力的特征,提高模型可解释性

适用场景:分类问题,且特征必须是离散型(非负整数),例如词频、标签计数等。


Scikit-learn中的SelectKBestchi2核心用法

Sklearn提供了feature_selection模块,其中的SelectKBestchi2配合,可以轻松实现卡方特征选择。

核心函数解析:

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: 评分函数,常用chi2f_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)有什么区别?

:互信息能捕捉非线性关系,但计算成本较高;卡方检验假设特征之间独立,更适合大规模稀疏数据(如文本)。


最佳实践:如何结合卡方选择提升模型性能

  1. 数据预处理:确保特征为非负整数,必要时离散化
  2. 特征初筛:先用卡方选择快速剔除低分特征(如保留前10%)
  3. 交叉验证选k:找到保留特征数量的最优值
  4. 模型训练:在选定的特征空间上训练最终模型
  5. 结果解释:利用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完成卡方特征选择任务,核心是记住:卡方选择只适合分类问题中的离散特征,而连续特征必须先转化为类别形式,动手实践是掌握它的最佳方式,建议立即用你手头的分类数据运行一遍上面的代码。

抱歉,评论功能暂时关闭!