result = compare(m_null, m_full)
lr_stat = float(result.filter(pl.col("chi2").is_not_null())["chi2"][0])
lr_pvalue = float(result.filter(pl.col("p_value").is_not_null())["p_value"][0])
assert np.isclose(lr_stat, scipy_chi2.statistic, rtol=0.5), f"stat mismatch: {lr_stat} vs {scipy_chi2.statistic}"
# LR and Pearson chi-square p-values converge asymptotically; for small counts they can differ
# but both should agree on significance
assert (lr_pvalue < 0.05) == (scipy_chi2.pvalue < 0.05), f"Significance mismatch: {lr_pvalue} vs {scipy_chi2.pvalue}"
Independence means no interaction term. The LRT comparing the main-effects model (logμij=β0+αi+γj) to the saturated model tests H0:(αγ)ij=0 for all i,j.
# Null model (independence = main effects only)
m_null = model("count ~ species + sex", contingency, family="poisson").fit()
# Full model (saturated, with interaction)
m_full = model("count ~ species * sex", contingency, family="poisson").fit()
compare(m_null, m_full)
Loading...
result = compare(m_null, m_full)
lr_stat = float(result.filter(pl.col("chi2").is_not_null())["chi2"][0])
lr_pvalue = float(result.filter(pl.col("p_value").is_not_null())["p_value"][0])
assert np.isclose(lr_stat, scipy_indep.statistic, rtol=0.5), f"stat mismatch: {lr_stat} vs {scipy_indep.statistic}"
assert np.isclose(lr_pvalue, scipy_indep.pvalue, rtol=0.5), f"p mismatch: {lr_pvalue} vs {scipy_indep.pvalue}"
# Three-way table: species × island × sex
counts_3way = penguins.group_by("species", "island", "sex").agg(pl.len().alias("count"))
m = model("count ~ species + island + sex", counts_3way, family="poisson").fit().infer()
m.params.select("term", "estimate", "p_value")
Loading...
assert m.params.shape[0] > 1, "Expected multiple coefficients"
assert all(m.params["p_value"].to_numpy() < 1.0), "All p-values should be valid"