📑 查看全课大纲(第 18 / 20 节)

对应分析代码实战与双标图解读

约 14 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 多元统计分析

对应分析(Correspondence Analysis)是多元统计中处理定性变量关联的核心方法。本节将讲解其原理与Python实现流程,学完后你可独立完成列联表的对应分析与双标图解读。

💡 核心导读

  • 核心思想:统一R型与Q型因子分析,实现行、列类别联合可视化
  • 分析前提:需通过卡方独立性检验验证变量关联显著性
  • 技术路径:标准化残差矩阵SVD分解→低维坐标提取→双标图绘制
  • 解读逻辑:行点与列点同方向(夹角小)为正关联,反方向为负关联

对应分析基本原理

设有r×cr \times c列联表K=(kij)K=(k_{ij}),总样本量n=i=1rj=1ckijn=\sum_{i=1}^r\sum_{j=1}^c k_{ij},频率矩阵P=K/nP=K/n。记行边际概率r=(r1,,rr)\mathbf{r}=(r_1,\dots,r_r)^\topri=jpijr_i=\sum_j p_{ij})、列边际概率c=(c1,,cc)\mathbf{c}=(c_1,\dots,c_c)^\topcj=ipijc_j=\sum_i p_{ij})。

核心变换为标准化残差矩阵ZZ,元素为: zij=pijricjricjz_{ij}=\frac{p_{ij}-r_i c_j}{\sqrt{r_i c_j}}

ZZ做奇异值分解Z=USVZ=USV^\top,其中URr×mU\in\mathbb{R}^{r\times m}VRc×mV\in\mathbb{R}^{c\times m}为正交矩阵,m=min(r1,c1)m=\min(r-1,c-1)S=diag(σ1,,σm)S=\text{diag}(\sigma_1,\dots,\sigma_m)为奇异值对角矩阵;主惯量对角矩阵为Λ=S2=diag(λ1,,λm)\Lambda=S^2=\text{diag}(\lambda_1,\dots,\lambda_m)(其中λj=σj2\lambda_j=\sigma_j^2,记号与7.2节一致)。

行、列坐标计算公式为: F=Dr1/2US,G=Dc1/2VSF=D_r^{-1/2}US, \quad G=D_c^{-1/2}VS 其中Dr=diag(r1,,rr)D_r=\text{diag}(r_1,\dots,r_r)Dc=diag(c1,,cc)D_c=\text{diag}(c_1,\dots,c_c)。通常取前2维绘制双标图。注意行点与列点分属不同度量空间的投影,二者间的欧氏距离并无直接解读意义;判断关联应看行点与列点相对原点的方向:方向一致(夹角小)为正关联,方向相反为负关联。

Python实战:消费支出对应分析

我们以7个收入等级、7个消费类别的城镇居民消费支出数据为例,完整实现对应分析。

数据前提说明(伪列联表):卡方独立性检验与对应分析的概率模型要求列联表单元格为非负计数(人数、户数等,服从多项分布)。本例单元格为消费支出金额(连续量,单位:百元),并非计数频数;对金额直接计算在代数上可以进行,但此时「独立性检验」的抽样分布解释并不严格成立。本例实质是把支出构成当作伪列联表描述性对应分析,卡方检验结果仅作近似参考;规范研究中应改用真正的计数(人数/户数)列联表,或先将金额转换为支出构成(行轮廓)再做分析。

import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency
import matplotlib.pyplot as plt

# 1. 数据准备:7个收入等级 × 7个消费类别(单位:百元)
income_levels = ['最低收入户', '较低收入户', '中等偏下户', '中等收入户', 
                 '中等偏上户', '较高收入户', '最高收入户']
consume_cats = ['肉禽制品', '蛋类', '水产品', '奶制品', '服装', '耐用品', '文化娱乐']
consumption_data = np.array([
    [45,12,8,6,25,5,3], [52,14,10,8,32,8,5], [60,16,15,12,40,15,10],
    [68,18,20,18,48,25,18], [75,20,25,25,55,35,25], [82,22,30,32,62,45,35],
    [90,25,35,40,70,60,50]
])
df = pd.DataFrame(consumption_data, index=income_levels, columns=consume_cats)
print("消费支出列联表维度:", df.shape)

# 2. 卡方独立性检验(注意:金额为连续量而非计数,此处为描述性近似参考,见上文数据前提说明)
chi2, p_val, dof, _ = chi2_contingency(consumption_data)
print(f"卡方统计量: {chi2:.2f}, P值: {p_val:.4e}, 自由度: {dof}")
print("是否适合对应分析:", p_val < 0.05)

# 3. 对应分析核心计算
def ca_analysis(table, n_comp=2):
    n = table.sum()
    P = table / n
    r, c = P.sum(axis=1), P.sum(axis=0)
    # 计算标准化残差矩阵
    Dr_inv = np.diag(1 / np.sqrt(r))
    Dc_inv = np.diag(1 / np.sqrt(c))
    Z = Dr_inv @ (P - np.outer(r, c)) @ Dc_inv
    # SVD降维
    U_full, s_full, Vt_full = np.linalg.svd(Z, full_matrices=False)
    U, s, V = U_full[:, :n_comp], s_full[:n_comp], Vt_full[:n_comp, :].T
    # 生成行、列坐标
    row_coord = Dr_inv @ U @ np.diag(s)
    col_coord = Dc_inv @ V @ np.diag(s)
    # 计算方差解释率
    exp_var = (s**2) / (s_full**2).sum()
    return row_coord, col_coord, exp_var

