Understanding Krippendorff's Alpha

The Problem with Raw Agreement
You're building an LLM evaluation pipeline. Four contractors rate 500 model responses as "Good," "Okay," or "Bad." The dashboard shows 87% pairwise agreement. Ship it?
Not so fast. One rater noticed that 70% of responses are "Good" and started marking everything "Good" to speed through the batch. They still agree with the others most of the time—but they're contributing zero signal.
This is the lazy rater problem, and raw agreement can't detect it. If 70% of items are genuinely "Good," two random guessers would agree about 54% of the time just by chance (). We need a metric that asks: how much better is our agreement than random chance?
The Whole Idea in One Formula
Krippendorff's Alpha measures the reduction in disagreement relative to random labeling:
where is the disagreement you actually observed, and is the disagreement you'd expect if the same labels had been dealt out at random.
- : Perfect agreement—no observed disagreement
- : Agreement equals chance—raters might as well flip coins
- : Systematic disagreement—raters actively contradict each other
Everything else is careful bookkeeping: how to count observed disagreement fairly, and how to estimate what chance alone would produce. The fastest way to internalize both is to compute them by hand, once, on a dataset small enough to hold in your head.
A Worked Example
Three raters evaluate 5 items using categories A, B, and C:
| Item | Rater 1 | Rater 2 | Rater 3 |
|---|---|---|---|
| 1 | A | A | A |
| 2 | B | B | C |
| 3 | A | A | A |
| 4 | B | B | B |
| 5 | C | C | B |
We'll follow the same five steps you'll use on real data (and in the practice exercise below).
Step 1: Raw Agreement
First, the simple metric everyone knows—what percentage of rating pairs agree?
For each item with raters, there are unordered pairs. With 3 raters per item, that's pairs per item. Across 5 items: pairs total.
| Item | Ratings | Pairs | Agreements |
|---|---|---|---|
| 1 | A, A, A | (A,A), (A,A), (A,A) | 3/3 |
| 2 | B, B, C | (B,B), (B,C), (B,C) | 1/3 |
| 3 | A, A, A | (A,A), (A,A), (A,A) | 3/3 |
| 4 | B, B, B | (B,B), (B,B), (B,B) | 3/3 |
| 5 | C, C, B | (C,C), (C,B), (C,B) | 1/3 |
Raw Agreement =
Not bad! But is 73% impressive, or would random labeling do nearly as well? Hold that thought.
Step 2: The Coincidence Matrix
The coincidence matrix counts how often each pair of categories co-occurs across all rater pairs. For each item rated by raters, we enumerate all ordered pairs and weight each by .
With 3 raters per item, each ordered pair gets weight .
▶Why the 1/(m-1) weighting?
This ensures each item contributes equally regardless of how many raters labeled it. With raters generating ordered pairs, the total contribution is —the number of ratings on that item.
Without this, items with more raters would dominate the calculation, biasing Alpha toward those items.
▶Show item-by-item breakdown
Item 1 (A, A, A): 6 ordered pairs, all (A,A) → contributes to cell (A,A)
Item 2 (B, B, C):
- (B,B): 2 pairs →
- (B,C): 2 pairs →
- (C,B): 2 pairs →
Item 3 (A, A, A): Same as Item 1 → adds 3 to (A,A)
Item 4 (B, B, B): All pairs are (B,B) → adds 3 to (B,B)
Item 5 (C, C, B):
- (C,C): 2 pairs →
- (C,B): 2 pairs →
- (B,C): 2 pairs →
The resulting coincidence matrix :
| A | B | C | Row Total | |
|---|---|---|---|---|
| A | 6 | 0 | 0 | 6 |
| B | 0 | 4 | 2 | 6 |
| C | 0 | 2 | 1 | 3 |
Matrix total = 15 (equals total ratings across all items: 5 items × 3 raters). The diagonal is where agreement lives; everything off it is disagreement.
Marginal totals (row/column sums):
- , ,
- (total pairable values)
Step 3: Observed Disagreement ()
For nominal data (unordered categories), the difference function is:
Observed disagreement is the proportion of coincidences that are disagreements (off-diagonal entries):
From our matrix:
This equals . So far, nothing new—we've just reframed agreement as disagreement.
Step 4: Expected Disagreement ()
Here's Krippendorff's key insight. If we shuffled all ratings randomly while keeping the same category frequencies, what disagreement would we expect by chance?
The probability that two random draws from our pool are different:
Where is the marginal frequency of category .
From our data: , , ,
Key insight: Random raters with these category frequencies would disagree 68.6% of the time. Our actual raters disagreed only 26.7% of the time—much better than chance!
Step 5: Krippendorff's Alpha
Interpretation: Our raters captured 61% of the maximum possible agreement beyond chance. The raw 73% sounded good, but after chance correction we land here:
Moderate reliability—acceptable for exploratory work, but not for high-stakes decisions.
Summary of the Example
| Metric | Value | Meaning |
|---|---|---|
| Raw Agreement | 73.3% | How often raters agreed |
| Observed Disagreement () | 26.7% | How often raters disagreed |
| Expected Disagreement () | 68.6% | Disagreement expected by chance |
| Krippendorff's Alpha | 0.61 | Reliability beyond chance |
Ways to Fool Yourself
Five mistakes account for most misreadings of Alpha:
1. Confusing the coincidence matrix with a confusion matrix. A confusion matrix compares predictions to ground truth. The coincidence matrix compares raters to each other—there's no "correct" answer involved.
2. Forgetting the weighting. Without it, items with more raters dominate the calculation. The weighting ensures each item contributes equally, not each pair.
3. Thinking high raw agreement means high Alpha. If categories are imbalanced (e.g., 90% of items are "Good"), random guessing produces high agreement. Alpha adjusts for this, so you can have 85% agreement but .
4. Confusing "agreement" with "accuracy." Alpha measures whether raters agree with each other, not whether they're correct. Three raters who all wrongly label "Bad" responses as "Good" will have perfect Alpha and zero accuracy.
5. Misinterpreting negative Alpha. Negative Alpha doesn't mean "bad raters"—it means raters systematically disagree more than chance. This is rare and usually indicates a coding error, inverted scales, or genuinely adversarial labeling.
Try It Yourself
The playground below simulates raters with controllable accuracy. Each row has a hidden ground truth; drag the sliders to see how rater quality affects Alpha.
| Item | Consensus | R1 | R2 | R3 | R4 |
|---|---|---|---|---|---|
| 1 | 3 | 3 | 3 | 3 | 3 |
| 2 | 2 | 2 | 2 | 2 | 1 |
| 3 | 1 | 2 | 1 | 3 | 1 |
| 4 | 1 | 1 | 1 | 1 | 1 |
| 5 | 2 | 2 | 2 | 2 | 2 |
| 6 | 1 | 1 | 1 | 1 | 1 |
| 7 | 3 | 3 | 3 | 3 | 3 |
| 8 | 3 | 3 | 3 | 3 | 3 |
| 9 | 3 | 3 | 3 | 3 | 3 |
| 10 | 2 | 2 | 2 | 1 | 2 |
| 11 | 2 | 2 | 2 | 2 | 2 |
| 12 | 2 | 2 | 2 | 2 | 2 |
Experiments to try:
- "LLM Eval: The Lazy Rater" — One rater at 33% (random guessing for 3 categories). Watch Alpha drop despite decent raw agreement.
- "Pure Random" — All raters at 33%. They agree by chance about 1/3 of the time, but .
- "Expert vs Novice" — Mixed skill levels. Alpha reflects systematic disagreement.
- Drag one slider to 0% (always wrong). How does systematic anti-correlation affect Alpha?
Your Turn
The worked example above was mine; this one is yours. Each problem generates fresh ratings in a new setting, and the five steps mirror the walkthrough exactly. If you get stuck, help escalates gently—a nudge first, then the setup with your numbers plugged in, and only then the full answer.
Practice problem
Science Fair
| Project | Judge 1 | Judge 2 | Judge 3 |
|---|---|---|---|
| #1 | B | S | B |
| #2 | G | G | B |
| #3 | B | B | B |
| #4 | B | B | B |
| #5 | B | B | G |
Choosing the Difference Function
The choice of encodes what "disagreement" means for your data type:
| Level | Example | Intuition | |
|---|---|---|---|
| Nominal | if , else | Categories (Good/Bad/Okay) | All disagreements equally bad |
| Ordinal | Likert scales (1-5 stars) | Disagreeing 1↔5 worse than 1↔2 | |
| Interval | Temperature, dates | Distance matters, ratios don't | |
| Ratio | Counts, durations | 2↔4 same as 20↔40 |
For most LLM evaluation tasks with categorical labels (Good/Okay/Bad), use nominal. For Likert scales or ranked preferences, use ordinal. You can flip between them in the playground above and watch Alpha move.
How Good Is Good Enough?
Krippendorff (2004, p. 241) recommends:
| Interpretation | |
|---|---|
| Reliable for most purposes | |
| – | Acceptable for tentative conclusions |
| Treat data with caution |
For high-stakes LLM evaluations, aim for . Below 0.67, your "signal" is mostly noise.
How Much Should You Trust That Number?
Alpha is a point estimate computed from a sample of items. A small or lopsided sample can produce an impressive Alpha by luck. The practical remedy is a bootstrap confidence interval: resample your items with replacement, compute Alpha for each sample, and take percentiles.
▶Show Python bootstrapping code
import numpy as np
import krippendorff
def bootstrap_alpha(ratings, n_bootstrap=1000):
"""Compute Alpha with 95% confidence interval."""
n_items = ratings.shape[0]
alphas = []
for _ in range(n_bootstrap):
# Resample items with replacement
indices = np.random.choice(n_items, n_items, replace=True)
sample = ratings[indices]
alphas.append(krippendorff.alpha(sample, level_of_measurement='nominal'))
return {
'alpha': krippendorff.alpha(ratings, level_of_measurement='nominal'),
'ci_low': np.percentile(alphas, 2.5),
'ci_high': np.percentile(alphas, 97.5),
'std': np.std(alphas)
}| Scenario | Alpha | 95% CI | Interpretation |
|---|---|---|---|
| Tight CI | 0.75 | [0.72, 0.78] | Reliable estimate; you can trust this |
| Wide CI | 0.75 | [0.55, 0.90] | Uncertain; need more data |
| CI crosses threshold | 0.75 | [0.62, 0.85] | Might not meet 0.80 standard |
A practical rule: if your 95% CI doesn't include your decision threshold (0.67 or 0.80), you can make a confident claim. If it spans the threshold, get more data. Always report CIs when making go/no-go decisions, comparing reliability across conditions, or publishing results others will build on.
Beyond the overall number, examine disagreement item by item. High-disagreement items are diagnostic gold—they show you exactly where your rubric is failing, and they make the best training examples for calibration sessions.
▶Show Python code for per-item analysis
def item_disagreement(ratings):
"""Compute disagreement for each item."""
disagreements = []
for item in ratings:
valid = item[~np.isnan(item)]
if len(valid) < 2:
disagreements.append(np.nan)
continue
# Proportion of pairs that disagree
pairs = [(valid[i], valid[j]) for i in range(len(valid))
for j in range(i+1, len(valid))]
disagree = sum(1 for a, b in pairs if a != b) / len(pairs)
disagreements.append(disagree)
return disagreementsWhen to Use Alpha (and When Not To)
Alpha Is Right For:
- Annotation quality checks — Are your labelers consistent enough to trust?
- Codebook validation — Can different people apply your categories reliably?
- Benchmarking raters — Identifying who needs more training
Alpha Is Wrong For:
1. When ground truth exists. If you have gold labels, measure accuracy, not agreement. Alpha tells you if raters agree with each other, not if they're correct.
2. When you're comparing models. Alpha measures human consistency, not model quality. Use head-to-head preference rates or accuracy instead.
3. When disagreement is the signal. Some tasks have legitimate ambiguity. If experts genuinely disagree, that's valuable information—not measurement failure.
Why Not Just Use Kappa?
Cohen's Kappa addresses chance correction too, but Krippendorff's Alpha is more general:
| Feature | Cohen's Kappa | Krippendorff's Alpha |
|---|---|---|
| Number of raters | 2 only | Any number |
| Missing data | Not allowed | Handles gracefully |
| Data types | Nominal (extensions exist) | Nominal, ordinal, interval, ratio |
| Use case | Simple pairwise agreement | Complex annotation projects |
For LLM evals with rotating contractor pools and incomplete coverage, Alpha is the practical choice.
Improving Low Alpha
Low reliability isn't failure—it's actionable feedback:
-
Refine your rubric. Ambiguous categories cause disagreement. "Is this response helpful?" is vague; "Does this response directly answer the user's question?" is testable.
-
Calibrate with examples. Give raters 20 pre-labeled examples to study before rating. Discuss edge cases explicitly.
-
Identify problem raters. Calculate Alpha with each rater removed. If one rater's removal dramatically improves Alpha, they need retraining.
-
Stratify by difficulty. Separate "clear" from "borderline" cases and report Alpha for each.
-
Add categories. If raters keep disagreeing between "Good" and "Okay," maybe you need a "Good-ish" middle ground.
Code
▶Python example
import krippendorff
import numpy as np
# Ratings matrix: rows = items, columns = raters
# Use np.nan for missing values
ratings = np.array([
[1, 1, 2, 1],
[2, 2, 2, 2],
[1, 2, 1, 1],
# ...
])
alpha = krippendorff.alpha(ratings, level_of_measurement='nominal')
print(f"Krippendorff's Alpha: {alpha:.3f}")▶R example
library(irr)
kripp.alpha(data_matrix, method="nominal")Further Reading
- Krippendorff, K. (2004). Content Analysis: An Introduction to Its Methodology
- Hayes & Krippendorff (2007). "Answering the call for a standard reliability measure"
- krippendorff Python package