外观
卡方检验
列联表卡方独立性检验。
核心代码
py
def _fit_chisq_independence(self, df: pd.DataFrame, decimals: int, title: str) -> None:
"""
执行卡方独立性检验
参数:
df: 数据框
decimals: 小数位数
title: 表格标题
"""
# 获取检验参数
alpha = self.chisq_options.get('alpha', 0.05)
show_expected = self.chisq_options.get('show_expected', False)
# 检查变量(独立性检验需要恰好两个变量)
if not self.x_vars or len(self.x_vars) < 2:
raise ValueError(_('卡方独立性检验需要至少选择两个分类变量。'))
# 如果选择了多个变量,进行两两独立性检验
results = []
contingency_tables = []
# 生成所有可能的配对组合
from itertools import combinations
pairs = list(combinations(self.x_vars, 2))
for var1, var2 in pairs:
if var1 not in df.columns or var2 not in df.columns:
continue
# 获取数据并删除缺失值
df_clean = df[[var1, var2]].dropna()
if len(df_clean) < 2:
continue
# 构建列联表
contingency_table = pd.crosstab(df_clean[var1], df_clean[var2])
# 执行卡方检验
chi2_stat, p_value, dof, expected_freq = chi2_contingency(contingency_table)
# 计算Cramér's V(效应量)
n = contingency_table.sum().sum()
min_dim = min(contingency_table.shape[0] - 1, contingency_table.shape[1] - 1)
cramers_v = np.sqrt(chi2_stat / (n * min_dim)) if min_dim > 0 else 0
# 判断显著性
if p_value < 0.01:
stars = "***"
elif p_value < 0.05:
stars = "**"
elif p_value < 0.1:
stars = "*"
else:
stars = ""
results.append({
'Variable 1': var1,
'Variable 2': var2,
'N': int(n),
'Chi-square': f"{chi2_stat:.{decimals}f}{stars}",
'df': int(dof),
'P-value': f"{p_value:.{decimals}f}",
"Cramér's V": f"{cramers_v:.{decimals}f}"
})
# 如果需要显示期望频数,保存列联表和期望频数
if show_expected:
contingency_tables.append({
'var1': var1,
'var2': var2,
'observed': contingency_table,
'expected': pd.DataFrame(
expected_freq,
index=contingency_table.index,
columns=contingency_table.columns
)
})
if not results:
raise ValueError(_('没有可用的数据进行卡方检验。'))
# 生成HTML表格
results_df = pd.DataFrame(results)
title = title if title else _('卡方独立性检验')
note = _("H0: 两变量相互独立; 显著性水平: α=%(alpha)s; Cramér's V: 效应量 (0-1); *** p<0.01, ** p<0.05, * p<0.1") % {'alpha': alpha}
# 生成主结果表格
main_html = self._generate_chisq_html(results_df, title, note, decimals)
# 如果需要显示期望频数,生成列联表和期望频数表格
if show_expected and contingency_tables:
contingency_html = ""
for ct_info in contingency_tables:
var1 = ct_info['var1']
var2 = ct_info['var2']
observed = ct_info['observed']
expected = ct_info['expected']
# 生成观测频数表格
obs_title = _('列联表: %(var1)s × %(var2)s (观测频数)') % {'var1': var1, 'var2': var2}
obs_html = self._generate_contingency_table_html(observed, obs_title, decimals)
# 生成期望频数表格
exp_title = _('列联表: %(var1)s × %(var2)s (期望频数)') % {'var1': var1, 'var2': var2}
exp_html = self._generate_contingency_table_html(expected, exp_title, decimals)
contingency_html += obs_html + exp_html
self.custom_html = main_html + contingency_html
else:
self.custom_html = main_html