row_coord, col_coord, exp_var = ca_analysis(consumption_data)
print(f"前两维方差解释率: {exp_var.round(4)}")

# 4. 双标图绘制
plt.figure(figsize=(10,8))
# 绘制行点(收入等级)
plt.scatter(row_coord[:,0], row_coord[:,1], c='red', s=100, label='收入等级')
for i, label in enumerate(income_levels):
    plt.annotate(label, (row_coord[i,0], row_coord[i,1]), xytext=(5,5), fontsize=9, c='darkred')
# 绘制列点(消费类别)
plt.scatter(col_coord[:,0], col_coord[:,1], c='blue', s=100, label='消费类别')
for j, label in enumerate(consume_cats):
    plt.annotate(label, (col_coord[j,0], col_coord[j,1]), xytext=(5,5), fontsize=9, c='darkblue')
# 辅助线与标签
plt.axhline(0, c='gray', ls='--', alpha=0.5)
plt.axvline(0, c='gray', ls='--', alpha=0.5)
plt.xlabel(f"维度1(解释{exp_var[0]*100:.1f}%方差)")
plt.ylabel(f"维度2(解释{exp_var[1]*100:.1f}%方差)")
plt.title("城镇居民消费支出对应分析双标图")
plt.legend()
plt.grid(alpha=0.3)
plt.show()

运行结果:

消费支出列联表维度: (7, 7)
卡方统计量: 66.30, P值: 1.550e-03, 自由度: 36
是否适合对应分析: True
前两维方差解释率: [0.9891 0.0084]

从双标图可观察到三类聚类:低收入群体与肉禽制品、蛋类等基本食品消费关联紧密;中等收入群体与水产品、奶制品、服装等品质消费关联;高收入群体与耐用品、文化娱乐等高端消费关联,清晰呈现了从生存型到享受型的消费升级路径。

结果解读与商业洞察

双标图中应看行点(收入群体)与列点(消费类别)相对原点的方向:方向一致(夹角小)表明该收入群体在该消费类别上的支出占比越具代表性。分析显示:

  • 生存型消费:低收入群体(最低、较低、中等偏下)与肉禽、蛋类等必需品高度关联
  • 品质型消费:中等收入群体(中等、中等偏上、较高)与奶制品、水产品、服装等关联
  • 发展型消费:最高收入户与耐用品、文化娱乐等关联

这种分层结构符合马斯洛需求层次理论,为市场细分和产品定位提供了量化依据。

📝 动手练一练

使用下方品牌-特征列联表,计算品牌0与特征0、特征1在双标图中的方向一致性(夹角余弦),判断哪个特征与品牌0关联更强。

import numpy as np
# 品牌-特征列联表(4品牌×3特征)
table = np.array([[120,30,20], [40,150,30], [25,35,140], [80,70,60]])
# 对应分析计算
n = table.sum()
P = table / n
r, c = P.sum(axis=1), P.sum(axis=0)
Z = np.diag(1/np.sqrt(r)) @ (P - np.outer(r,c)) @ np.diag(1/np.sqrt(c))
U, s, Vt = np.linalg.svd(Z, full_matrices=False)
row = np.diag(1/np.sqrt(r)) @ U[:,:2] @ np.diag(s[:2])
col = np.diag(1/np.sqrt(c)) @ Vt[:2,:].T @ np.diag(s[:2])
# 计算方向一致性(夹角余弦)
cos0 = np.dot(row[0], col[0]) / (np.linalg.norm(row[0]) * np.linalg.norm(col[0]))
cos1 = np.dot(row[0], col[1]) / (np.linalg.norm(row[0]) * np.linalg.norm(col[1]))
print(f"品牌0与特征0余弦相似度: {cos0:.4f}, 与特征1余弦相似度: {cos1:.4f}")
print("品牌0与特征0关联更强:", cos0 > cos1)

运行结果:

品牌0与特征0余弦相似度: 0.9998, 与特征1余弦相似度: -0.4234
品牌0与特征0关联更强: True

参考答案:品牌0与特征0的夹角余弦接近1(方向高度一致),与特征1的余弦为负(方向相反),因此品牌0与特征0关联更强。

本章小结

本节核心要点:

  1. 对应分析通过标准化残差矩阵的SVD分解,实现行、列类别的联合降维可视化
  2. 分析前需通过卡方独立性检验验证变量关联显著性,避免盲目分析
  3. 双标图中行点与列点方向一致(夹角小、同远离原点)为强正关联,方向相反为负关联,可用于市场细分、品牌定位等业务场景

行动清单

  • 使用chi2_contingency函数验证你的列联表数据是否适合对应分析
  • 实现完整的对应分析流程:数据标准化→SVD分解→坐标计算→双标图绘制
  • 尝试解读一个实际业务场景的双标图,找出关键关联模式

— 小象教研组

配套学习资源与课件
  • 第7章课件:对应分析(相应分析)
    下载
  • 多元统计分析参考讲义与常用函数(多元分析与主成分常用函数速查)
    下载
  • 课程配套数据集(全课程实战数据包)
    下载
  • 课程全套源代码(课程相关代码汇总)
    下载
🎁 免费学习资源

领取《小象 11GB VIP 课件资料包与大厂真题手册》

包含全套实战 Jupyter 源码、清洗后数据集、大厂高频面试真题与专属学员答疑交流群。

  • 完整 Python / 数据分析 Jupyter 实战源码
  • 大厂真实业务数据集与练习题
  • 微信扫码添加顾问免费领取;想学什么,直接告诉顾问
微信二维码:扫码添加课程顾问微信扫码添加顾问