第五章 经典 CCA 的实操实现
本章要点
- 一套贯通思路:同一份数据、同一套检验逻辑,在 SPSS / R / Python 三平台跑通
- 三份对照代码:菜单路径、包函数、类接口的"翻译对照表"
- 四个输出模块:典型相关系数、显著性检验、权重系数、载荷矩阵
- 一条验证底线:三大平台的典型相关数值必须一致,符号约定必须对齐
- 一个自动化工序:把分析写成可复现脚本,而不是手工菜单点击记录
前四章完成了从数学到检验的全部理论装备,本章把它们落到键盘上。全章使用同一份示范数据——"生理指标组对体测成绩组"的简化设定(16 名运动员的 3 项生理指标:肺活量、最大摄氧量、体脂率;2 项体测成绩:百米用时、耐力跑用时),样本量小仅用于演示流程,真实分析请按第三章准则放大样本。三平台的流程骨架完全同构,如图 5-1 所示。
图 5-1 三平台共用的 CCA 流程总体结构(图注:本图给出平台无关的分析流水线,蓝色结构节点按数据加工次序排列。请重点看三个关键节点:数据读入与标准化是各平台差异最大的环节,也是出错高发区;典型相关求解之后的三步(检验、载荷、导出)三平台几乎一一对应;结果导出环节的分叉最大——SPSS 输出在查看器里、R 输出在对象里、Python 输出在属性里,理解各自的"出口"是本章实操的关键。)
5.1 SPSS 实现
5.1.1 菜单操作路径(分析 → 相关 → 典型相关性)
SPSS 的经典 CCA 主路径为:分析 → 相关 → 典型相关性,弹出对话框后:把生理指标组的三个变量放入"集合 1(X 组)“,体测成绩组放入"集合 2(Y 组)”,点击确定执行。若菜单中没有"典型相关性"项(旧版本或精简安装),两条等价替代路径可任选其一:
- 语法路线:调用官方宏
CANCORR——在语法窗口运行INCLUDE '...\Canonical correlation.sps'. CANCORR SET1 = 肺活量 最大摄氧量 体脂率 / SET2 = 百米用时 耐力跑用时 .(宏文件路径随安装目录而定); - MANOVA 路线:在旧版中通过
MANOVA 百米用时 耐力跑用时 WITH 肺活量 最大摄氧量 体脂率 / DISCRIMINANT STAN CORR ALPHA(1) .获得等价的典型相关输出。
三条路径的数值结果一致,区别只在输出组织方式与是否顺带给出交叉载荷——建议新用户直接用主菜单路径,老数据文件则用语法路径保证可复现。菜单与语法的入口分工如图 5-2 所示。
图 5-2 SPSS 双入口的接口对接(图注:本图展示菜单与语法两条入口到输出查看器的信号流向,蓝色是输入接口,紫色是输出端。请重点看三个关键节点:两条入口共享同一套数据与同一计算内核,输出文件在查看器中汇总;菜单路径适合探索与教学,语法路径适合批量与复现;把菜单操作粘贴回语法窗口(点击"粘贴"按钮),是 SPSS 用户从交互走向可复现的最低成本过渡——本节的语法示例全部以该方式获得。)
5.1.2 变量分组与参数设置
执行前需要确认四个设置点:变量角色——被解释方向不预设,X 组/Y 组仅表示配对结构,不表达因果;缺失值策略——对话框默认逐对删除(pairwise),需要与第三章的缺失机制判断对齐;标准化——SPSS 的典型相关过程基于相关矩阵还是协方差矩阵由版本默认决定,建议在语法中显式要求标准化后再分析(对原始变量先执行"描述统计 → 描述 → 另存为标准化值"),保证输出可直接读权重;统计输出项——选择输出相关系数、载荷(structure coefficients)与交叉载荷(cross-loadings),这是第六章解读的原材料。参数语义的对应关系随平台而变,完整的对照表在 5.4.3 节给出。
5.1.3 输出结果解读(典型相关系数表 / 标准化系数表 / 载荷矩阵)
SPSS 输出的核心表组共五张,对应第 4、6 章的全部概念:
- 典型相关系数表:列出 r1,r2r_1,r_2r1,r2(本例 min(p,q)=2\min(p,q)=2min(p,q)=2),通常附带特征值 λi=ri2/(1−ri2)\lambda_i=r_i^2/(1-r_i^2)λi=ri2/(1−ri2) 与 Wilks’ Lambda 的逐级检验列;
- 显著性检验表:给出 Λ\LambdaΛ、Bartlett 近似的 χ2\chi^2χ2 值、自由度与 ppp 值——整体检验 p<0.05p<0.05p<0.05 才进入下一步解读(4.1 节);
- 标准化系数表:两侧各自的权重向量 a\boldsymbol{a}a、b\boldsymbol{b}b(标准化数据上),各列对应 U1U_1U1、U2U_2U2 与 V1V_1V1、V2V_2V2;
- 载荷矩阵表:原始变量与典型变量的相关(structure coefficients),以及交叉载荷(原始变量与对侧典型变量的相关);
- 冗余度表:两侧方向的方差解释比例与冗余指数(4.3 节)。
解读顺序建议严格按"先检验、再载荷、后权重"推进:ppp 值不显著就停止;显著后先看载荷识别"每个典型变量实质携带哪些变量"(载荷绝对值 > 0.5 为显性成员,6.3.3 节),再看权重确认合成公式——先载荷后权重的次序能避开共线环境下的权重误读。
5.1.4 SPSS 结果导出与三线表制作
SPSS 原生输出是图文混排的查看器格式,不适合直接进论文。导出流程分三步:其一,在查看器里对目标表格右键"复制"或以 .spv 文件保存;其二,把数值粘贴到 Excel/WPS 中,按中文期刊三线表规范重新排版——顶线粗、栏目线细、底线粗,无线条封口与竖线;其三,把显著性符号(∗^*∗、∗∗^{**}∗∗)与检验统计量标注为表注放在表下方。为降低手工出错率,建议在语法中直接输出到 Excel(OUTPUT MODIFY 与 OMS 子系统可将指定表块导出为 .xls),再以脚本统一格式化——第 6.5.1 节给出三线表的完整排版规范与模板。
认知检查点: SPSS 的实操心法是"菜单探索、语法固化":先点菜单看结果形态,再把关键操作粘贴为语法存档——输出查看器里的每一张表都能在语法中定位到对应命令,分析报告的可复现性由此而来。
5.2 R 语言实现
5.2.1 基础包 stats::cancor 的使用
R 基础包 stats 自带的 cancor 函数是零依赖的最简入口:
# 数据:x 为生理指标组数据框(3 列),y 为体测成绩组数据框(2 列)
res <- cancor(x = as.matrix(physio), y = as.matrix(score))
res$cor # 典型相关系数 r1, r2
res$xcoef # X 组权重矩阵(每列一个典型方向)
res$ycoef # Y 组权重矩阵
res$xcenter; res$ycenter # 中心化偏移(用于新样本打分)
cancor 的数值底层正是 2.3.4 节的相干矩阵 SVD 路径:对标准化后的数据做 QR 分解、再对相干矩阵取奇异值,因此它不需要显式求逆,数值稳健。它的主要短板是不输出显著性检验、载荷矩阵与冗余度——需要后续手工补充,因此在教学与快速检查中好用,论文级分析推荐 5.2.2 的 CCA 包。
5.2.2 CCA 包的 cc() 函数完整流程
CCA 包(González 等)的 cc 函数把检验、载荷与冗余一次配齐,是 R 端经典 CCA 的主力接口。完整流程:
library(CCA)
res <- cc(X = physio, Y = score) # 返回典型相关、权重与载荷
# 显著性检验(整体 + 逐级):借助 CCP 包
library(CCP)
p_asym <- p.asym(rho = res$cor, N = nrow(physio), p = 3, q = 2, tstat = "Wilks")
p_asym$p.value # 各"至少保留前 k 对"的 p 值序列
# 冗余分析
r1 <- comput(res, X = physio, Y = score) # 载荷与交叉载荷
rd <- r1$Rdx2 %*% (res$cor^2); rd # 冗余指数(X 对 Y 方向)
解读输出时注意 cc 返回的 xcoef/ycoef 默认针对原始(未标准化)数据,comput 给出的载荷基于标准化相关;若需要标准化权重,先对两数据框做 scale() 再调用 cc。各对象在内存中的对应关系是本章"结果出口"的核心:res$cor 是系数表、p_asym$p.value 是检验表、r1 是载荷表,三个对象拼起来就是 SPSS 五张表的内容。
5.2.3 自定义显著性检验与冗余分析代码
不依赖 CCP 时,Bartlett 检验与冗余指数各用十余行即可手写,既加深理解也便于移植:
wilks_seq <- function(rho, n, p, q) {
m <- length(rho)
out <- data.frame(k = 0:(m - 1), Lambda = NA, chisq = NA, df = NA, p = NA)
for (k in 0:(m - 1)) { # 从全对开始逐级剔除
Lam <- prod(1 - rho[(k + 1):m]^2) # 剩余对的乘积
df <- (p - k) * (q - k)
chisq <- -(n - 1 - (p + q + 1) / 2) * log(Lam)
out[k + 1, -1] <- c(Lam, chisq, df, pchisq(chisq, df, lower.tail = FALSE))
}
out # 对应执行快照:逐行给出逐步检验表
}
该函数的实现与 4.1.3、4.2.1 的公式一一对应——循环变量 k 每推进一档,检验对象就从"全体相关"剔除当前最大的那一对,这与图 4-5 的状态机逐状态同步,代码中的每次循环即图中的一个"剔除最大对"转移。
5.2.4 可视化:典型载荷图 / 双标图(Biplot)
解读辅助图建议两张。其一,载荷双线图:用 matplot 分别绘制 X 组、Y 组的载荷向量随典型轴变化的折线,直观显示"哪个变量在哪一对上贡献大";其二,典型得分双标图:
U <- as.matrix(scale(physio)) %*% res$xcoef # 样本的 U1, U2 得分
V <- as.matrix(scale(score)) %*% res$ycoef # 样本的 V1, V2 得分
plot(U[, 1], V[, 1], xlab = "U1", ylab = "V1") # 每点一个样本
abline(lm(V[, 1] ~ U[, 1])) # 回归线斜率即相关结构
散点围绕对角线方向的伸展程度直观呈现第一典型相关的大小;若有颜色可分组的类别变量(如训练组别),按组着色还能顺带检查关联结构是否由某子群主导。
5.3 Python 实现
5.3.1 scikit-learn 的 CCA 类(sklearn.cross_decomposition.CCA)
Python 生态的主入口是 scikit-learn 的 CCA 类(位于 sklearn.cross_decomposition),它的接口遵循机器学习惯例:fit 拟合、transform 投影、score 打分。与 R 端"一次调用返回全表"的风格不同,sklearn 默认只做"求方向 + 打分",检验与载荷需要围绕其属性手工组装——这是两平台体验差异的最大来源,先建立预期再上手。
5.3.2 完整流程:数据准备 → 拟合 → 变换 → 结果提取
import numpy as np, pandas as pd
from sklearn.cross_decomposition import CCA
from scipy.stats import chi2
X = df[["肺活量", "最大摄氧量", "体脂率"]].values # X 组
Y = df[["百米用时", "耐力跑用时"]].values # Y 组
model = CCA(n_components=2, scale=True) # scale=True 即标准化
model.fit(X, Y) # 拟合:求解典型方向
U = model.transform(X) # 样本典型得分 U1, U2
V = model.transform(X, Y) # 注意接口:需同时传 X 定位
fit 完成后,方向向量在 model.x_rotations_(权重,符号与 model.y_rotations_ 已按"使 U、V 相关为正"对齐)。典型相关系数不在属性里,需要显式计算:
# 每列一个典型方向:对样本得分求相关
r = [np.corrcoef(U[:, i], V[:, i])[0, 1] for i in range(2)]
5.3.3 典型相关系数 / 载荷矩阵 / 交叉载荷的计算
sklearn 不直接给载荷,需要两步补全:先算"原始变量与自身侧典型得分的相关"(载荷),再算"与对侧典型得分的相关"(交叉载荷):
def loadings(A, S):
# A: 标准化原始数据 (n x d);S: 典型得分 (n x k)
A = (A - A.mean(0)) / A.std(0)
return np.corrcoef(A.T, S.T)[:A.shape[1], A.shape[1]:]
Lx = loadings(X, U) # X 组载荷:Lx[j, i] = corr(变量 j, U_i)
Ly = loadings(Y, V) # Y 组载荷
CLx = loadings(X, V) # 交叉载荷:corr(X 变量, V_i)
CLy = loadings(Y, U)
显著性检验同样手工组装(与 5.2.3 的 R 版逐行对应):
def wilks_seq(r, n, p, q):
m, res = len(r), []
for k in range(m):
Lam = np.prod(1 - np.array(r[k:]) ** 2)
df = (p - k) * (q - k)
chisq = -(n - 1 - (p + q + 1) / 2) * np.log(Lam)
res.append((k + 1, Lam, chisq, df, chi2.sf(chisq, df)))
return pd.DataFrame(res, columns=["保留前k对", "Lambda", "chisq", "df", "p"])
5.3.4 可视化:matplotlib 绘制双标图与相关性热图
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(11, 4))
axes[0].scatter(U[:, 0], V[:, 0]) # 双标图:U1 vs V1
axes[0].set(xlabel="U1", ylabel="V1")
# 相关性热图:载荷矩阵 Lx 的行=变量、列=典型轴
im = axes[1].imshow(Lx, cmap="RdBu_r", vmin=-1, vmax=1, aspect="auto")
axes[1].set(yticks=range(X.shape[1]), yticklabels=cols_x, xlabel="典型轴")
plt.colorbar(im, ax=axes[1]); plt.tight_layout(); plt.show()
热图是载荷矩阵的"最速阅读形式":行变量、列典型轴,颜色深浅直接给出 6.3 节的显著载荷识别结果。
5.4 三大平台结果对比与一致性验证
5.4.1 典型相关系数的一致性
理论底线:三平台解同一数学问题,r1,r2r_1,r_2r1,r2 应一致到小数点后 6~8 位(差异源于 SVD 与广义特征路径的舍入次序)。将三处输出并排,若某平台的第一典型相关与其余平台差超过 10−410^{-4}10−4,先查数据读取(列序颠倒、缺失值处理口径不同),再查标准化开关,最后查是否对协方差而非相关矩阵求解。数据与统计量的接口流向如图 5-3 所示。
图 5-3 三平台一致性验证的接口链路(图注:本图展示跨平台核验的数据流向,蓝色是各平台的求解接口,紫色是汇合后的对照表。请重点看三个关键节点:三路输入必须来自同一份清洗后的数据文件——数据在前端分叉,数值在后端就无法对齐;求解接口内部的路径不同(SPSS 广义特征、R 的 QR+SVD、sklearn 的迭代 NIPALS 类算法),但数学解唯一;对照表输出只需盯三个数字——第一、第二典型相关与整体检验 p 值,三者一致即判定通过。)
5.4.2 标准化系数的符号一致性
相关系数一致后,权重符号仍需对齐——因为特征向量差一个全局符号仍是特征向量,三个平台可能给出 +0.62+0.62+0.62 与 −0.62-0.62−0.62 的同一权重。对齐约定:令 UUU 与 VVV 的第一对典型相关为正(即若原始解的相关为负,把 a\boldsymbol{a}a 或 b\boldsymbol{b}b 整体乘 −1-1−1)。sklearn 已内置该对齐;R 的 cancor 默认保证相关为正;SPSS 的输出符号约定在部分版本中与 R 相反——比对权重时先按"相关系数为正"重排符号再比较绝对值,避免误判平台差异。
5.4.3 平台间差异的来源分析
数值一致的结论背后,差异仍可能来自五个来源,排查顺序如表 5-1 所示。
表 5-1 跨平台差异来源排查表
| 排查层级 | 差异来源 | 症状 | 处置 |
|---|---|---|---|
| 1 数据 | 缺失值口径不同 | 样本量对不上 | 统一逐列删除或插补方案 |
| 2 数据 | 列顺序/变量名错位 | 载荷张冠李戴 | 核对列名映射表 |
| 3 预处理 | 标准化开关不一致 | 权重尺度差若干倍 | 统一 scale 后再比较 |
| 4 数学 | 相关 vs 协方差路径 | 系数轻微漂移 | 显式指定用相关矩阵 |
| 5 数值 | 迭代收敛阈值 | 末位小数差异 | 提高收敛精度/容忍 10−410^{-4}10−4 |
认知检查点: 跨平台验证的本质是"用同一份数据做控制实验":相关系数必须一致到浮点精度、权重允许整体符号翻转、载荷必须逐变量对齐;任何超出这三条的差异都指向数据或预处理层的错误,而不是平台"各有各的算法"——统计软件没有意见分歧的资格。
输出五张表各自承担一个解读任务,其归属关系、三平台的模块分工、跨平台验证的协同闭环以及实现的整体分层,分别如图 5-4、图 5-5、图 5-6 与图 5-7 所示。
图 5-4 输出表与解读任务的知识图谱(图注:本图展示五张输出表各自服务的解读环节,红色根节点是输出体系,蓝色是五张表,橙色是各表的解读产物。请重点看:系数表管强度排序、检验表管资格、权重表管合成配方、载荷表管成员认定、冗余表管解释量化——五表各司其职,正是第六章逐节展开的解读地图;任何一张表缺位,对应的解读环节就失去数据支撑。)
图 5-5 三平台执行模块设计图(图注:本图并列三大平台的实现模块,每个模块内为"主入口—配套工序"的两级橙链。请重点看:SPSS 的配套工序在查看器导出,R 的配套工序由 CCP/comput 补齐检验与载荷,Python 的配套工序是手工补装代码——三个模块的主入口数学同解,差异全在"配套工序放哪个环节"。)
图 5-6 跨平台验证的协同闭环(图注:本图展示一致性验证的闭环,橙色是验证环节,紫色是放行输出,虚线是疑点反馈边。请重点看:系数比对与符号对齐两道工序拦截两类最典型差异;差异定位按表 5-1 的五层排查表逐层下探;疑点一旦定位到数据层,反馈虚线把流程送回求解起点——验证不是一次通过就结束,而是以"数据一致"为终止条件的循环。)
图 5-7 实操实现的三层架构总览(图注:本图分层展示实现全流程,分层配色——黄色数据层界定输入与预处理约定,蓝色求解层完成分析与检验,紫色输出层给出结果出口与可复现脚本。请重点看:预处理约定是数据层通住求解层的规格书,两个平台若在此处分叉,下游一切比对都失去意义;可复现脚本是输出层的最佳形态——"结果出口 + 脚本存档"齐备,分析才真正完成。)
本章小结:实操实现留下的四个判断
第一,流程骨架平台无关,差异全在"结果出口"。数据读入、标准化、求解、检验、载荷五步在三平台一一对应,真正的学习成本是记住各自的对象与属性:SPSS 看查看器、R 看 cc 对象、Python 看 model 属性。
第二,SPSS 的黄金习惯是"粘贴语法"。菜单用于探索、语法用于存档,OMS 导出是批量出表的捷径;只会点菜单的分析不可复现,等于没做。
第三,R 与 Python 的互补格局:R 的 CCA::cc + CCP 一条龙覆盖经典分析全流程;Python 的 sklearn 是工程化与机器学习的入口,检验与载荷需手工组装——5.3.3 节的 30 行补全代码应视作标准配置随项目存档。
第四,一致性验证是每次分析发布的"出厂质检"。系数一致、符号按约定对齐、差异按五层排查表定位——这三条工序加起来不超过十分钟,却能拦住绝大多数"换了个软件结果就变了"的冤案。
代码是语言的外壳,解读是分析的内核。下一章把注意力从"怎么算出来"转向"怎么说清楚":标准化系数的稳定性陷阱、载荷矩阵的显著性阈值、方差解释与冗余度在论文中的标准表述,以及 APA 格式的 CCA 结果报告模板。
参考文献(本章)
[1] González I, Déjean S, Martin P G P, Baccini A. CCA: An R package to extend canonical correlation analysis[J]. Journal of Statistical Software, 2008, 23(12): 1-14.
[2] scikit-learn developers. sklearn.cross_decomposition.CCA: Documentation[EB/OL]. https://scikit-learn.org.
[3] IBM SPSS Statistics Documentation: Canonical Correlation[EB/OL].
[4] Pedregosa F, et al. Scikit-learn: Machine learning in Python[J]. Journal of Machine Learning Research, 2011, 12: 2825-2830.
[5] 张文彤, 董伟. SPSS 统计分析高级教程[M]. 北京: 高等教育出版社.
[6] 何晓群. 多元统计分析[M]. 北京: 中国人民大学出版社.
】第五章 经典CCA的实操实现&spm=1001.2101.3001.5002&articleId=164341247&d=1&t=3&u=c6b33f530f844c7994235823fc7de0bd)
8893

被折叠的 条评论
为什么被折叠?



