Choosing a Rounding Strategy for Published Rates

This guide, part of Precision Standards in Epi-Mapping, treats the number of digits in a published rate as a decision with two consequences: what it claims about precision, and what it lets a reader recover.

Problem Context & Constraints

A tract rate of 47.3829 per 100,000 is a statement that the rate is known to four decimal places. It is not; with a numerator of eleven cases its 95% interval spans roughly 24 to 85. Every digit beyond the first or second is noise presented as information, and it invites readers to rank tracts by differences that the data cannot support.

Excess precision has a second consequence that matters more. A rate is a count divided by a population, and if the population is published — or can be looked up in a public census table — a rate with enough decimal places can be multiplied back to recover the count exactly. A table that suppresses a count of three and publishes the rate to four decimals has published the three.

Three constraints shape a rounding strategy. Digits should reflect uncertainty, so a rate is rounded to a precision its interval can support. Rounding must not re-enable back-calculation of suppressed or small counts, which means the rounding granularity must exceed the change in rate that one case produces. And tables that must add up — counts by area that sum to a published total — need controlled rounding, because rounding each cell independently makes the published cells disagree with the published total.

Extra Decimals Can Undo Suppression A tract with a publicly available population of 2 214 and a suppressed case count. The published rate carries four decimal places, 135.5014 per 100 000; multiplying back by the public population gives 3.0000 cases, recovering the suppressed count exactly. Rounded to the nearest 50 per 100 000, the rate becomes 150, which is consistent with any count from about two to four and recovers nothing. The rounding granularity has to exceed the change in rate produced by one case. Rate × public population = the count you suppressed Published to four decimals rate 135.5014 per 100 000 population 2 214 (public) 135.5014 × 2 214 / 100 000 = 3.0000 cases the suppressed count, recovered Rounded to the nearest 50 rate 150 per 100 000 population 2 214 (public) consistent with 2.2 to 4.4 cases nothing recovered granularity exceeds one case's effect one case changes this tract's rate by about 45 per 100 000 — round coarser than that

Prerequisites

  • python 3.11
  • pandas 2.2.2, numpy 1.26.4
  • Rates with their counts, denominators and confidence intervals, before any rounding
  • The disclosure policy’s small-cell threshold
  • Knowledge of which denominators are publicly available, since those are the ones that enable back-calculation

Step-by-Step Solution

Compute, per area, the change in rate that one case produces; set the rounding granularity above it; check that rounding also matches the rate’s own uncertainty; and use controlled rounding where published cells must sum to a total.

# Rounding published rates to their precision without enabling back-calculation.
# Pinned: pandas==2.2.2, numpy==1.26.4
import logging
import math

import numpy as np
import pandas as pd

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("rounding")

PER = 100_000
NICE_STEPS = [0.1, 0.5, 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500]


def one_case_step(pop: pd.Series) -> pd.Series:
    """Change in the published rate caused by a single case."""
    return PER / pop.replace(0, np.nan)


def granularity(rates: pd.DataFrame, pop_col: str = "population",
                ci_lo: str = "lo", ci_hi: str = "hi") -> pd.DataFrame:
    """Rounding step per area: coarser than one case, and no finer than the interval."""
    out = rates.copy()
    step_case = one_case_step(out[pop_col])
    # Uncertainty-based precision: a tenth of the interval width is a generous ceiling.
    step_ci = (out[ci_hi] - out[ci_lo]) / 10.0
    need = np.maximum(step_case * 1.01, step_ci)
    out["round_to"] = [next((s for s in NICE_STEPS if s >= n), NICE_STEPS[-1])
                       if np.isfinite(n) else np.nan for n in need]
    out["rate_published"] = (out["rate"] / out["round_to"]).round() * out["round_to"]
    log.info("rounding steps assigned: %s", out["round_to"].value_counts().sort_index().to_dict())
    return out


def back_calculation_check(pub: pd.DataFrame, pop_col: str = "population",
                           count_col: str = "cases") -> pd.DataFrame:
    """Can the published rate and public population pin the count to one integer?"""
    lo = (pub["rate_published"] - pub["round_to"] / 2) * pub[pop_col] / PER
    hi = (pub["rate_published"] + pub["round_to"] / 2) * pub[pop_col] / PER
    candidates = np.floor(hi) - np.ceil(lo) + 1
    pub = pub.assign(count_candidates=candidates)
    pinned = pub["count_candidates"] <= 1
    if pinned.any():
        log.error("%d published rates pin their count to a single value", int(pinned.sum()))
    else:
        log.info("no published rate identifies its count uniquely")
    return pub


