Cohen's Kappa (κ)
The engine for Categorical Agreement. Cohen’s Kappa (κ) audits the consistency between two raters, reveal the true 'Consensus Signal' after mathematically neutralizing the influence of random guessing.
What is it?
Cohen's Kappa (κ) quantifies the degree of agreement, consistency, or concordance among raters, measurements, or scale items.
The engine for Categorical Agreement. Cohen’s Kappa (κ) audits the consistency between two raters, reveal the true 'Consensus Signal' after mathematically neutralizing the influence of random guessing.
Goals & Indications
- Consensus Audit: Determine the degree of true agreement between two observers on a categorical outcome.
- Chance Neutralization: Mathematically subtract the agreement that would occur by pure random coincidence.
- Categorical Precision: Quantify the reliability of clinical diagnoses or classification systems.
Core Idea Diagram
Claims tested
How it works
- Cross-tabulate ratings from two independent raters into a 2x2 contingency table.
- Calculate observed proportion of agreement (sum of main diagonal elements).
- Calculate expected proportion of agreement under chance based on marginal totals.
- Compute Cohen's Kappa statistic: kappa = (p_o - p_e) / (1 - p_e).
Assumptions
Important Note
Cohen's κ corrects for chance agreement. κ > 0.60 = substantial, κ > 0.80 = almost perfect (Landis & Koch, 1977).
Worked Example
| Metric | Observed (Po) | Chance (Pe) | Kappa (κ) | Verdict |
|---|---|---|---|---|
| Agreement | 0.82 | 0.54 | 0.609 | Substantial |
Cohen's Inter-Rater Agreement Matrix
Observe how the Kappa value adapts. When marginal distributions are extremely skewed, chance agreement increases, lowering Kappa for the same raw agreement.
Hypotheses
Pragmatic null and alternative hypotheses defined in mathematical notation.
H₀: κ = 0 (agreement no better than chance)
Hₐ: κ > 0 (agreement exceeds chance)
Cohen's κ corrects for chance agreement. κ > 0.60 = substantial, κ > 0.80 = almost perfect (Landis & Koch, 1977).
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.
- Confusion matrix (cross-tabulation of rater 1 vs rater 2 classifications)
- Observed agreement proportion (Po = sum of diagonal / total)
- Expected agreement by chance (Pe = sum of marginal probabilities)
- Prevalence index (difference in proportion of positive ratings between raters)
- Bias index (extent to which raters disagree on proportion of cases in each category)
- Kappa confidence intervals (asymptotic or bootstrap methods)
- Statistical significance test (z-test: κ/SE against H₀: κ=0)
- Category-specific kappa (conditional kappa for each category)
- Prevalence-adjusted bias-adjusted kappa (PABAK) if prevalence is extreme
- Gwet's AC1 coefficient (less affected by prevalence paradox)
- Examine specific disagreement patterns in confusion matrix
- Compare raw agreement with kappa to assess impact of chance correction
Applied Minds
Review concrete study examples, data layout guidelines, and copy executable syntax scripts.
Psychiatric Diagnosis Agreement (4-Category Classification)
Research question: Do two board-certified psychiatrists reliably agree when independently diagnosing patients with mood/anxiety disorders? Design: Cross-sectional diagnostic agreement study with 120 patients presenting with psychological symptoms. Categories: Depression, Anxiety, Bipolar Disorder, No Disorder. Real-world basis: Diagnostic reliability studies show κ=0.60-0.75 for structured clinical interviews (Regier et al., 2013 DSM-5 field trials).
# ============================================================================
# Comprehensive Cohen's Kappa Analysis for Diagnostic Agreement
# Complete workflow: simulation, kappa calculation, diagnostics, visualization
# ============================================================================
library(irr) # For kappa calculations
library(psych) # Additional statistics
library(vcd) # For agreement visualization
library(ggplot2) # For plotting
library(gridExtra) # For multi-panel plots
library(boot) # For bootstrap confidence intervals
# Seed for reproducibility
set.seed(2025)
# ============================================================================
# 1. DATA SIMULATION: 120 patients, 2 psychiatrists, 4 diagnostic categories
# ============================================================================
n <- 120
categories <- c("Depression", "Anxiety", "Bipolar", "No Disorder")
cat("\n========== COHEN'S KAPPA: DIAGNOSTIC AGREEMENT ==========\n")
cat("Sample size:", n, "patients\n")
cat("Raters: 2 psychiatrists\n")
cat("Categories:", length(categories), "diagnostic categories\n\n")
# Simulate true diagnoses (latent variable)
true_diagnosis <- sample(categories, n, replace=TRUE,
prob=c(0.35, 0.25, 0.15, 0.25))
# Psychiatrist 1: 85% accuracy
rater1 <- sapply(true_diagnosis, function(x) {
if(runif(1) < 0.85) return(x)
sample(setdiff(categories, x), 1)
})
# Psychiatrist 2: 80% accuracy (slightly lower)
rater2 <- sapply(true_diagnosis, function(x) {
if(runif(1) < 0.80) return(x)
sample(setdiff(categories, x), 1)
})
# Create data frame
diag_data <- data.frame(
Rater1 = factor(rater1, levels=categories),
Rater2 = factor(rater2, levels=categories)
)
# ============================================================================
# 2. CALCULATE COHEN'S KAPPA
# ============================================================================
cat("--- Cohen's Kappa Calculation ---\n")
kappa_result <- kappa2(diag_data, weight="unweighted")
print(kappa_result)
kappa_value <- kappa_result$value
kappa_se <- sqrt(kappa_result$var.kappa)
cat("\nCohen's Kappa: κ =", round(kappa_value, 3))
cat("\nStandard Error:", round(kappa_se, 3))
# ============================================================================
# 3. CONFIDENCE INTERVALS
# ============================================================================
cat("\n\n--- 95% Confidence Interval ---\n")
# Asymptotic CI
ci_lower <- kappa_value - 1.96 * kappa_se
ci_upper <- kappa_value + 1.96 * kappa_se
cat("Asymptotic 95% CI: [", round(ci_lower, 3), ",", round(ci_upper, 3), "]\n")
# Bootstrap CI
cat("\nBootstrap 95% CI(500 iterations):\n")
boot_kappas <- replicate(500, {
indices <- sample(1:n, replace=TRUE)
boot_data <- diag_data[indices, ]
kappa2(boot_data, weight="unweighted")$value
})
boot_ci <- quantile(boot_kappas, c(0.025, 0.975))
cat("Bootstrap CI: [", round(boot_ci[1], 3), ",", round(boot_ci[2], 3), "]\n")
# ============================================================================
# 4. STATISTICAL SIGNIFICANCE TEST
# ============================================================================
cat("\n--- Statistical Significance Test ---\n")
z_score <- kappa_value / kappa_se
p_value <- 2 * (1 - pnorm(abs(z_score))) # Two-tailed
cat("H₀: κ = 0 (no agreement beyond chance)\n")
cat("Z-score:", round(z_score, 3), "\n")
cat("P-value:", format.pval(p_value, digits=3), "\n")
cat(ifelse(p_value < 0.001, "Result: Highly significant(p < .001)\n",
ifelse(p_value < 0.05, "Result: Significant(p < .05)\n",
"Result: Not significant\n")))
# ============================================================================
# 5. CONFUSION MATRIX AND AGREEMENT STATISTICS
# ============================================================================
cat("\n--- Confusion Matrix ---\n")
conf_matrix <- table(diag_data$Rater1, diag_data$Rater2)
print(addmargins(conf_matrix))
cat("\n--- Agreement Statistics ---\n")
# Observed agreement
po <- sum(diag(conf_matrix)) / sum(conf_matrix)
cat("Observed agreement(Po):", round(po, 3), "(",
round(po*100, 1), "%)\n")
# Expected agreement by chance
rater1_marginal <- rowSums(conf_matrix) / sum(conf_matrix)
rater2_marginal <- colSums(conf_matrix) / sum(conf_matrix)
pe <- sum(rater1_marginal * rater2_marginal)
cat("Expected agreement(Pe):", round(pe, 3), "(",
round(pe*100, 1), "%)\n")
cat("Agreement beyond chance:", round((po - pe)*100, 1), "%\n")
# Kappa formula verification
kappa_manual <- (po - pe) / (1 - pe)
cat("\nKappa(manual): κ =", round(kappa_manual, 3), "\n")
# ============================================================================
# 6. PREVALENCE AND BIAS INDICES
# ============================================================================
cat("\n--- Prevalence and Bias Indices ---\n")
# Prevalence index
prevalence_idx <- abs(sum(conf_matrix[1,]) - sum(conf_matrix[,1])) / n
cat("Prevalence Index:", round(prevalence_idx, 3))
cat("\n(Measures asymmetry in marginal totals)\n")
# Bias index
bias_idx <- (sum(conf_matrix[1,]) - sum(conf_matrix[,1])) / n
cat("Bias Index:", round(bias_idx, 3))
cat("\n(Positive: Rater1 diagnoses more; Negative: Rater2 diagnoses more)\n")
# PABAK (Prevalence-Adjusted Bias-Adjusted Kappa)
pabak <- 2 * po - 1
cat("\nPABAK:", round(pabak, 3))
cat("\n(Less affected by prevalence than standard kappa)\n")
# ============================================================================
# 7. CATEGORY-SPECIFIC ANALYSIS
# ============================================================================
cat("\n--- Category-Specific Kappas ---\n")
for(cat_name in categories) {
# Binary kappa for this category vs all others
binary_r1 <- ifelse(rater1 == cat_name, cat_name, "Other")
binary_r2 <- ifelse(rater2 == cat_name, cat_name, "Other")
binary_data <- data.frame(
R1 = factor(binary_r1, levels=c(cat_name, "Other")),
R2 = factor(binary_r2, levels=c(cat_name, "Other"))
)
cat_kappa <- kappa2(binary_data)$value
cat(" ", cat_name, ": κ =", round(cat_kappa, 3), "\n")
}
# ============================================================================
# 8. INTERPRETATION
# ============================================================================
cat("\n========== INTERPRETATION ==========\n")
if(kappa_value < 0) {
interpretation <- "Poor(less than chance agreement)"
} else if(kappa_value < 0.20) {
interpretation <- "Slight"
} else if(kappa_value < 0.40) {
interpretation <- "Fair"
} else if(kappa_value < 0.60) {
interpretation <- "Moderate"
} else if(kappa_value < 0.80) {
interpretation <- "Substantial"
} else {
interpretation <- "Almost Perfect"
}
cat("\nLandis & Koch(1977) Classification:", interpretation)
cat("\n\nThe two psychiatrists showed", tolower(interpretation), "agreement")
cat("\n(κ =", round(kappa_value, 3), ", 95% CI [",
round(ci_lower, 3), ",", round(ci_upper, 3), "]).\n")
cat("\nObserved agreement was", round(po*100, 1), "%, which is")
cat("\n", round((po-pe)*100, 1), "% better than expected by chance alone.\n")
if(kappa_value >= 0.60) {
cat("\nThis level of agreement is acceptable for research purposes.\n")
if(kappa_value >= 0.75) {
cat("Agreement is sufficient for clinical decision-making.\n")
}
} else {
cat("\nThis level of agreement may be insufficient for clinical use.\n")
cat("Consider additional rater training or clearer diagnostic criteria.\n")
}
cat("\n========== ANALYSIS COMPLETE ==========\n")κ = 0.70-0.80 indicates substantial agreement, suitable for research purposes but may need improvement for high-stakes clinical decisions. Examine confusion matrix for systematic patterns of disagreement. Consider additional rater training for frequently confused categories.
Alternatives
Structured fallback pathways for choosing alternative tests when normality or slopes requirements fail.
- Gwet’s AC1 — A robust alternative that remains stable even when one category is extremely rare.
- PABAK Index — Provide the Prevalence-Adjusted Bias-Adjusted Kappa for clinical transparency.
- Multilevel Logistic — Account for clustering if raters work in teams or sites.
Post-hoc
Group mean comparisons and correction controls (e.g. Tukey HSD, Bonferroni) to protect against Family-Wise Error Rates.
- Compare with percent agreement (Kappa adjusts for chance)
- Examine prevalence and bias indices affecting Kappa
- Compare with Gwet's AC1 (less affected by prevalence)
- Bootstrap confidence intervals for Kappa
- Calculate category-specific Kappa for diagnostic insight
Cohen's Kappa measures inter-rater agreement for 2 raters. Post-hoc tests are not applicable.
Effect Size
Understanding effect sizes (e.g., Cohen's d, Partial Eta-Squared) and clinical impact benchmarks.
Poor - less than chance agreement (systematic disagreement)
Slight - minimal agreement beyond chance
Fair - weak agreement
Moderate - acceptable for exploratory studies
Substantial - good agreement for research
Almost Perfect - excellent agreement for clinical/applied use
0.2
0.5
0.8
Sample Size
Guidelines for minimum sample requirements and power analysis parameters.
The 'Consensus Buffer': A minimum of 60 subjects is recommended for a 2-rater agreement audit. Chance-correction math (Kappa) is highly sensitive to small cell counts in the disagreement cells.
| Effect Size | Parameters | Required n |
|---|---|---|
| Small Effect | Expected κ = .40 | n ≈ 100 |
| Medium Effect | Expected κ = .60 | n ≈ 45 |
| Large Effect | Expected κ = .80 | n ≈ 25 |
The 'Prevalence Paradox': If 95% of your sample is in one category, Kappa will be misleadingly low even if agreement is high. Ensure your recruitment covers a diverse spectrum of the clinical construct.
Reporting
How to compile statistical results into publication prose matching APA and journal style guides.
Inter-rater reliability was assessed using Cohen's kappa coefficient. Agreement between Rater 1 and Rater 2 was interpretation (κ = value, 95% CI lower, upper, p < .001), with X% observed agreement. Optional: The prevalence index was [value, indicating balanced/imbalanced category distributions.]
Manuscript Lab
Copy standard summary tables and forensic reporting grids to outline analysis details.
| Metric | Value | SE | p-value | Agreement Strength |
|---|---|---|---|---|
| Observed Agreement | 85.0% | — | — | High |
| Cohen's Kappa (κ) | 0.68 | 0.082 | < .001 | Substantial |
The 'Truth' Agreement. Represents the percentage of agreement that remains AFTER removing the agreement that would happen by random guessing.
The 'Raw' Agreement. The simple percentage of cases where both raters picked the same category.
Command Center
Syntax libraries and function parameters for executing calculations in stats packages.
# 1. Execute Cohen's Kappa
irr::kappa2(df_ratings)
# 2. Extract Weighted Kappa (for ordinal data)
irr::kappa2(df_ratings, weight = 'squared')The 'Prevalence Trap'. If one category is very common, Kappa will be low even if agreement is high. Always check 'Prevalence-Adjusted Bias-Adjusted Kappa' (PABAK).
# Execute PABAK Audit
epiR::epi.kappa(table(r1, r2))Common Mistakes
Analytical caveats and corrections to maintain modeling integrity.
References
Scholarly lineage and citation keys grounding the statistical framework.