ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

主成分分析与因子分析:降维原理、Python实现与场景选择指南

主成分分析与因子分析:降维原理、Python实现与场景选择指南 1. 从“降维”说起为什么我们需要主成分分析和因子分析做数据分析的朋友尤其是处理过问卷、量表、财务指标或者任何包含一大堆变量的数据集的朋友肯定都遇到过这样的困境手头有几十个甚至上百个指标它们之间往往还存在着千丝万缕的相关性。你想用这些指标去评价一个对象比如一家公司的综合实力、一个地区的经济发展水平、一个用户的满意度但直接把这些指标一股脑儿扔进模型里不仅计算复杂、容易过拟合而且指标间的多重共线性会让结果难以解释甚至失真。这时候“降维”就成了一个必须面对的课题。降维不是简单粗暴地删除指标而是在尽可能保留原始数据信息的前提下用更少的、互不相关的“新变量”来代表原来的那一大堆变量。这就好比你要向别人描述一个人的外貌不需要事无巨细地列出身高、臂长、腿长、头围等几十个数据而是用“高挑”、“魁梧”、“清瘦”这几个综合印象就能传达出核心特征。主成分分析PCA和因子分析FA就是统计学中两种最经典、应用最广泛的降维“利器”。虽然最终目的都是简化数据结构但PCA和FA从思想根源上就分道扬镳了。PCA更像是一个“数据工程师”它不关心数据是怎么来的只关心怎么用一组新的、正交的坐标轴主成分来重新表达原有数据并且让第一个新坐标轴第一主成分携带的信息量最大第二个与第一个正交且携带剩余信息中最大的部分以此类推。它寻找的是数据方差最大的方向纯粹是数学上的变换。而因子分析则更像一个“理论侦探”。它假设我们观测到的所有变量比如“数学成绩”、“物理成绩”、“逻辑题得分”背后都受到少数几个无法直接测量的“公共因子”比如“数理逻辑能力”所支配同时每个变量还受到自己独有的“特殊因子”影响。因子分析的目标就是把这些隐藏的“公共因子”给挖掘出来并解释观测变量与这些因子之间的关系。它带有很强的模型假设和解释性目的。理解这个根本区别是正确选用这两种方法并读懂后续Python代码结果的前提。接下来我们就深入它们的内部看看具体是怎么实现的。2. 主成分分析寻找数据“伸展”得最开的方向2.1 核心思想与数学过程拆解主成分分析的目标非常明确对原始数据进行一种线性变换生成一组新的变量主成分满足两个核心条件1. 各主成分之间互不相关正交2. 第一主成分方差最大第二主成分在剩余方差中最大且与第一主成分正交依此类推。这个过程在数学上等价于对原始数据的协方差矩阵或相关系数矩阵当变量量纲差异大时需先标准化进行特征值分解。我们来一步步拆解第一步数据标准化通常建议进行这不是PCA数学上的必须步骤但强烈建议。因为PCA对变量的方差非常敏感如果某个变量的单位很大比如“销售额”以亿元计其方差就会主导主成分的方向导致量纲小的变量如“利润率百分比”被忽略。标准化就是将每个变量减去其均值再除以其标准差使得所有变量都处于同一“起跑线”均值为0标准差为1。第二步计算协方差矩阵或相关矩阵假设我们有p个变量n个样本。标准化后的数据矩阵为Xn x p。其协方差矩阵Σ是一个 p x p 的对称矩阵对角线元素是各变量的方差标准化后均为1非对角线元素是变量两两之间的协方差标准化后即为相关系数。第三步特征值分解对协方差矩阵Σ进行特征值分解Σ VΛVᵀ。Λ是一个对角矩阵对角线上的元素λ₁, λ₂, ..., λ_p就是特征值并且我们通常按从大到小排序λ₁ ≥ λ₂ ≥ ... ≥ λ_p ≥ 0。V的每一列v₁, v₂, ..., v_p是对应的特征向量并且是单位正交的。第四步主成分的构成与解释第k个主成分PC_k就是原始变量X在特征向量v_k方向上的投影PC_k X * v_k。这个v_k就被称为第k主成分的“载荷向量”。特征值λ_k的物理意义就是第k个主成分的方差。因为数据已标准化所有原始变量的总方差为p。因此第k个主成分的方差贡献率为λ_k / p前m个主成分的累计方差贡献率为(λ₁λ₂...λ_m) / p。关键理解特征向量v_k定义了新坐标轴的方向特征值λ_k衡量了这个方向的重要性。载荷向量v_k中每个元素的绝对值大小代表了对应原始变量对该主成分的“贡献”大小符号代表正负关系。这是我们解释主成分含义的依据。2.2 Python实现与结果解读我们使用scikit-learn库来实现PCA它封装得很好但明白背后的计算至关重要。import numpy as np import pandas as pd from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler import matplotlib.pyplot as plt # 1. 构造示例数据假设我们测量了4种产品的3个指标销量、利润、客户评分 data np.array([ [100, 20, 8.5], [150, 25, 7.8], [80, 15, 9.2], [200, 40, 7.0], [120, 22, 8.0] ]) df pd.DataFrame(data, columns[销量, 利润, 评分]) print(原始数据) print(df) # 2. 数据标准化强烈推荐 scaler StandardScaler() data_scaled scaler.fit_transform(df) print(\n标准化后数据均值~0 标准差~1) print(pd.DataFrame(data_scaled, columnsdf.columns).round(2)) # 3. 执行PCA pca PCA() # 默认保留所有成分 pca.fit(data_scaled) # 拟合模型 # 4. 查看核心结果 print(f\n各主成分的方差特征值: {pca.explained_variance_}) print(f各主成分的方差贡献率: {pca.explained_variance_ratio_}) print(f累计方差贡献率: {np.cumsum(pca.explained_variance_ratio_)}) # 主成分载荷矩阵成分矩阵每一列是一个主成分的载荷向量 loadings pca.components_.T * np.sqrt(pca.explained_variance_) # 这是计算载荷的一种常用方式 loadings_df pd.DataFrame(loadings, indexdf.columns, columns[fPC{i1} for i in range(len(df.columns))]) print(\n主成分载荷矩阵Loadings Matrix:) print(loadings_df.round(4)) # 5. 计算样本的主成分得分转换后的新坐标 scores pca.transform(data_scaled) scores_df pd.DataFrame(scores, columns[fPC{i1} for i in range(len(df.columns))]) print(\n样本的主成分得分:) print(scores_df.round(2)) # 6. 可视化 - 碎石图用于决定保留几个主成分 plt.figure(figsize(10, 4)) plt.subplot(1, 2, 1) plt.plot(range(1, len(pca.explained_variance_ratio_)1), pca.explained_variance_ratio_, bo-, linewidth2) plt.title(Scree Plot: Variance Ratio per PC) plt.xlabel(Principal Component) plt.ylabel(Variance Explained Ratio) plt.grid(True) plt.subplot(1, 2, 2) plt.plot(range(1, len(pca.explained_variance_ratio_)1), np.cumsum(pca.explained_variance_ratio_), ro-, linewidth2) plt.title(Cumulative Variance Explained) plt.xlabel(Number of Principal Components) plt.ylabel(Cumulative Variance Explained Ratio) plt.axhline(y0.85, colorg, linestyle--, label85% threshold) # 常用阈值 plt.legend() plt.grid(True) plt.tight_layout() plt.show()结果解读实战假设我们运行上述代码得到如下关键输出数值为模拟方差贡献率:[0.721, 0.245, 0.034]累计贡献率:[0.721, 0.966, 1.000]载荷矩阵:PC1 PC2 PC3 销量 0.9500 0.2800 -0.1400 利润 0.9700 -0.2100 0.1200 评分 -0.6500 0.7300 0.2100决定保留几个主成分看碎石图和累计贡献率。第一主成分PC1解释了72.1%的方差前两个主成分累计解释了96.6%的方差。通常我们选择累计贡献率超过85%或根据碎石图拐点这里在PC2之后变平来确定。这里保留两个主成分PC1, PC2就足够了它们几乎代表了原始三个指标的全部信息。解释主成分的含义PC1综合规模因子在PC1上“销量”(0.95)和“利润”(0.97)有很高的正载荷且数值接近说明PC1主要综合反映了“销量”和“利润”的信息且两者变化高度一致。“评分”载荷为负(-0.65)意味着在这个综合维度上评分与销量、利润呈反向关系可能意味着薄利多销的产品评分不高或高利润产品服务可能跟不上。我们可以将PC1命名为“商业规模与效益”因子。PC2质量口碑因子在PC2上“评分”(0.73)有最高的正载荷“销量”(0.28)有较小的正载荷而“利润”(-0.21)为负。这似乎捕捉到了“叫好又叫座但不一定最赚钱”或者“高口碑产品”的维度。可以命名为“市场接受与口碑”因子。使用主成分得分scores_df给出了每个样本产品在PC1和PC2上的坐标。我们可以用这两个得分来代替原来的三个指标进行后续分析比如聚类、回归或排序。例如PC1得分高的产品商业规模效益好PC2得分高的产品口碑和市场接受度好。实操心得PCA的载荷符号有时会因计算中的方向选择特征向量方向可以反向而不同但这不影响解释关注绝对值大小。解释主成分时不要强行给每个成分都安上一个完美的名字有时它就是一个数学上的综合能用于区分样本即可。标准化几乎是必须的除非你确信所有变量量纲一致且方差的意义可比。3. 因子分析探寻变量背后的“公共驱动因子”3.1 模型假设与求解思路因子分析基于一个非常直观的数学模型。假设我们有p个标准化后的观测变量X₁, X₂, ..., X_p。因子分析认为存在m个 (m p) 无法直接观测的公共因子F₁, F₂, ..., F_m以及p个唯一影响每个变量的特殊因子ε₁, ε₂, ..., ε_p使得X_i l_{i1}F₁ l_{i2}F₂ ... l_{im}F_m ε_i 对于i 1, 2, ..., p。其中l_{ij}称为因子载荷表示第i个变量在第j个公共因子上的负荷绝对值越大关系越强。公共因子之间通常假设互不相关正交因子模型也可以相关斜交因子模型。特殊因子ε_i之间互不相关且与所有公共因子也不相关。我们的目标就是从观测数据X中估计出因子载荷矩阵Lp x m以及特殊因子的方差称为独特性。常用的估计方法有主成分法、主轴因子法、最大似然法等。与PCA的关键区别PCA中的主成分是原始变量的线性组合PC X * v而因子分析中的观测变量是公共因子的线性组合X L * F ε。PCA关注如何用少数成分“代表”XFA关注如何用少数因子“生成”X。3.2 Python实现与因子旋转我们使用factor_analyzer库它比scikit-learn的因子分析功能更全面支持旋转。import pandas as pd import numpy as np from factor_analyzer import FactorAnalyzer from factor_analyzer.factor_analyzer import calculate_bartlett_sphericity, calculate_kmo import matplotlib.pyplot as plt # 1. 使用更复杂的数据示例学生6门科目成绩 np.random.seed(42) n_samples 200 # 假设存在两个公共因子文科因子影响语文、历史、英语、理科因子影响数学、物理、化学 # 因子载荷预设 true_loadings np.array([ [0.8, 0.1], # 语文 [0.7, 0.2], # 历史 [0.6, 0.3], # 英语 [0.1, 0.85], # 数学 [0.2, 0.8], # 物理 [0.3, 0.75] # 化学 ]) m true_loadings.shape[1] # 公共因子数2 p true_loadings.shape[0] # 变量数6 # 生成公共因子和特殊因子 F np.random.randn(n_samples, m) # 公共因子 ~ N(0,1) epsilon np.random.randn(n_samples, p) * 0.3 # 特殊因子方差小一些 # 生成观测数据 X F * L.T epsilon (注意载荷矩阵的转置) X np.dot(F, true_loadings.T) epsilon # 为了更真实我们加入一些变量特有的方差独特性 for i in range(p): X[:, i] np.random.randn(n_samples) * np.sqrt(1 - np.sum(true_loadings[i]**2)) * 0.5 df_scores pd.DataFrame(X, columns[语文, 历史, 英语, 数学, 物理, 化学]) print(生成的学生成绩数据部分:) print(df_scores.head()) # 2. 数据适用性检验非常重要 # Bartlett球形检验检验变量间是否独立。原假设是相关矩阵为单位阵即变量独立。 chi_square_value, p_value calculate_bartlett_sphericity(df_scores) print(f\nBartlett球形检验: 卡方值{chi_square_value:.2f}, p值{p_value:.4f}) if p_value 0.05: print(- p0.05拒绝原假设变量间存在相关性适合做因子分析。) else: print(- 变量间可能独立不适合做因子分析。) # KMO检验比较变量间简单相关和偏相关系数衡量采样充足度。0.6可接受0.8很好。 kmo_all, kmo_model calculate_kmo(df_scores) print(fKMO检验值: {kmo_model:.4f}) if kmo_model 0.8: print(- KMO值极佳非常适合因子分析。) elif kmo_model 0.6: print(- KMO值中等可以接受。) else: print(- KMO值较差可能不适合因子分析。) # 3. 确定因子数量 fa FactorAnalyzer(rotationNone, methodml) # 先用最大似然法不旋转 fa.fit(df_scores) # 绘制碎石图基于特征值 ev, v fa.get_eigenvalues() plt.figure(figsize(8,5)) plt.scatter(range(1, df_scores.shape[1]1), ev, s100) plt.plot(range(1, df_scores.shape[1]1), ev) plt.axhline(y1, colorr, linestyle--, labelKaiser准则 (特征值1)) plt.title(Scree Plot for Factor Analysis) plt.xlabel(Factor Number) plt.ylabel(Eigenvalue) plt.legend() plt.grid(True) plt.show() print(f\n特征值: {ev}) # Kaiser准则保留特征值大于1的因子。平行分析是更稳健的方法这里简化。 n_factors sum(ev 1) print(f根据Kaiser准则特征值1建议保留 {n_factors} 个因子。) # 4. 进行因子分析以2个因子为例并进行旋转 fa FactorAnalyzer(n_factors2, rotationvarimax, methodml) # 方差最大旋转最常用 fa.fit(df_scores) # 获取因子载荷矩阵 loadings fa.loadings_ loadings_df pd.DataFrame(loadings, indexdf_scores.columns, columns[fFactor{i1} for i in range(2)]) print(\n旋转后的因子载荷矩阵:) print(loadings_df.round(3)) # 5. 计算共同度Communality和特殊方差Uniqueness communality fa.get_communalities() uniqueness fa.get_uniquenesses() print(\n变量共同度因子解释的方差比例:) for i, col in enumerate(df_scores.columns): print(f {col}: {communality[i]:.3f} (独特性: {uniqueness[i]:.3f})) # 6. 可选获取因子得分每个学生在两个因子上的得分 factor_scores fa.transform(df_scores) factor_scores_df pd.DataFrame(factor_scores, columns[fFactor{i1}_Score for i in range(2)]) print(\n前5个学生的因子得分:) print(factor_scores_df.head().round(3))结果解读实战假设输出如下Bartlett检验p值远小于0.05KMO值0.82数据非常适合因子分析。碎石图显示前两个特征值大于1第三个开始陡降确定提取2个因子。旋转后因子载荷矩阵Factor1 Factor2 语文 0.872 0.123 历史 0.801 0.210 英语 0.743 0.185 数学 0.089 0.901 物理 0.152 0.842 化学 0.211 0.798共同度各变量均在0.6以上说明两个因子能较好地解释这些变量。因子命名与解释Factor1在“语文”、“历史”、“英语”上有非常高的载荷0.74而在理科科目上载荷很低。这清晰地指向了“文科能力”或“语言逻辑因子”。Factor2在“数学”、“物理”、“化学”上有非常高的载荷0.79在文科上载荷低。这清晰地指向了“理科能力”或“数理逻辑因子”。旋转这里是方差最大法Varimax的作用至关重要它使得因子载荷矩阵的“简单结构”更明显即每个变量尽可能只在一个因子上有高载荷在其他因子上载荷接近0这样因子的解释性大大增强。如果不旋转载荷矩阵可能很混乱难以命名。共同度与独特性共同度表示该变量方差能被所有公共因子共同解释的比例。例如“语文”共同度0.78意味着其变异的78%可由两个公共因子解释剩下的22%是特殊因子如个人兴趣、临场发挥等的影响。共同度太低如0.4的变量考虑删除或反思其是否属于当前因子体系。因子得分factor_scores给出了每个学生在两个因子上的量化得分。这可以用于后续分析比如将学生按文理能力分类或者作为回归模型的自变量控制文理能力后看其他因素对总成绩的影响。踩坑实录因子分析最常犯的错误是不做数据适用性检验KMO和Bartlett。如果变量间本身相关性很弱强行做因子分析的结果是无效的。另一个坑是因子数量的选择特征值1Kaiser准则只是一个经验法则在变量多时可能提取过多因子需要结合碎石图拐点、方差解释率通常累计60%、以及因子的可解释性综合判断。最大似然法有时会无法收敛或出现“Heywood case”共同度1这时可以尝试主成分法或调整因子数量。4. PCA vs FA核心差异、适用场景与选择指南经过前面的拆解我们可以系统地对比这对“孪生兄弟”了。理解它们的差异是正确选型的关键。4.1 哲学思想与模型假设的根本不同这是所有差异的根源。PCA是一种数据变换技术。它没有模型假设不关心数据生成机制。它的目标是找到一组新的正交基来重新表示数据追求的是表示数据的效率——用最少的成分解释最多的方差。PCA的“成分”是原始变量的线性组合。FA是一种统计建模方法。它有明确的模型假设观测变量由潜在的公共因子和特殊因子线性生成。它的目标是揭示数据结构验证变量背后是否存在假设的潜在构念Construct并估计变量与因子的关系。FA的“因子”是假设的潜变量观测变量是因子的线性组合。4.2 数学目标与求解对象的差异PCA目标最大化每个主成分的方差即特征值。求解对象是数据的协方差/相关矩阵本身通过特征值分解直接得到成分。FA目标通过模型X LF ε来拟合数据的协方差/相关矩阵。求解对象是因子载荷矩阵L和独特性方差使得模型推导出的协方差矩阵尽可能接近样本协方差矩阵。这通常需要一个迭代估计过程。4.3 结果解释与旋转的差异PCA成分主成分是正交的且按方差大小排序。第一个成分就是方差最大的方向。解释时我们看每个原始变量在该成分上的载荷权重。FA因子初始提取的因子可能难以解释因此几乎总是需要进行因子旋转如Varimax正交旋转或Promax斜交旋转。旋转的目的是使载荷矩阵结构更简单便于对因子进行命名和解释。旋转后因子可能不再正交如果使用斜交旋转。4.4 方差处理的差异这是初学者最容易混淆的一点。PCA解释的是总方差。每个主成分的方差特征值包含了变量方差的所有部分共同方差唯一方差。FA聚焦于共同方差。因子分析试图只解释变量之间的共同方差协方差而将每个变量的唯一方差特殊方差分离出去。因此FA的出发点通常是调整后的相关矩阵对角线元素不是1而是共同度估计值。4.5 适用场景选择指南如何选择记住这个简单的决策流你的核心目标是什么目标是为了降维、压缩数据、减少变量数量以便进行后续的回归、聚类、可视化等操作-优先选择PCA。PCA更稳健计算快总是有解且能最大程度保留原始数据的方差非常适合作为数据预处理步骤。目标是为了验证理论结构、探索变量背后的潜在维度、测量不可直接观测的构念如智力、满意度、社会经济地位-应该选择FA。FA的模型特性允许你检验因子结构评估测量工具如量表的效度。具体场景举例图像压缩、基因表达数据降噪、金融指标合成指数用PCA。我们只关心如何用更少的数据高效地代表原信息。心理学量表开发、满意度调查问卷分析、社会经济指标归类用FA。我们关心“焦虑”、“忠诚度”、“发展水平”这些潜变量是否真的存在以及每个题目观测变量在多大程度上测量了它们。探索性数据分析EDA不清楚数据结构可以两者都做对比看看。PCA的碎石图可以帮助初步判断潜在维度数量FA的旋转结果可以提供更有理论意义的解释。一个经验法则如果你打算用降维后的新变量去替代原变量做后续分析PCA更合适。如果你是想理解和解释变量之间的内在关系结构FA更合适。个人经验之谈在实际项目中我经常看到误用。很多人只要看到多个变量想降维就上FA结果解释起来很牵强。我的习惯是但凡没有坚实的理论预期潜在因子是什么或者主要目的是为了简化数据输入给下游模型一律先用PCA。只有当我有明确的假设比如这份问卷就是设计来测量三个维度的并且需要检验这个结构时才会做验证性因子分析CFAFA的一种或探索性因子分析EFA。对于纯粹的预测模型PCA预处理的效果通常比FA更好、更稳定。5. 综合例题用Python分析消费者品牌感知让我们用一个更贴近实际的综合案例串联起从数据准备、方法选择、实施到解读的全过程。场景某手机品牌收集了200名消费者对其7个品牌属性的评分1-10分设计、续航、拍照、系统、价格、服务、口碑。我们想了解消费者评价背后的潜在维度并简化这些指标。import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.decomposition import PCA from factor_analyzer import FactorAnalyzer from factor_analyzer.factor_analyzer import calculate_kmo, calculate_bartlett_sphericity # 1. 模拟生成数据 np.random.seed(123) n 200 # 假设存在两个潜在因子1. 产品硬实力影响设计、续航、拍照、系统 2. 市场软实力影响价格、服务、口碑 # 同时价格可能与两个因子都负相关硬实力强价格高软实力强可能性价比高 latent_factor1 np.random.randn(n) * 1.5 # 产品硬实力 latent_factor2 np.random.randn(n) * 1.5 # 市场软实力 data {} data[设计] 0.8*latent_factor1 0.1*latent_factor2 np.random.randn(n)*0.8 6 data[续航] 0.7*latent_factor1 0.0*latent_factor2 np.random.randn(n)*1.0 5 data[拍照] 0.9*latent_factor1 - 0.1*latent_factor2 np.random.randn(n)*0.7 7 data[系统] 0.6*latent_factor1 0.2*latent_factor2 np.random.randn(n)*1.1 6 data[价格] -0.3*latent_factor1 - 0.6*latent_factor2 np.random.randn(n)*0.9 5 # 与因子负相关 data[服务] 0.1*latent_factor1 0.85*latent_factor2 np.random.randn(n)*0.6 6 data[口碑] 0.2*latent_factor1 0.75*latent_factor2 np.random.randn(n)*0.8 7 df_brand pd.DataFrame(data) # 将评分限制在1-10分 df_brand df_brand.clip(1, 10) print(消费者品牌感知数据前5行:) print(df_brand.head().round(2)) print(f\n数据形状: {df_brand.shape}) print(\n各变量描述性统计:) print(df_brand.describe().round(2)) # 2. 数据标准化 scaler StandardScaler() df_scaled pd.DataFrame(scaler.fit_transform(df_brand), columnsdf_brand.columns) # 3. 先进行PCA分析 print(\n *60) print(主成分分析 (PCA) 结果) print(*60) pca PCA() pca.fit(df_scaled) print(f特征值方差: {pca.explained_variance_.round(3)}) print(f方差贡献率: {pca.explained_variance_ratio_.round(3)}) print(f累计方差贡献率: {np.cumsum(pca.explained_variance_ratio_).round(3)}) # 绘制碎石图 plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(range(1, 8), pca.explained_variance_ratio_, bo-) plt.axhline(y1/7, colorr, linestyle--, labelAverage Variance (1/p)) plt.title(PCA Scree Plot) plt.xlabel(Principal Component) plt.ylabel(Variance Explained Ratio) plt.legend() plt.grid(True) # 前两个主成分的载荷图 loadings_pca pca.components_.T * np.sqrt(pca.explained_variance_) plt.subplot(1, 2, 2) for i, feature in enumerate(df_brand.columns): plt.arrow(0, 0, loadings_pca[i, 0], loadings_pca[i, 1], head_width0.05, head_length0.05, fck, eck) plt.text(loadings_pca[i, 0]*1.15, loadings_pca[i, 1]*1.15, feature, fontsize10) plt.axhline(y0, colorgrey, linestyle--, linewidth0.5) plt.axvline(x0, colorgrey, linestyle--, linewidth0.5) plt.xlim(-1, 1) plt.ylim(-1, 1) plt.xlabel(PC1 ({:.1f}%).format(pca.explained_variance_ratio_[0]*100)) plt.ylabel(PC2 ({:.1f}%).format(pca.explained_variance_ratio_[1]*100)) plt.title(PCA Loadings Plot (PC1 vs PC2)) plt.grid(True) plt.tight_layout() plt.show() # 4. 进行因子分析前的检验 print(\n *60) print(因子分析 (FA) 适用性检验) print(*60) chi_square_value, p_value calculate_bartlett_sphericity(df_scaled) kmo_all, kmo_model calculate_kmo(df_scaled) print(fBartlett球形检验 p值: {p_value:.4f} (应0.05)) print(fKMO检验值: {kmo_model:.4f} (应0.6)) # 5. 因子分析探索性 print(\n *60) print(探索性因子分析 (EFA) 结果) print(*60) # 确定因子数看特征值 fa_for_n FactorAnalyzer(rotationNone, methodml) fa_for_n.fit(df_scaled) ev, v fa_for_n.get_eigenvalues() print(f特征值: {ev.round(3)}) n_factors sum(ev 1) print(f根据Kaiser准则建议提取 {n_factors} 个因子。) # 进行因子分析提取2个因子方差最大旋转 fa FactorAnalyzer(n_factors2, rotationvarimax, methodml) fa.fit(df_scaled) loadings_fa fa.loadings_ loadings_fa_df pd.DataFrame(loadings_fa, indexdf_brand.columns, columns[fFactor{i1} for i in range(2)]) print(\n旋转后的因子载荷矩阵:) print(loadings_fa_df.round(3)) # 高亮载荷绝对值大于0.5的通常认为有显著负荷 def highlight_loadings(val): color red if abs(val) 0.5 else black return fcolor: {color} styled_df loadings_fa_df.style.applymap(highlight_loadings) print(\n红色表示载荷绝对值0.5) print(\n共同度因子解释的方差比例:) communality fa.get_communalities() for i, col in enumerate(df_brand.columns): print(f {col}: {communality[i]:.3f}) # 6. 对比PCA和FA的前两个维度 print(\n *60) print(PCA与FA结果对比前两个维度) print(*60) print(PCA主成分载荷PC1, PC2:) pca_loadings_2 loadings_pca[:, :2] print(pd.DataFrame(pca_loadings_2, indexdf_brand.columns, columns[PC1, PC2]).round(3)) print(\nFA因子载荷旋转后:) print(loadings_fa_df.round(3)) # 计算两个因子得分的相关系数理论上正交旋转后应接近0 fa_scores fa.transform(df_scaled) print(f\n两个因子得分的相关系数: {np.corrcoef(fa_scores[:,0], fa_scores[:,1])[0,1]:.4f})深度解读与对比运行上述代码我们可能会得到类似以下的发现PCA结果碎石图显示前两个主成分特征值大于1且累计解释方差约75%。载荷图显示PC1设计、续航、拍照、系统有较高的正载荷价格有中等负载荷。这像是一个“核心产品力”维度产品力强的手机往往价格也偏高。PC2服务、口碑有很高的正载荷价格有较强的负载荷。这像是一个“市场与服务”维度服务好、口碑佳的品牌可能给人以性价比高的感觉。 PCA成功地将7个指标压缩为2个不相关的综合指标可用于后续的消费者细分或回归建模。FA结果KMO0.8Bartlett检验显著适合做FA。提取2个因子旋转后得到清晰的简单结构Factor1在设计(0.85)、续航(0.72)、拍照(0.88)、系统(0.65)上有高载荷。这正是我们预设的“产品硬实力”因子。Factor2在服务(0.87)、口碑(0.80)上有高载荷在价格(-0.62)上有较高的负载荷。这正是“市场软实力”因子且软实力强的感知与价格承受度或性价比感知正相关。价格在Factor1上也有轻微负载荷(-0.32)这与“产品力强导致价格高”的常识相符。共同度显示大部分变量能被两个因子解释60%以上的方差模型拟合良好。PCA vs FA 对比洞察目标达成两者都成功将7个变量降维到2个。PCA的PC1/PC2与FA的Factor1/Factor2在含义上高度相似这很常见因为数据背后的潜在结构是相同的。关键区别PCA的PC1是方差最大的方向它混合了“产品力”和部分“价格”信息因为价格与产品力相关。而FA通过旋转更干净地将“价格”的方差分配给了与其更相关的“市场软实力”因子使得因子的含义更纯粹、更容易解释。这正是FA模型优势的体现它试图分离共同方差并通过旋转追求简单的理论结构。使用建议如果目标是构建一个“消费者综合评价指数”PCA的PC1得分可能是一个不错的候选。如果目标是理解品牌形象的两个独立维度并可能针对“产品硬实力”和“市场软实力”分别制定策略那么FA的因子得分提供了更清晰的视角。最后的经验分享在实际分析中我通常会先跑一遍PCA看看碎石图和累计贡献率快速了解数据大概有几个主要维度并检查是否有异常。如果降维是为了给聚类、分类模型做预处理我会直接用PCA的结果。如果分析目的是撰写报告、提出理论假设、或开发测量量表我会深入进行FA包括尝试不同因子数、不同旋转方法正交Varimax vs 斜交Promax并结合业务知识来确定最终因子结构。记住没有绝对的对错只有更适合当前分析目的的工具。最终报告图表时PCA的载荷图Biplot和FA的旋转后载荷矩阵都是非常有力、直观的可视化工具。
返回列表