根据用户的历史提问和本次问题“python因子分析代码”,我将提供一个完整的、包含心理学案例的因子分析(Factor Analysis)Python实现示例。本次回答将聚焦于探索性因子分析(EFA),涵盖其作用、数学基础、详细步骤和完整代码。
### 一、 因子分析的核心作用
因子分析旨在从一组观测变量中提取少数几个无法直接观测的潜在公共因子,以简化数据结构并解释变量间的相关性 [ref_2]。其主要作用可归纳如下:
| 作用 | 说明 |
| :--- | :--- |
| **数据降维** | 用少数几个因子(如2-3个)代表数十个观测变量,便于后续分析和可视化 [ref_3]。 |
| **揭示潜在结构** | 识别驱动多个观测变量的、不可直接测量的潜在构念(如“幸福感”、“焦虑水平”)。 |
| **变量归类** | 将高度相关的变量归为一类,形成清晰的因子维度,便于量表开发与修订。 |
| **减少共线性** | 在回归等模型中,使用提取的因子得分代替原始变量,可避免多重共线性问题。 |
在心理学中,因子分析是量表(如人格量表、智力量表、态度量表)编制和验证的核心工具。它帮助研究者验证理论结构(如大五人格的五个维度是否真的能从题目中提取出来),并筛选出最能代表潜在构念的题目 [ref_2]。
### 二、 因子分析的数学基础简述
因子分析模型假设每个观测变量 \(X_i\) 都可以表示为几个公共因子 \(F_j\) 和唯一因子 \(\epsilon_i\)(即误差或特殊因子)的线性组合 [ref_1]。
其数学模型为:
\[
X = \Lambda F + \epsilon
\]
其中:
* \(X\) 是 \(p \times 1\) 的观测变量向量。
* \(\Lambda\) 是 \(p \times m\) 的因子载荷矩阵(`factor loadings`),元素 \( \lambda_{ij} \) 表示第 \(i\) 个变量在第 \(j\) 个公共因子上的载荷,反映了变量与因子的相关性强度。
* \(F\) 是 \(m \times 1\) 的公共因子向量。
* \(\epsilon\) 是 \(p \times 1\) 的唯一因子向量。
模型求解的核心在于估计因子载荷矩阵 \(\Lambda\) 和唯一性方差。常用方法有**主成分法**和**最大似然法** [ref_1]。在得到初始载荷矩阵后,通常会进行**因子旋转**(如最大方差正交旋转 `Varimax` 或斜交旋转 `Promax`),使因子结构更清晰、更易于解释 [ref_2]。
### 三、 Python实现详细步骤与心理学案例
下面以一个心理学研究案例进行说明:假设我们有一份关于“工作满意度”的调查问卷,包含10个题目(变量),我们想探索其潜在的因子结构。
#### **步骤1:环境准备与数据加载**
首先导入必要的库,并加载或生成模拟数据。
```python
# 导入必要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from factor_analyzer import FactorAnalyzer
from factor_analyzer.factor_analyzer import calculate_bartlett_sphericity, calculate_kmo
import warnings
warnings.filterwarnings('ignore')
# 设置中文字体和图表样式
plt.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
sns.set(style="whitegrid")
# 生成模拟心理学问卷数据(10个题目,200份答卷)
np.random.seed(42) # 确保结果可复现
n_samples = 200
n_variables = 10
# 假设存在3个潜在因子:内在满意、外在满意、同事关系
# 为每个潜在因子定义其强相关的题目
latent_factor_1 = np.random.randn(n_samples) * 1.5 # 内在满意
latent_factor_2 = np.random.randn(n_samples) * 1.2 # 外在满意
latent_factor_3 = np.random.randn(n_samples) * 1.0 # 同事关系
# 根据潜在因子结构生成观测题目数据(加入噪声)
data_dict = {
'Q1': 0.8 * latent_factor_1 + 0.2 * latent_factor_2 + np.random.randn(n_samples) * 0.5, # 主要载荷在因子1
'Q2': 0.7 * latent_factor_1 + 0.1 * latent_factor_3 + np.random.randn(n_samples) * 0.6,
'Q3': 0.9 * latent_factor_1 + np.random.randn(n_samples) * 0.4,
'Q4': 0.1 * latent_factor_1 + 0.8 * latent_factor_2 + np.random.randn(n_samples) * 0.5, # 主要载荷在因子2
'Q5': 0.2 * latent_factor_1 + 0.75 * latent_factor_2 + np.random.randn(n_samples) * 0.55,
'Q6': 0.85 * latent_factor_2 + np.random.randn(n_samples) * 0.5,
'Q7': 0.1 * latent_factor_2 + 0.8 * latent_factor_3 + np.random.randn(n_samples) * 0.5, # 主要载荷在因子3
'Q8': 0.7 * latent_factor_3 + np.random.randn(n_samples) * 0.6,
'Q9': 0.6 * latent_factor_3 + 0.2 * latent_factor_1 + np.random.randn(n_samples) * 0.6,
'Q10': 0.5 * latent_factor_3 + 0.3 * latent_factor_2 + np.random.randn(n_samples) * 0.7,
}
df = pd.DataFrame(data_dict)
print("数据前5行预览:")
print(df.head())
print(f"\n数据形状:{df.shape}")
```
#### **步骤2:数据预处理**
因子分析通常要求数据满足多变量正态性且无多重共线性,因此需要进行标准化处理 [ref_1]。
```python
# 1. 标准化数据(消除量纲影响)
scaler = StandardScaler()
df_scaled = pd.DataFrame(scaler.fit_transform(df), columns=df.columns)
# 2. 计算相关矩阵
corr_matrix = df_scaled.corr()
plt.figure(figsize=(10, 8))
sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='coolwarm', center=0, square=True)
plt.title('观测变量相关系数矩阵热图')
plt.tight_layout()
plt.show()
```
#### **步骤3:因子分析适用性检验**
在进行因子分析前,必须检验数据是否适合。两个关键检验是**Bartlett球形检验**和**KMO检验** [ref_2]。
```python
# 1. Bartlett球形检验:检验相关矩阵是否为单位矩阵(若拒绝原假设,则适合做因子分析)
chi_square_value, p_value = calculate_bartlett_sphericity(df_scaled)
print(f"Bartlett球形检验:")
print(f" 卡方值: {chi_square_value:.2f}")
print(f" P值: {p_value:.4f}")
if p_value < 0.05:
print(" 结论: P值 < 0.05,拒绝原假设,数据适合进行因子分析。")
else:
print(" 结论: 数据可能不适合进行因子分析。")
# 2. KMO检验:检验变量间偏相关性是否足够小(KMO > 0.6 通常认为可以接受)
kmo_all, kmo_model = calculate_kmo(df_scaled)
print(f"\nKMO检验:")
print(f" 总体KMO值: {kmo_model:.4f}")
if kmo_model >= 0.8:
print(" 结论: 数据非常适合因子分析。")
elif kmo_model >= 0.7:
print(" 结论: 数据适合因子分析。")
elif kmo_model >= 0.6:
print(" 结论: 数据勉强适合因子分析。")
else:
print(" 结论: 数据不适合因子分析。")
# 检查每个变量的KMO值
kmo_per_variable = pd.DataFrame({'变量': df_scaled.columns, 'KMO值': kmo_all})
print("\n各变量KMO值:")
print(kmo_per_variable)
```
#### **步骤4:确定因子数量**
使用**特征值大于1准则(Kaiser准则)**和**碎石图**来辅助判断应提取的因子个数 [ref_1]。
```python
# 1. 使用主成分法初步提取因子,不旋转,以获取特征值
fa = FactorAnalyzer(n_factors=n_variables, rotation=None, method='ml') # 使用最大似然法,这里先设置因子数为变量数以获取所有特征值
fa.fit(df_scaled)
# 获取特征值和方差贡献率
ev, v = fa.get_eigenvalues()
print("特征值:", ev)
# 2. 绘制碎石图
plt.figure(figsize=(10, 6))
plt.plot(range(1, df_scaled.shape[1] + 1), ev, marker='o', linestyle='--')
plt.axhline(y=1, color='r', linestyle='-', linewidth=0.8) # 画出特征值=1的参考线
plt.title('因子分析碎石图 (Scree Plot)')
plt.xlabel('因子序号')
plt.ylabel('特征值')
plt.grid(True)
plt.show()
# 3. 根据特征值>1的准则确定因子数
n_factors = sum(ev > 1)
print(f"\n根据特征值>1准则 (Kaiser准则),建议提取的因子数量为: {n_factors}")
```
#### **步骤5:拟合因子分析模型并旋转**
根据确定的因子数量,拟合模型并进行最大方差正交旋转(Varimax),使因子载荷矩阵结构更简单 [ref_2]。
```python
# 使用确定好的因子数量,进行因子分析(使用最大似然法进行参数估计)
fa = FactorAnalyzer(n_factors=n_factors, rotation='varimax', method='ml')
fa.fit(df_scaled)
# 获取因子载荷矩阵
loadings = fa.loadings_
loadings_df = pd.DataFrame(loadings,
index=df.columns,
columns=[f'Factor{i+1}' for i in range(n_factors)])
print("旋转后的因子载荷矩阵:")
print(loadings_df.round(3))
# 可视化因子载荷矩阵(热图形式)
plt.figure(figsize=(8, 6))
sns.heatmap(loadings_df, annot=True, fmt='.2f', cmap='RdBu_r', center=0, vmin=-1, vmax=1, square=True)
plt.title('旋转后因子载荷矩阵热图')
plt.tight_layout()
plt.show()
```
#### **步骤6:模型评估与结果解释**
检查公因子方差(Communalities)、方差解释率,并为因子命名 [ref_2]。
```python
# 1. 获取公因子方差(每个变量能被公共因子解释的方差比例)
communalities = fa.get_communalities()
communalities_df = pd.DataFrame({'变量': df.columns, '公因子方差': communalities})
print("公因子方差(变量能被公共因子解释的程度):")
print(communalities_df.round(3))
# 2. 获取因子方差解释率(旋转后)
variance_df = pd.DataFrame({
'因子': [f'Factor{i+1}' for i in range(n_factors)],
'SS Loadings (特征值)': fa.get_factor_variance()[0],
'方差解释比例 (%)': fa.get_factor_variance()[1] * 100,
'累积方差解释比例 (%)': np.cumsum(fa.get_factor_variance()[1]) * 100
})
print("\n因子方差解释情况(旋转后):")
print(variance_df.round(3))
# 3. 因子命名与解释(基于载荷矩阵,通常认为 |载荷| > 0.4 或 0.5 的变量属于该因子)
factor_assignment = {}
for factor in loadings_df.columns:
# 找出在该因子上载荷绝对值大于0.4的变量
significant_vars = loadings_df.index[abs(loadings_df[factor]) > 0.4].tolist()
factor_assignment[factor] = significant_vars
print(f"\n{factor} 高载荷变量: {significant_vars}")
print(f" 载荷值: {loadings_df.loc[significant_vars, factor].values.round(3)}")
# 根据题目内容(模拟)为因子命名
factor_names = {
'Factor1': '内在满意度(对工作内容本身的兴趣和价值感)',
'Factor2': '外在满意度(薪酬、福利、晋升机会)',
'Factor3': '同事关系质量(团队氛围、沟通合作)'
}
print("\n=== 因子命名与心理学解释 ===")
for fac, name in factor_names.items():
if fac in factor_assignment:
print(f"{fac}: {name}")
print(f" 代表性题目: {factor_assignment[fac]}")
```
#### **步骤7:计算因子得分(可选)**
如果需要将样本在潜在因子上的得分用于后续分析(如回归、聚类),可以计算因子得分 [ref_4]。
```python
# 计算因子得分(有多种方法,如回归法)
factor_scores = fa.transform(df_scaled)
factor_scores_df = pd.DataFrame(factor_scores,
columns=[f'{factor_names.get(col, col)}_得分' for col in loadings_df.columns])
print("部分样本的因子得分(前5个):")
print(factor_scores_df.head().round(3))
# 可以将因子得分与原始数据合并
df_with_scores = pd.concat([df, factor_scores_df], axis=1)
```
### 四、 关键输出与心理学意义解读
通过上述代码,我们可以获得以下核心结果,并对其心理学意义进行解读:
1. **因子载荷矩阵**:这是解读的核心。例如,如果 `Q1`、`Q2`、`Q3` 在 `Factor1` 上有高载荷(>0.7),且这些题目内容都是关于“工作挑战性”、“成就感”、“兴趣匹配”,那么我们可以将 `Factor1` 命名为 **“内在动机”** 因子。这验证了量表设计是否成功测量了理论构念 [ref_2]。
2. **公因子方差**:它反映了每个观测变量(问卷题目)能被所有公共因子共同解释的方差比例。在量表修订中,公因子方差过低的题目(如 < 0.4)可能测量误差太大或与量表整体潜在结构关系不大,应考虑删除 [ref_1]。
3. **方差解释率**:表明提取的几个公共因子总共能解释原始数据总方差的多少。在心理学研究中,累积方差解释率达到 **60% 以上**通常被认为是比较理想的,说明提取的因子较好地概括了原始信息 [ref_3]。
4. **因子得分**:它将每个受访者的回答“投射”到几个潜在因子上,得到连续数值。这些得分可以作为新的、互不相关的变量,用于预测个体在某种心理特质(如抑郁倾向、领导力)上的表现,或者用于对不同心理特征的群体进行细分和比较 [ref_4]。
### 五、 总结与注意事项
本次演示完整展示了使用 `factor_analyzer` 库在 Python 中执行探索性因子分析的全流程,并以心理学问卷数据为例。关键步骤包括:数据准备、适用性检验、确定因子数、模型拟合与旋转、结果解释和得分计算。
进行因子分析时需注意:
* **样本量**:通常要求样本数至少是变量数的 **5-10 倍**,本次模拟的 200 个样本对 10 个变量是充分的。
* **变量相关性**:原始变量间应存在适度相关性(KMO检验和Bartlett检验通过),完全独立或共线性过强的数据都不适合 [ref_2]。
* **因子旋转选择**:正交旋转(如 `Varimax`)假设因子间不相关,简化解释;若理论上因子相关,则可选用斜交旋转(如 `