Standardization: direct, indirect, and the SMR
Last updated
A crude death rate is every death in a population divided by the person-time at risk. It is easy to compute and easy to misread. Mortality climbs steeply with age, so a district full of retirees will bury more people per thousand person-years than a district full of young families, even when a 70-year-old faces exactly the same risk in both places. The crude rate blends two separate things: how dangerous each age is, and how the population is spread across the ages. When you compare two districts with different age structures, age is confounding the comparison.
Picture two Malaysian districts, each with 100,000 person-years at risk. Split each into "under 50" and "50 and over". The age-specific death rates are identical in both districts: 1 per 1,000 under 50, and 30 per 1,000 at 50 and over. District Muda is young, with 90,000 person-years under 50 and 10,000 at 50 plus. District Tua is old, with 40,000 under 50 and 60,000 at 50 plus. Muda records 90 + 300 = 390 deaths, a crude rate of 3.9 per 1,000. Tua records 40 + 1,800 = 1,840 deaths, a crude rate of 18.4 per 1,000. The crude rates differ almost fivefold, yet age-for-age the two districts are equally safe. The whole gap is age structure.
Age-specific rates, and why you still want one number
The honest comparison is the table of age-specific rates. Lay Muda and Tua side by side and you see at once that they match at every age. Age-specific rates are where you should always start, and you saw how to build them from person-time in module 11. The trouble is practical. A full table is hard to rank, hard to fit in a headline, and hard to track over time. A health department wants a single adjusted number per district that it can plot on one axis. Standardization produces that number by holding the age structure fixed, so any difference that remains reflects the rates and not the demography.
There are two standard methods, direct and indirect. Both lean on a standard population: a reference age structure that every comparison is forced to share. Which method you use depends on what data you can get.
Direct standardization
Direct standardization takes each district's own age-specific rates and asks what overall rate they would produce in one shared population. The steps are:
- Choose a standard population. It can be one of the study districts, the two combined, or a national census. The choice is somewhat arbitrary, but use the same one for every comparison in a report.
- Take the age-specific rates from the district of interest.
- Apply each rate to the standard population's matching age band: multiply the rate by the standard person-time in that band to get the events you would expect there.
- Sum the expected events across bands and divide by the total standard person-time. That weighted average is the directly standardized rate.
Here is the person-time in age band of the standard population, and is the age-specific rate in that band in the district of interest. The directly standardized rate is just a weighted average of the district's age-specific rates, weighted by the standard population's age structure. Because both districts are now scored on the same age structure, their standardized rates are comparable. Divide one by the other for a standardized rate ratio.
Worked example 1: directly standardized rates for two districts
Two districts report age-specific mortality per 1,000 person-years across four bands. We standardize both to a shared reference structure (person-years in thousands), then compare.
| Age band | Standard person-years (000s), w | District A rate | District B rate |
|---|---|---|---|
| 0−14 | 300 | 1.0 | 1.2 |
| 15−44 | 450 | 1.8 | 2.2 |
| 45−64 | 180 | 6.5 | 7.8 |
| 65+ | 70 | 42.0 | 50.0 |
Example
For District A, the expected events are 1.0×300 + 1.8×450 + 6.5×180 + 42×70 = 5,220. Divide by the total standard person-time of 1,000 and the directly standardized rate is 5.22 per 1,000. District B works out to 6.254, a standardized rate ratio of about 1.20.
Build the bands, the standard person-years, and each district's age-specific rates as vectors, then compute both directly standardized rates and their ratio.
band <- c("0-14", "15-44", "45-64", "65+")
w <- c(300, 450, 180, 70) # standard person-years (000s)
rateA <- c(1.0, 1.8, 6.5, 42.0) # District A, per 1000 pyar
rateB <- c(1.2, 2.2, 7.8, 50.0) # District B, per 1000 pyardsrA <- # weighted sum of rateA over w
dsrA <- sum(rateA * w) / sum(w) dsrB <- sum(rateB * w) / sum(w) ratio <- dsrB / dsrA round(c(dsrA = dsrA, dsrB = dsrB, ratio = ratio), 3) # dsrA dsrB ratio # 5.220 6.254 1.198
Put the bands, weights, and rates in a pandas table, then compute both directly standardized rates and their ratio with weighted sums.
import pandas as pd
df = pd.DataFrame({
"band": ["0-14", "15-44", "45-64", "65+"],
"w": [300, 450, 180, 70],
"rateA": [1.0, 1.8, 6.5, 42.0],
"rateB": [1.2, 2.2, 7.8, 50.0],
})dsrA = # weighted sum of rateA over w
dsrA = (df["rateA"] * df["w"]).sum() / df["w"].sum() dsrB = (df["rateB"] * df["w"]).sum() / df["w"].sum() ratio = dsrB / dsrA print(round(dsrA, 3), round(dsrB, 3), round(ratio, 3)) # 5.22 6.254 1.198
Indirect standardization and the SMR
Direct standardization needs reliable age-specific rates from every district you study. When a district is small, an age band might hold only a handful of deaths, and its age-specific rate bounces around too much to trust. Indirect standardization turns the calculation around. Instead of trusting the district's own shaky rates, you borrow a stable set of standard rates and ask: how many deaths would this district have seen if it had experienced the standard rates at every age?
- Choose a standard set of age-specific rates, usually national figures large enough to be reliable.
- For each age band in the study district, multiply the standard rate by the district's person-time to get the expected events in that band.
- Sum the expected events across bands to get the total expected, .
- Count the observed events, , actually recorded in the district.
- Divide observed by expected. That ratio is the standardized mortality ratio.
Here is the standard rate in band and is the district's person-time in that band, so is the expected events there. The same idea gives a standardized morbidity ratio when the events are new cases of disease rather than deaths.
Key term
The SMR is the observed number of events in a population divided by the number you would expect if it had the standard population's age-specific rates. It compares a person in the study population to someone of the same age in the standard population.
Interpreting an SMR
An SMR of 1 means the district had exactly the deaths the standard rates predict for its age structure. Above 1 means more deaths than expected, below 1 means fewer. An SMR of 1.21 says 21 percent more deaths than a population of the same ages would produce at standard rates. People often multiply by 100 and quote it as a percentage, so 1.21 becomes 121 and 0.85 becomes 85. If you know the crude rate of the standard population, multiply it by the SMR to get an age-adjusted rate in familiar units.
Worked example 2: expected events and an SMR
An industrial district reports its own person-time by age band and its total deaths for the year. National age-specific rates serve as the standard. We compute the expected deaths and the SMR.
Example
Expected deaths are 1.1×40 + 2.0×120 + 7.0×90 + 45×25 = 2,039 (using person-years in thousands against rates per 1,000). With 2,470 deaths observed, the SMR is 2,470 / 2,039 = 1.21. The district has about 21 percent more deaths than national age-specific rates would predict.
Build the standard rates and the district's person-years as vectors, then compute expected deaths and the SMR.
band <- c("0-14", "15-44", "45-64", "65+")
std_rate <- c(1.1, 2.0, 7.0, 45.0) # standard rates per 1000 pyar
pyar <- c(40000, 120000, 90000, 25000) # district person-years
observed <- 2470 # deaths recordedexpected <- # sum of standard rate times person-years
expected_i <- std_rate * pyar / 1000 # expected deaths per band expected <- sum(expected_i) smr <- observed / expected round(c(expected = expected, smr = smr), 3) # expected smr # 2039.000 1.211
Build the standard rates and person-years as numpy arrays, then compute expected deaths and the SMR with a weighted sum.
import numpy as np std_rate = np.array([1.1, 2.0, 7.0, 45.0]) # per 1000 pyar pyar = np.array([40000, 120000, 90000, 25000]) # district person-years observed = 2470
expected = # sum of standard rate times person-years
expected = np.sum(std_rate * pyar / 1000) smr = observed / expected print(round(expected, 3), round(smr, 3)) # 2039.0 1.211
Choosing direct or indirect
Reach for direct standardization when every group has solid age-specific rates and you want adjusted rates you can compare straight against each other. Reach for indirect standardization when some groups are small or their age-specific rates are unstable, or when you only have each group's age structure and total event count rather than a full rate table. Indirect standardization is holds up better with sparse data, because it leans on the stable standard rates rather than the district's own.
Watch out
Standardization removes confounding by the factor you standardize on and nothing else. An age-adjusted rate says nothing about sex, ethnicity, or smoking unless you standardize on those too. It is also a descriptive summary, not a significance test: to decide whether two adjusted rates differ by more than chance, use the Mantel-Haenszel or Poisson regression methods from the earlier modules. And two SMRs are only comparable when they share the same standard rates and the underlying age structures are similar, so do not rank a column of SMRs as if they were rates.
A rural district has only a few deaths in each age band, so its age-specific rates jump around from year to year. You want one age-adjusted figure to compare it against the national picture. Which method fits best?
A district's SMR for stroke is 0.85, using national age-specific rates as the standard. What does that mean?
Common mistakes
- Comparing crude rates across populations with different age structures. This is the original sin. The Muda and Tua districts had identical age-specific rates and crude rates that differed fivefold. Standardize first.
- Swapping which population supplies the rates and which supplies the structure. Direct applies the study district's rates to the standard structure. Indirect applies the standard rates to the study district's structure. Mixing them gives a meaningless number.
- Ranking a column of SMRs as if they were rates. SMRs are only comparable when they share the same standard rates and similar age structures. They are ratios against a reference, not rates you can line up.
- Reading an SMR as a death rate. An SMR of 1.21 is not 1.21 per 1,000. It is observed over expected. Multiply by the standard crude rate to get a rate.
- Losing the units. If rates are per 1,000 person-years, the person-time and the weights must be on a matching scale, or the expected count comes out a thousandfold wrong.
Tips
- Always inspect the age-specific rates before standardizing. If the rate patterns cross between groups, a single summary can hide the very thing you care about, and the choice of standard population will sway the answer.
- Pick one standard population and reuse it for every comparison in a report, so the numbers sit on the same axis.
- Use direct standardization when rates are stable across all groups, and indirect when events are sparse in some of them.
- Multiply an SMR by the standard population's crude rate to get an age-adjusted rate you can talk about in ordinary units.
- Carry a units check through every calculation: rate per 1,000 against person-years in thousands keeps the expected counts honest.