一、相关性分析(Correlation):快速算出特征之间的“关系矩阵”
1. 场景:为什么要算相关系数?
在特征工程阶段,我们经常会问:
- 哪些特征之间高度相关,可能存在冗余?
- 某个特征和目标变量之间相关性强不强?
- 是否有一些线性关系比较明显的特征,可以重点关注?
这时最常用的工具就是相关系数,比如:
- Pearson 相关系数:衡量线性相关性
- Spearman 相关系数:基于排序的相关性,更适合非线性、单调关系
在 Spark MLlib 中,Correlation可以直接对向量列(Vector column)计算相关性矩阵。
2. API 介绍:Correlation.corr
在 PySpark 中:
frompyspark.ml.linalgimportVectorsfrompyspark.ml.statimportCorrelation data=[(Vectors.sparse(4,[(0,1.0),(3,-2.0)]),),(Vectors.dense([4.0,5.0,0.0,3.0]),),(Vectors.dense([6.0,7.0,0.0,8.0]),),(Vectors.sparse(4,[(0,9.0),(3,1.0)]),)]df=spark.createDataFrame(data,["features"])# 默认方法是 Pearsonr1=Correlation.corr(df,"features").head()print("Pearson correlation matrix:\n"+str(r1[0]))# Spearman 相关系数r2=Correlation.corr(df,"features","spearman").head()print("Spearman correlation matrix:\n"+str(r2[0]))几个要点:
- 输入 DataFrame 中有一列向量列,比如
"features" Correlation.corr(df, "features", method)会返回一个只有一行一列的 DataFrame,这一列就是相关性矩阵head()[0]拿到的是一个DenseMatrix,可以转成字符串打印
3. Pearson vs Spearman:怎么选?
Pearson
- 假设变量之间是线性关系
- 对异常值较为敏感
- 是最常用、最经典的相关系数
Spearman
- 把数值转成秩(rank),再算 Pearson
- 不要求线性,只要是单调关系就能体现
- 对异常值更鲁棒
实践建议:
- 做连续特征线性关系分析:优先用 Pearson
- 数据有明显非线性趋势、或者有很多异常点:可以尝试 Spearman
4. 典型用法:特征筛选 & 多重共线性检查
你可以对特征列构建一个大的
features向量,然后用Correlation.corr算出相关系数矩阵:- 高度相关(例如 |ρ| > 0.9)的特征可以考虑做降维或只保留一个
某些特征与标签的相关性太弱:可以考虑剔除或降低权重(当然不能只看相关系数,还是要配合业务理解)
二、假设检验与卡方检验(ChiSquareTest):离散特征和标签到底有没有关系?
1. 核心问题:这个特征和标签真有关系吗?
在分类问题中,我们经常有离散特征 / 类别型特征,例如:
- 用户职业(学生 / 上班族 / 自由职业)
- 设备类型(iOS / Android / Web)
- 地区(省份 / 城市)
我们想知道的问题通常是:
这个离散特征和 “是否点击 / 是否购买 / 是否违约” 等标签,
是否存在统计意义上的相关性?
这时就轮到卡方检验(Chi-square test)出场了。
2. MLlib 中的 ChiSquareTest:特征 vs 标签 独立性检验
spark.ml.stat.ChiSquareTest提供了对每一维特征与标签之间做Pearson 卡方独立性检验的功能:
- 对每个特征维度,构造一个列联表(contingency table)
- 计算卡方统计量(χ²)、自由度和 p-value
- 判断是否拒绝 “特征和标签相互独立” 这个零假设
👉前提:特征和标签都必须是离散 / 类别值。
3. Python 示例
frompyspark.ml.linalgimportVectorsfrompyspark.ml.statimportChiSquareTest data=[(0.0,Vectors.dense(0.5,10.0)),(0.0,Vectors.dense(1.5,20.0)),(1.0,Vectors.dense(1.5,30.0)),(0.0,Vectors.dense(3.5,30.0)),(0.0,Vectors.dense(3.5,40.0)),(1.0,Vectors.dense(3.5,40.0))]df=spark.createDataFrame(data,["label","features"])r=ChiSquareTest.test(df,"features","label").head()print("pValues: "+str(r.pValues))print("degreesOfFreedom: "+str(r.degreesOfFreedom))print("statistics: "+str(r.statistics))ChiSquareTest.test(df, "features", "label")返回一个 DataFrame,每一行对应一次测试结果.head()拿到第一行结果r.pValues:每一个特征维度对应一个 p-valuer.degreesOfFreedom:每个特征对应的自由度r.statistics:对应的卡方统计量
4. 怎么解读结果?
- 原假设 H₀:特征和标签相互独立
- 备择假设 H₁:特征和标签不独立(存在相关性)
通常我们会设定一个显著性水平,比如 α = 0.05:
如果
pValue < 0.05:- 拒绝独立性假设
- 说明该特征与标签之间有显著的统计相关性,可以认为是“有用”的
如果
pValue ≥ 0.05:- 不能拒绝独立性假设
- 说明没观察到足够强的统计证据证明它和标签有关
5. 实战中的典型用途
离散特征的筛选:
- 对每个类别型特征做卡方检验
- 剔除与标签“几乎无关”的特征,减轻模型负担
对特征工程结果做验证:
- 比如你做了某些桶化 / 分箱操作,可以用卡方检验看看新特征是否和标签有更强的统计相关性
模型解释:
- 给业务方输出“哪些类别特征和目标最相关”的证据时,卡方检验是一个很好的工具
三、Summarizer:一行代码搞定向量列的均值、方差、非零比例……
1. 问题:如何快速看一列向量特征的整体分布?
假设你的 DataFrame 里有一列是向量特征"features":
每一行是一个样本的特征向量
你想知道每一维特征的:
- 均值(mean)
- 方差(variance)
- 最大值 / 最小值(max/min)
- 非零元素个数(numNonZeros)
- 总计数(count)等
这时就可以用Summarizer。
2. API 介绍:Summarizer.metrics
示例代码:
frompyspark.ml.statimportSummarizerfrompyspark.sqlimportRowfrompyspark.ml.linalgimportVectors df=sc.parallelize([Row(weight=1.0,features=Vectors.dense(1.0,1.0,1.0)),Row(weight=0.0,features=Vectors.dense(1.0,2.0,3.0))]).toDF()# 1. 创建一个 summarizer,指定要计算的指标summarizer=Summarizer.metrics("mean","count")# 2. 计算带权重的统计指标df.select(summarizer.summary(df.features,df.weight)).show(truncate=False)# 3. 计算不带权重的统计指标df.select(summarizer.summary(df.features)).show(truncate=False)# 4. 单独计算 "mean" 带权重df.select(Summarizer.mean(df.features,df.weight)).show(truncate=False)# 5. 单独计算 "mean" 不带权重df.select(Summarizer.mean(df.features)).show(truncate=False)几个关键点:
Summarizer.metrics("mean", "count", "variance", ...)用来指定要计算的指标summarizer.summary(df.features, df.weight):- 第一个参数是向量列
- 第二个参数是权重列(可选)
如果不传权重列,则表示所有样本权重相同
也可以直接用
Summarizer.mean(...)、Summarizer.variance(...)这种“快捷方法”
3. 权重的意义
权重在很多场景下会很有用,比如:
- 你的数据是预聚合结果(比如某个样本出现了 n 次)
- 某些样本在建模中更重要(例如高价值用户)
- 用采样的数据估计总体统计量时,需要做加权修正
有了weight列,可以很方便地得到加权均值、加权方差等。
4. Summarizer 支持的指标
官方支持的指标包括:
mean:均值variance:方差std:标准差min/max:按列最小值 / 最大值sum:按列求和numNonZeros:按列非零元素个数count:总样本数
这些统计量基本覆盖了 EDA(探索性数据分析)中最常用的一批指标。
四、把基础统计能力融入你的 ML Pipeline
虽然Correlation、ChiSquareTest和Summarizer看起来“只是统计工具”,
但在实际的 ML 项目中,它们可以自然地融入到你的整体流程中:
数据理解阶段
用 Summarizer 看每一维特征的均值/方差/非零比例,识别:
- 常量特征(variance=0)
- 极端稀疏或极端密集的特征
用 Correlation 看特征之间的线性相关性,初步感知特征结构
特征筛选阶段
- 对离散特征用 ChiSquareTest 筛掉“和标签没有关系”的变量
- 对连续特征,用相关系数 + 业务理解做初步筛选和降维
模型调试与解释阶段
- 用 Summarizer 跟踪不同阶段特征处理后的数据分布是否合理
- 用 Correlation 和 ChiSquareTest 为“为什么选这些特征”提供统计证据
从工程实践角度看,这些基础统计模块是 MLlib 中非常值得熟练掌握的一块——
它们不是“锦上添花”,而是很多实战问题的“起手式”。
五、总结
Spark MLlib 提供的基础统计工具大致可以归纳为三类:
Correlation(相关性分析)
- 支持 Pearson / Spearman
- 面向向量列,计算相关性矩阵
- 适用于特征筛选、多重共线性检查、数据理解等场景
ChiSquareTest(卡方独立性检验)
- 针对离散特征与标签的独立性检验
- 输出 p-value、自由度、统计量
- 常用于类别特征的筛选和假设验证
Summarizer(向量列汇总统计)
- 提供均值、方差、标准差、max/min、sum、非零数、count 等指标
- 支持权重,适合加权统计场景
- 是 EDA 与特征监控的基础工具
如果你已经在用 Spark 做机器学习,不妨把spark.ml.stat这几个组件视为自己的“统计瑞士军刀”,
在建模前后多用一用,很多肉眼看不出的数据问题,都能靠这些基本统计手段提前暴露出来。