def controlled_round(cells: pd.Series, total: int, base: int = 5) -> pd.Series:
    """Round counts to a base so the rounded cells sum to the rounded total.

    Cells are rounded down, then the remainder is distributed to the cells with
    the largest fractional parts — a deterministic largest-remainder scheme.
    """
    q = cells / base
    down = np.floor(q)
    target = int(round(total / base))
    short = target - int(down.sum())
    order = (q - down).sort_values(ascending=False, kind="mergesort").index
    add = pd.Series(0, index=cells.index)
    add.loc[order[:max(short, 0)]] = 1
    out = ((down + add) * base).astype(int)
    assert out.sum() == target * base
    return out

Setting the step from the one-case effect, not from a fixed number of decimals, is the design point. A fixed rule — one decimal place everywhere — is too coarse for a large county, where one case moves the rate by a fraction of a unit, and dangerously fine for a small tract, where one case moves it by dozens. Deriving the step per area gives each rate the precision it can safely carry.

The back-calculation check verifies the rule rather than trusting it. For each published rate it computes how many integer counts are consistent with the rounded value and the public population; any rate that admits only one count has disclosed that count, whatever the rounding rule intended.

Controlled Rounding Keeps the Table Adding Up Five area counts that sum to a total of 104. Rounded independently to the nearest five, the cells become 25, 20, 15, 30 and 20, summing to 110, which no longer matches the total rounded to 105. Readers notice the mismatch and, worse, the discrepancy can leak information about which cells were rounded up. Controlled rounding rounds cells down and distributes the remainder to the cells with the largest fractional parts, breaking ties in a fixed order, giving 25, 20, 15, 25 and 20, which sum exactly to 105. Independent rounding breaks the total; controlled rounding does not true 23 18 13 28 22 total 104 independent 25 20 15 30 20 sums to 110 ≠ 105 controlled 25 20 15 25 20 sums to 105 = 105 round down, then give the remainder to the largest fractional parts, deterministically the same input always produces the same rounded table

Validation & Edge Cases

Rounding a suppressed count’s neighbours. Rounding does not replace suppression. A cell below the small-cell threshold is suppressed first; rounding applies to what remains, and the back-calculation check confirms the combination is safe.

Public denominators you did not publish. A rate paired with a population you withheld is still back-calculable if the population is in a public census table. Assume every census and survey population is available to the reader.

Rounding ties. Round-half-to-even and round-half-up differ at exact halves, which occur more often than expected with small integers. Choose one, record it, and apply it everywhere; the controlled rounding above uses a stable sort so ties resolve deterministically.

Rates near zero. Rounding a small positive rate to zero states that the rate is zero, which is false and may imply suppression where none occurred. Publish “less than” the smallest step instead of zero.

Percentages and proportions. The same logic applies: a proportion of 33.33% from a denominator of three is a disclosure. The one-case step for a proportion is one over the denominator.

INFO rounding rounding steps assigned: {0.5: 58, 1: 214, 5: 611, 10: 402, 50: 188}
ERROR rounding 4 published rates pin their count to a single value

Those four rates are exactly the cases the check exists for: tracts with very large public populations where even a coarse step is finer than one case’s effect on the rate would suggest, because the interval-based step dominated. Raising their step to the next value in the list, or rounding the population used for the rate as well, resolves them; the choice should be recorded against each area.

The Safe Step Shrinks as Population Grows The effect of a single case on a rate per 100 000 plotted against area population, falling steeply as population grows. The rounding step assigned by the procedure is drawn as a staircase that always sits above the one-case curve, so no published rate can pin its count. Small tracts receive coarse steps of fifty or a hundred; counties receive steps of one or less. A fixed one-decimal rule would sit far below the curve for small areas, which is where back-calculation happens. The rounding staircase must stay above the one-case curve one-case effect assigned step fixed one-decimal rule small tract county area population

Compliance Notes

Record the rounding rule — the one-case criterion, the interval ceiling, the list of permitted steps, the tie-breaking rule and the controlled-rounding base — as configuration, and record per area the step applied and the result of the back-calculation check. Any published table with rounded rates should state the rounding rule in a footnote, because a reader comparing your rates with an unrounded source needs to know the difference is deliberate.

Rounding is a disclosure control as well as a presentation choice, and it belongs in the disclosure review alongside suppression. The two interact — a suppressed cell with rounded neighbours can still be recovered from a row total — so they should be evaluated together over the whole release, following Detecting Differencing Attacks Across Published Tables.