本文目录导读:

- 单因素方差分析(One-way ANOVA)
- 双因素方差分析(Two-way ANOVA)
- 重复测量ANOVA(Repeated Measures ANOVA)
- 方差分析前提假设检验
- 非参数替代方法(Kruskal-Wallis检验)
- 可视化结果
- 完整ANOVA分析流程示例
- 关键注意事项
我来详细介绍如何使用Statsmodels进行方差分析(ANOVA)。
单因素方差分析(One-way ANOVA)
准备数据和基本ANOVA
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
# 创建示例数据:三种不同肥料对植物生长的影响
np.random.seed(42)
# 生成数据
fertilizer_A = np.random.normal(20, 3, 30) # 肥料A
fertilizer_B = np.random.normal(25, 3, 30) # 肥料B
fertilizer_C = np.random.normal(22, 3, 30) # 肥料C
# 创建DataFrame
df = pd.DataFrame({
'growth': np.concatenate([fertilizer_A, fertilizer_B, fertilizer_C]),
'fertilizer': ['A']*30 + ['B']*30 + ['C']*30
})
print("数据预览:")
print(df.head())
print(f"\n各组统计量:")
print(df.groupby('fertilizer')['growth'].describe())
执行单因素ANOVA
# 方法1:使用ols模型
model = ols('growth ~ C(fertilizer)', data=df).fit()
anova_results = anova_lm(model)
print("单因素方差分析结果:")
print(anova_results)
# 方法2:直接从statsmodels获取
from statsmodels.stats.oneway import oneway
# 准备数据
groups = [df[df['fertilizer'] == g]['growth'].values for g in ['A', 'B', 'C']]
# 执行One-way ANOVA
f_stat, p_val, means = oneway(groups, use_var='equal')
print(f"\n直接One-way ANOVA结果:")
print(f"F统计量: {f_stat:.4f}")
print(f"p值: {p_val:.4f}")
事后检验(Post-hoc tests)
from statsmodels.stats.multicomp import pairwise_tukeyhsd
# Tukey HSD事后检验
tukey = pairwise_tukeyhsd(df['growth'], df['fertilizer'], alpha=0.05)
print("\nTukey HSD事后检验结果:")
print(tukey)
# 可视化事后检验结果
tukey.plot_simultaneous()'Tukey HSD检验结果')
plt.show()
双因素方差分析(Two-way ANOVA)
# 创建双因素数据:肥料类型和土壤类型对植物生长的影响
np.random.seed(42)
# 生成数据
data = []
fertilizers = ['A', 'B', 'C']
soils = ['clay', 'sandy', 'loamy']
for fert in fertilizers:
for soil in soils:
# 不同组合有不同的均值
if fert == 'A':
mean = 20 if soil == 'clay' else 22 if soil == 'sandy' else 24
elif fert == 'B':
mean = 25 if soil == 'clay' else 23 if soil == 'sandy' else 27
else: # C
mean = 22 if soil == 'clay' else 24 if soil == 'sandy' else 26
growth = np.random.normal(mean, 2, 10)
for g in growth:
data.append({'growth': g, 'fertilizer': fert, 'soil': soil})
df2 = pd.DataFrame(data)
print("双因素数据预览:")
print(df2.head())
print(f"\n数据形状: {df2.shape}")
执行双因素ANOVA
# 执行双因素ANOVA(考虑交互作用)
model2 = ols('growth ~ C(fertilizer) + C(soil) + C(fertilizer):C(soil)', data=df2).fit()
anova_results2 = anova_lm(model2)
print("双因素方差分析结果:")
print(anova_results2)
# 如果交互作用不显著,可以使用加性模型(无交互)
model2_additive = ols('growth ~ C(fertilizer) + C(soil)', data=df2).fit()
anova_results2_additive = anova_lm(model2_additive)
print("\n加性模型(无交互)方差分析结果:")
print(anova_results2_additive)
重复测量ANOVA(Repeated Measures ANOVA)
# 创建重复测量数据
np.random.seed(42)
# 10个被试,在3个时间点的测量
subjects = 10
times = 3
data_rm = []
for subject in range(subjects):
# 每个被试的基线水平不同
baseline = np.random.normal(50, 10)
for time in range(times):
# 随时间变化
measurement = baseline + time * 5 + np.random.normal(0, 5)
data_rm.append({
'subject': f'Subject_{subject+1}',
'time': f'Time_{time+1}',
'measurement': measurement
})
df_rm = pd.DataFrame(data_rm)
# 使用混合模型进行重复测量ANOVA
from statsmodels.stats.anova import AnovaRM
# 执行重复测量ANOVA
rm_anova = AnovaRM(df_rm, 'measurement', 'subject', within=['time'])
rm_results = rm_anova.fit()
print("重复测量方差分析结果:")
print(rm_results)
方差分析前提假设检验
# 1. 正态性检验(Shapiro-Wilk test)
print("正态性检验 (Shapiro-Wilk):")
for fert in ['A', 'B', 'C']:
stat, p_value = stats.shapiro(df[df['fertilizer'] == fert]['growth'])
print(f"{fert}: 统计量={stat:.4f}, p值={p_value:.4f}")
# 2. 方差齐性检验(Levene's test)
from scipy import stats as scipy_stats
groups_data = [df[df['fertilizer'] == g]['growth'].values for g in ['A', 'B', 'C']]
levene_stat, levene_p = scipy_stats.levene(*groups_data)
print(f"\n方差齐性检验 (Levene):")
print(f"统计量={levene_stat:.4f}, p值={levene_p:.4f}")
# 3. 残差分析
model = ols('growth ~ C(fertilizer)', data=df).fit()
residuals = model.resid
fitted = model.fittedvalues
# Q-Q图检验正态性
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# Q-Q图
stats.probplot(residuals, dist="norm", plot=axes[0])
axes[0].set_title('Q-Q图 - 残差正态性检验')
# 残差vs拟合值图
axes[1].scatter(fitted, residuals, alpha=0.7)
axes[1].axhline(y=0, color='r', linestyle='--')
axes[1].set_xlabel('拟合值')
axes[1].set_ylabel('残差')
axes[1].set_title('残差 vs 拟合值')
plt.tight_layout()
plt.show()
# 4. 对非正态数据进行变换
from scipy import stats as scipy_stats
# Box-Cox变换
df['growth_boxcox'], lambda_val = scipy_stats.boxcox(df['growth'])
print(f"\nBox-Cox变换的lambda值: {lambda_val:.4f}")
# 变换后的正态性检验
for fert in ['A', 'B', 'C']:
stat, p_value = scipy_stats.shapiro(df[df['fertilizer'] == fert]['growth_boxcox'])
print(f"变换后 {fert}: 统计量={stat:.4f}, p值={p_value:.4f}")
非参数替代方法(Kruskal-Wallis检验)
from scipy.stats import kruskal
# 当方差分析假设不满足时的非参数替代
h_stat, p_value = kruskal(*groups_data)
print("Kruskal-Wallis非参数检验结果:")
print(f"H统计量: {h_stat:.4f}")
print(f"p值: {p_value:.4f}")
# 事后比较(Dunn检验)
from scikit_posthocs import posthoc_dunn
# 准备数据
df_dunn = df.copy()
# 执行Dunn检验
dunn_results = posthoc_dunn(df_dunn, val_col='growth', group_col='fertilizer', p_adjust='bonferroni')
print("\nDunn事后检验结果:")
print(dunn_results)
可视化结果
# 创建综合可视化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
# 1. 箱线图
sns.boxplot(x='fertilizer', y='growth', data=df, ax=axes[0, 0])
axes[0, 0].set_title('各肥料组生长情况分布')
# 2. 小提琴图
sns.violinplot(x='fertilizer', y='growth', data=df, ax=axes[0, 1])
axes[0, 1].set_title('分布密度图')
# 3. 点图+误差线
sns.pointplot(x='fertilizer', y='growth', data=df,
ci=95, capsize=0.2, ax=axes[0, 2])
axes[0, 2].set_title('均值与95%置信区间')
# 4. 双因素交互作用图
sns.pointplot(x='fertilizer', y='growth', hue='soil', data=df2,
ci=95, capsize=0.2, ax=axes[1, 0])
axes[1, 0].set_title('双因素交互作用图')
# 5. 频率分布直方图
for i, fert in enumerate(['A', 'B', 'C']):
data_filtered = df[df['fertilizer'] == fert]['growth']
axes[1, 1].hist(data_filtered, alpha=0.5, label=f'肥料{fert}', bins=10)
axes[1, 1].set_title('分布直方图')
axes[1, 1].legend()
# 6. 热力图显示组间差异
means_matrix = df2.pivot_table(values='growth', index='fertilizer', columns='soil', aggfunc='mean')
sns.heatmap(means_matrix, annot=True, fmt='.1f', cmap='YlOrRd', ax=axes[1, 2])
axes[1, 2].set_title('各组均值热力图')
plt.tight_layout()
plt.show()
完整ANOVA分析流程示例
def complete_anova_analysis(data, dependent_var, group_var, alpha=0.05):
"""
完整的方差分析函数
Parameters:
-----------
data : DataFrame
dependent_var : str, 因变量
group_var : str, 分组变量
alpha : float, 显著性水平
"""
results = {}
# 1. 描述性统计
results['descriptive'] = data.groupby(group_var)[dependent_var].describe()
# 2. 执行ANOVA
formula = f'{dependent_var} ~ C({group_var})'
model = ols(formula, data=data).fit()
anova_table = anova_lm(model)
results['anova'] = anova_table
# 3. 假设检验
# 正态性
shapiro_results = {}
for group in data[group_var].unique():
stat, p = scipy_stats.shapiro(data[data[group_var] == group][dependent_var])
shapiro_results[group] = {'statistic': stat, 'p_value': p}
results['normality'] = shapiro_results
# 方差齐性
groups_list = [data[data[group_var] == g][dependent_var].values
for g in data[group_var].unique()]
levene_stat, levene_p = scipy_stats.levene(*groups_list)
results['homogeneity'] = {'statistic': levene_stat, 'p_value': levene_p}
# 4. 如果需要,执行事后检验
if anova_table['Pr(>F)'][0] < alpha:
tukey = pairwise_tukeyhsd(data[dependent_var], data[group_var], alpha=alpha)
results['posthoc'] = tukey
return results
# 使用示例
results = complete_anova_analysis(df, 'growth', 'fertilizer')
print("描述性统计:")
print(results['descriptive'])
print("\n方差分析结果:")
print(results['anova'])
print("\n正态性检验:")
for group, stats in results['normality'].items():
print(f"{group}: p值={stats['p_value']:.4f}")
print(f"\n方差齐性检验: p值={results['homogeneity']['p_value']:.4f}")
关键注意事项
- 假设检验重要:在进行ANOVA前,始终检查正态性和方差齐性
- 事后检验:如果ANOVA显著,需要进行事后检验来确定具体哪些组有差异
- 多重比较校正:进行多项比较时要校正p值(如Bonferroni校正)
- 交互作用:在双因素ANOVA中,先检查交互作用是否显著
- 样本量:各组的样本量应尽量均衡
这些代码涵盖了使用Statsmodels进行方差分析的主要方法,从基本的一因素到复杂的重复测量设计。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播