Log-Linear Analysis
The engine for Multi-Way Categorical Discovery. Log-Linear Analysis audits the complex network of associations between categorical variables in multi-dimensional tables, seeking the 'Best-Fit' hierarchy of synergy.
What is it?
Log-Linear Analysis models contingency tables involving multiple categorical variables, evaluating multi-way interactions on logarithmic probability scales.
When to use it
- Multiple Categories: Outcome variable is count frequencies in combinations of factors.
- 3-Way Interactions: Evaluate if associations change across categories.
Categorical Log-Linear Interaction Laboratory
Adjust interaction coefficient size to see observed vs expected category deviations.
| Metric | Value |
|---|---|
| Likelihood Chi-Square | 6.6667 |
| p-value | 0.0098 |
Hypotheses
Pragmatic null and alternative hypotheses defined in mathematical notation.
H₀: Model of independence (or specified structure) fits the data (no k-way interaction)
Hₐ: Model does not fit; specified interaction(s) exist
For saturated model: H₀: all k-way interactions = 0. For independence model: H₀: all associations = 0 (variables independent). For partial associations: test specific λ parameters (interaction terms). Loglinear models are symmetric: no distinction between DV and IVs, unlike logistic regression. Log of expected cell frequencies modeled as linear combination of effects.
Assumptions
The core mathematical criteria needed to ensure that statistical testing remains unbiased and valid.
Diagnostics
Checking residual plots and indices to examine model deviations and ensure standard error integrity.
- Likelihood ratio chi-square (G²) for overall model fit
- Pearson chi-square (X²) as alternative fit statistic
- Standardized residuals (|r| > 2 suggests poor fit in cell)
- AIC/BIC for model comparison (lower is better)
- Deviance residuals to identify poorly fitted cells
- Compare multiple models (independence, main effects, two-way interactions, saturated)
- Examine adjusted residuals (> |2| indicates significant cell contribution to misfit)
- Plot mosaic plots or association plots for visualizing patterns
- Check for outlier cells (large residuals)
- Calculate λ parameters (log-linear effects) with confidence intervals
- Odds ratios for specific associations
- Test specific contrasts or simple effects if interactions present
Applied Minds
Review concrete study examples, data layout guidelines, and copy executable syntax scripts.
Yoga Type × Injury Status × Experience Level (3-Way Contingency Table)
Research question: Are yoga-related injuries associated with yoga type and practitioner experience level? Design: Cross-sectional survey (n=600). Variables: Yoga type (Hatha, Vinyasa, Restorative), Injury past year (No, Yes), Experience (Beginner, Intermediate, Advanced). Test all two-way and three-way associations.
# Loglinear Analysis: 3-Way Contingency Table
# Yoga Type × Injury × Experience Level
library(MASS) # For loglm()
library(vcd) # For mosaic plots and association plots
library(vcdExtra) # For extended loglinear tools
set.seed(2025)
# Simulate realistic data
n <- 600
yoga_type <- sample(c("Hatha", "Vinyasa", "Restorative"), n, replace=TRUE, prob=c(0.4, 0.4, 0.2))
experience <- sample(c("Beginner", "Intermediate", "Advanced"), n, replace=TRUE, prob=c(0.35, 0.45, 0.2))
# Injury depends on yoga type and experience (interaction)
# Hatha + Beginner = higher injury risk
injury_prob <- 0.10 +
0.15*(yoga_type=="Hatha") +
0.10*(yoga_type=="Vinyasa") -
0.05*(experience=="Intermediate") -
0.10*(experience=="Advanced") +
0.12*(yoga_type=="Hatha" & experience=="Beginner")
injury <- rbinom(n, 1, pmin(injury_prob, 0.6))
injury <- factor(injury, levels=c(0, 1), labels=c("No", "Yes"))
# Create contingency table
data <- data.frame(yoga_type, injury, experience)
contingency_table <- xtabs(~ yoga_type + injury + experience, data=data)
cat("=== Three-Way Contingency Table ===\n")
print(contingency_table)
cat("\nTotal n:", sum(contingency_table), "\n")
cat("Number of cells:", length(contingency_table), "\n\n")
# === STEP 1: Test Independence (Mutual Independence Model) ===
# Model 1: Mutual independence [Y][I][E]
model_indep <- loglm(~ yoga_type + injury + experience, data=contingency_table)
summary(model_indep)
cat("\n=== Model 1: Mutual Independence ===\n")
cat("G² =", model_indep$lrt, ", df =", model_indep$df, ", p =", model_indep$prob, "\n")
if(model_indep$prob < 0.05) {
cat("Model does NOT fit(p < .05): variables are associated\n")
} else {
cat("Model fits(p > .05): mutual independence supported\n")
}
# === STEP 2: All Two-Way Associations [YI][YE][IE] ===
model_2way <- loglm(~ yoga_type*injury + yoga_type*experience + injury*experience,
data=contingency_table)
summary(model_2way)
cat("\n=== Model 2: All Two-Way Associations ===\n")
cat("G² =", model_2way$lrt, ", df =", model_2way$df, ", p =", model_2way$prob, "\n")
cat("AIC:", model_2way$lrt - 2*model_2way$df, "\n")
# === STEP 3: Saturated Model (includes three-way interaction) [YIE] ===
model_sat <- loglm(~ yoga_type*injury*experience, data=contingency_table)
summary(model_sat)
cat("\n=== Model 3: Saturated(Three-Way Interaction) ===\n")
cat("G² =", model_sat$lrt, ", df =", model_sat$df, "\n")
cat("(Saturated model always fits perfectly)\n")
# === STEP 4: Model Comparison ===
cat("\n=== Model Comparison ===\n")
models <- data.frame(
Model = c("Mutual Independence", "All 2-Way", "Saturated"),
G2 = c(model_indep$lrt, model_2way$lrt, model_sat$lrt),
df = c(model_indep$df, model_2way$df, model_sat$df),
p_value = c(model_indep$prob, model_2way$prob, NA),
AIC = c(model_indep$lrt - 2*model_indep$df,
model_2way$lrt - 2*model_2way$df,
model_sat$lrt - 2*model_sat$df)
)
print(models)
cat("\nBest model(lowest AIC):")
best_model <- models$Model[which.min(models$AIC)]
cat(best_model, "\n\n")
# === STEP 5: Test Specific Associations ===
# Test if three-way interaction is needed (compare 2-way vs saturated)
G2_diff <- model_2way$lrt - model_sat$lrt
df_diff <- model_2way$df - model_sat$df
p_diff <- 1 - pchisq(G2_diff, df_diff)
cat("=== Test Three-Way Interaction ===\n")
cat("ΔG² =", G2_diff, ", Δdf =", df_diff, ", p =", round(p_diff, 4), "\n")
if(p_diff < 0.05) {
cat("Three-way interaction significant(p < .05)\n")
cat("Injury × Yoga × Experience interaction exists\n\n")
} else {
cat("Three-way interaction not significant(p > .05)\n")
cat("Two-way associations model is adequate\n\n")
}
# === STEP 6: Examine Residuals ===
# Standardized residuals from best-fitting model
resid_model <- model_2way
std_resids <- residuals(resid_model, type="pearson")
cat("=== Standardized Residuals(|r| > 2 indicates poor fit) ===\n")
print(round(std_resids, 2))
large_resids <- which(abs(std_resids) > 2, arr.ind=TRUE)
if(nrow(large_resids) > 0) {
cat("\nCells with |residual| > 2:\n")
print(large_resids)
} else {
cat("\nNo cells with large residuals(model fits well)\n")
}
# === STEP 7: Odds Ratios for Key Associations ===
# Yoga Type × Injury association (collapsed across experience)
yoga_injury_table <- margin.table(contingency_table, c(1,2))
cat("\n=== Yoga Type × Injury Table ===\n")
print(yoga_injury_table)
# Odds ratios (Hatha vs Restorative for injury)
OR_hatha_rest <- (yoga_injury_table["Hatha","Yes"] * yoga_injury_table["Restorative","No"]) /
(yoga_injury_table["Hatha","No"] * yoga_injury_table["Restorative","Yes"])
cat("\nOdds Ratio(Hatha vs Restorative for Injury):", round(OR_hatha_rest, 2), "\n")
cat("Interpretation: Hatha practitioners have", round(OR_hatha_rest, 2),
"times the odds of injury compared to Restorative practitioners\n")
# === STEP 8: Visualization ===
# Mosaic plot
library(vcd)
mosaic(~ yoga_type + experience + injury, data=data,
shade=TRUE, legend=TRUE,
main="Mosaic Plot: Yoga Type × Experience × Injury",
labeling=labeling_border(rot_labels=c(45,0,0,0)))
# Association plot (shows residuals)
assoc(contingency_table, shade=TRUE,
main="Association Plot: Standardized Residuals")
# Conditional plot: Injury by Yoga Type, stratified by Experience
library(ggplot2)
ggplot(data, aes(x=yoga_type, fill=injury)) +
geom_bar(position="fill") +
facet_wrap(~ experience) +
scale_fill_manual(values=c("#1a9850", "#d73027"),
labels=c("No Injury", "Injury")) +
labs(title="Injury Rates by Yoga Type and Experience Level",
x="Yoga Type", y="Proportion", fill="Injury Status") +
theme_classic() +
theme(axis.text.x=element_text(angle=45, hjust=1))
# === APA-Style Reporting ===
cat("\n=== APA Report ===\n")
cat("A loglinear analysis examined associations among yoga type(Hatha, Vinyasa,\n")
cat("Restorative), injury status, and experience level in a 3×2×3 contingency\n")
cat("table(n=600). The mutual independence model did not fit(G²=XX.X, df=XX,\n")
cat("p<.001), indicating variables were associated. The model with all two-way\n")
cat("associations fit adequately(G²=X.X, df=X, p=.XX, AIC=XX) and was preferred\n")
cat("over the saturated model(ΔAIC=X.X). The three-way interaction was not\n")
cat("significant(ΔG²=X.X, Δdf=X, p=.XX), suggesting associations did not differ\n")
cat("across experience levels. Key findings: Hatha yoga was associated with higher\n")
cat("injury risk compared to Restorative(OR=2.3, 95% CI [1.5, 3.6]). Beginners\n")
cat("showed higher injury rates across all yoga types(OR=1.8 vs Advanced,\n")
cat("p<.01). Results suggest injury prevention efforts should target Hatha\n")
cat("practitioners, especially beginners.\n")Loglinear analysis revealed significant associations among yoga type, injury status, and experience level. Mutual independence model was rejected (G²=45.3, p<.001), indicating variables are not independent. Model with all two-way associations fit adequately (G²=3.2, df=4, p=.52, AIC=31.2) and was preferred (lowest AIC). Three-way interaction was not significant (ΔG²=3.2, p=.52), suggesting associations between yoga type and injury were similar across experience levels. Key findings: (1) Yoga Type × Injury: Hatha associated with higher injury odds (OR=2.3 vs Restorative). (2) Experience × Injury: Beginners had 1.8× higher injury odds than Advanced. (3) Yoga Type × Experience: Beginners disproportionately practice Hatha. Results suggest injury prevention should target Hatha beginners specifically.
Alternatives
Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.
- Category Collapsing — Merge adjacent thin levels to stabilize the G-Square fit.
- Monte Carlo Chi-Square — resample the null distribution to protect significance in sparse grids.
- Penalized Log-Linear (L1) — Apply Lasso shrinkage to select only the most robust interactions.
- Multinomial Logistic Regression — Pivot to asymmetric modeling if one variable is the 'Outcome' and others are 'Predictors'.
- Poisson Regression — Treat the cell counts as the dependent variable if exposure varies across the grid.
Post-hoc
Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.
Post-hoc pairwise tests defined for this model.
No specific guidelines provided.
Effect Size
Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.
OR for 2×2 subtables. OR=1: no association. OR>1: positive association. OR<1: negative association. Report with 95% CI. Convert to log-odds for additive interpretation.
Log-linear effect parameters (λ). λ=0: no effect. λ>0: positive effect on log cell frequency. λ<0: negative effect. Exponentiate for multiplicative interpretation on cell counts.
Pseudo-R² based on G²: R²_LR = (G²_null - G²_model) / G²_null. Values 0.20-0.40 considered moderate to good fit for categorical models.
0.2
0.5
0.8
Sample Size
Guidelines for minimum sample requirements and power analysis parameters.
The 'Cell-Symmetry' Minimum: A minimum of 5 participants per individual cell in the multi-way grid is required. Categorical interactions collapse mathematically if grid sparsity is too high to stabilize the G-Square math.
| Effect Size | Parameters | Required n |
|---|---|---|
| Small Effect | w=0.10 (Small) | n ≈ 1200 |
| Medium Effect | w=0.30 (Medium) | n ≈ 150 |
| Large Effect | w=0.50 (Large) | n ≈ 50 |
The 'Parsimony Buffer': In large grids, 'Zero-Cells' are the silent killers of discovery. If your grid has more than 20% empty cells, your power is a phantom. Recruit for 'Grid Saturation' to maintain the integrity of the hierarchy audit.
Reporting
How to compile statistical results into publication prose matching APA and journal style guides.
A loglinear analysis examined associations among list variables in a I×J×K contingency table (n=N). If tested The mutual independence model was rejected (G²=value, df=value, p<.XXX), indicating variables were associated. Model fitting The model specify: e.g., with all two-way associations fit the data adequately (G²=value, df=value, p=.XXX, AIC=value) and was preferred over comparison model (ΔAIC=value). If tested three-way interaction The three-way interaction was/was not significant (ΔG²=value, Δdf=value, p=.XXX). Key associations Specifically, Variable A and Variable B were significantly associated (OR=value, 95% CI X.XX, X.XX), indicating substantive interpretation. Conclude Findings suggest theoretical/practical implications.
- Sample size and table dimensions (I×J×K)
- G² (likelihood ratio chi-square) and df for each model tested
- p-values for model fit tests
- AIC/BIC for model comparison
- Specific test statistics for key associations or interactions (ΔG², Δdf, p)
- Odds ratios with 95% CI for substantively important associations
- Statement about which model was selected and why
- Any violations of assumptions (sparse cells) and remedies
Manuscript Lab
Copy standard summary tables and forensic reporting grids to outline analysis details.
| Model Term | df | L.R. χ² (G²) | p | Result |
|---|---|---|---|---|
| Main Effects (G+R+O) | 6 | 145.2 | < .001 | Significant |
| First-Order Int. (G*R + G*O + R*O) | 12 | 45.1 | < .001 | Significant |
| Saturated (G*R*O) | 4 | 2.1 | .718 | Parity (Good Fit) |
The Deviance Meter. Measures how well the model predicts the frequency counts in each cell of the multi-way table.
The Perfect Model. Includes every possible interaction. We compare simpler models to the saturated model to find the most 'parsimonious' fit.
Command Center
Syntax libraries and function parameters for executing calculations in stats packages.
# 1. Fit Log-Linear Model
model <- loglm(~ Group * Response * Outcome, data = table_data)
# 2. Extract Partial Associations
summary(model)Log-linear analysis is the multidimensional version of the Chi-Square test. Use it when you have 3 or more categorical variables and want to find where the interactions live.
# Visualize Multi-way Interactions (Mosaic Plot)
vcd::mosaic(~ Group + Response + Outcome, data = table_data)Common Mistakes
Analytical caveats and corrections to maintain modeling integrity.
References
Scholarly lineage and citation keys grounding the statistical framework.