一、相关性分析(Correlation):快速算出特征之间的"关系矩阵"
1. 场景:为什么要算相关系数?
在特征工程阶段,我们经常会问:
- 哪些特征之间高度相关,可能存在冗余?
- 某个特征和目标变量之间相关性强不强?
- 是否有一些线性关系比较明显的特征,可以重点关注?
这时最常用的工具就是 相关系数,比如:
- Pearson 相关系数:衡量线性相关性
- Spearman 相关系数:基于排序的相关性,更适合非线性、单调关系
在 Spark MLlib 中,Correlation 可以直接对 向量列(Vector column) 计算相关性矩阵。
2. API 介绍:Correlation.corr
在 PySpark 中:
python
from pyspark.ml.linalg import Vectors
from pyspark.ml.stat import Correlation
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"])
# 默认方法是 Pearson
r1 = 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 示例
python
from pyspark.ml.linalg import Vectors
from pyspark.ml.stat import ChiSquareTest
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
示例代码:
python
from pyspark.ml.stat import Summarizer
from pyspark.sql import Row
from pyspark.ml.linalg import Vectors
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 这几个组件视为自己的"统计瑞士军刀",
在建模前后多用一用,很多肉眼看不出的数据问题,都能靠这些基本统计手段提前暴露出来。