Normalize counts into per-capita rates

Turn raw counts into population-adjusted rates for any list of regions. Choose per capita, per 1,000, per 10,000, per 100,000 or a custom base, and export the ranked table as text, CSV, Markdown or JSON.

Try:
Normalized rate table

About this tool

Raw counts lie when the groups behind them are different sizes. A city of two million will report more cases, crimes, complaints or sign-ups than a town of forty thousand even when the underlying risk is identical. Per Capita Normalizer divides each count by its own population and rescales it to a shared reporting base, so the numbers become comparable.

Paste one label, count, population row per line, pick a base — per person, per 1,000, per 10,000, per 100,000 (the public-health convention), per 1,000,000, or a custom base such as "per 500 residents" — and the tool returns a ranked table with:

Everything runs locally in your browser; nothing is uploaded.

Worked example

Input (comma-delimited, first row is a header):

region,cases,population
Northbridge,120,400000
Eastvale,45,150000
Westport,18,900000

Output at per 100,000 with 2 decimals:

per 100000 · rows: 3 · total count: 183 · total population: 1450000 · overall rate: 12.62 · flagged unstable (count < 20): 1

rank	label	count	population	rate_per_100000	index	flag
1	Eastvale	45	150000	30.00	2.38	ok
2	Northbridge	120	400000	30.00	2.38	ok
3	Westport	18	900000	2.00	0.16	unstable

Northbridge has by far the biggest raw count, but Eastvale — under half its size — carries the exact same rate of 30 per 100,000. Westport looks busy at 18 cases until you notice it is spread across 900,000 people, giving a rate 84% below the overall average, and its 18 events sit under the 20-event threshold, so it is flagged as unstable.

Reading population tables that aren't in people

International statistics often publish population in thousands or millions. Set Population column is in to Thousands or Millions and the tool multiplies the column for you, instead of forcing you to expand 8,175 into 8175000 by hand.

Limits and conventions

FAQ

Why per 100,000 rather than a percentage?

A percentage is just a rate per 100. For events that are rare relative to the population — disease cases, homicides, fatal accidents — a percentage collapses into a string of zeros (0.003%), while per 100,000 gives a readable 3.0. Pick the base that puts your typical value in the 1–1000 range: per 1,000 for births, deaths or defects, per 100,000 for rarer events, per person for things everybody does several times.

What does the index column mean?

It is each row's rate divided by the overall rate of everything you pasted, so 1.00 means "exactly the pooled average". A row at 2.38 is 138% above average; a row at 0.16 is 84% below. It saves you a second pass of mental arithmetic when comparing a region against the group instead of against the top row.

Why is a row flagged unstable?

Rates built on very few events are noisy: with 3 events, one more or fewer moves the rate by a third. Statistical agencies commonly suppress or asterisk rates under about 20 events for that reason, so this tool flags them by default. Change the threshold in Flag rows with a count below, or set it to 0 to turn flagging off entirely — every row then reads ok.

Can I paste straight from a spreadsheet?

Yes. Copy the three columns and leave the delimiter on Auto (or choose Tab). Auto-detect also handles semicolon and pipe files, and falls back to whitespace when a row contains no separator at all. Header rows are detected automatically when the count or population cell isn't a number, and you can force the choice with the Header row control.

What if my rows have no label?

A two-field row is read as count, population and labelled row 1, row 2, and so on, so you can paste two bare columns. Single-row input works too — the index is then 1.00, since the one row is the whole population.

Does it do age-standardized rates?

No. It computes crude rates only. Age standardization needs an external standard population broken into age bands and the same age breakdown for your own data, which is a different input shape. If you need age-adjusted comparisons, compute the rate per age band here and weight them yourself.

How do I get the result into a report?

Choose the output format: CSV for a spreadsheet (it starts with a small metric block, then the row table), Markdown for a documentation or ticket table, or JSON for scripts and dashboards. The text format adds a fixed-width rate chart for a quick visual scan, and every format can be copied with one click.

Developer & Automation Access

Run it from the terminal

Same engine as this page, headless — via the gizza CLI:

gizza tool per-capita-normalizer "region,cases,population
Northbridge,120,400000
Eastvale,45,150000
Westport,18,900000"

New to the CLI? Get gizza →

Open it by URL

Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:

https://gizza.ai/tools/per-capita-normalizer/?data=region%2Ccases%2Cpopulation%0ANorthbridge%2C120%2C400000%0AEastvale%2C45%2C150000%0AWestport%2C18%2C900000&delimiter=auto&header=auto&per=100000&custom_per=500&population_unit=ones&decimals=2&sort=rate_desc&unstable_below=20&output=table

Machine-readable descriptor: tool.json — title + parameters JSON Schema for agents.