Skip to main content

Coarsening a Coordinate for Privacy

You geocoded a customer's address and got a rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. coordinate back — accurate to a few metres. That precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. is the whole point when you're routing a driver to the door. It's a liability when you're about to store the point in an analytics table, share it with a partner, or plot a thousand customers on a public dashboard. A rooftoprooftopGeocoding precision at the building or parcel level — coordinates within a few metres — the highest tier of the geocode cascade. Sourced from address-point and situs data. point is the house; you usually want the neighbourhood.

So coarsen it on purpose. You have two ways to do it, and the only real decision is how much accuracy you're willing to give up.

Round the decimal degrees

The cheapest coarsening is to drop decimal places. Each one you keep is worth roughly 10× the precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1.:

DecimalsCell sizeWhat it pins
5~1 mthe doormat
4~11 mthe building
3~110 mthe block
2~1.1 kmthe neighbourhood
1~11 kmthe citylocalityThe city / town / settlement component of an address: a populated place sitting between region and neighbourhood in the hierarchy.
const round = (n: number, decimals: number) => {
const f = 10 ** decimals
return Math.round(n * f) / f
}

// A rooftop point coarsened to the neighbourhood.
const coarse = { lat: round(result.lat, 2), lon: round(result.lon, 2) }
// { lat: 40.75, lon: -73.99 }

The catch with rounding is that the cell it lands in depends on where the point sits relative to the grid — two houses on the same streetstreetThe named linear feature along which house numbers are ordered. Decomposes into a name plus street affixes; one of the Tier 2 fine labels. can round into different 0.01° cells. If you need every point in a regionregionThe first-level administrative subdivision of a country — a US state, a French region, a province. The component between country and locality. to collapse to the same coarse location, reach for a geohash instead.

Truncate a geohash

A geohash encodes a coordinate as a string where every character you drop widens the cell it names. @mailwoman/spatial ships the encoderencoderThe part of a transformer that turns input tokens into contextualized vector representations. Mailwoman's classifier is a small encoder-only transformer (~30M parameters)., so you pick a precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. and hand back the cell:

import { toGeohash } from "@mailwoman/spatial"

toGeohash(40.7484, -73.9857) // precision 9 ≈ 4.8 m: "dr5ru6j28"
toGeohash(40.7484, -73.9857, 5) // ≈ 4.9 km: "dr5ru"
PrecisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1.Cell size
9 (default)~4.8 m
7~153 m
6~1.2 km
5~4.9 km
4~39 km

Because the cell boundaries are fixed, every address inside dr5ru shares the prefix — you can group, join, or count by the truncated string and know that two records in the same cell really are neighbours. Store the geohash, not the point, and the precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. you didn't keep is precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1. you can't leak.

One thing to name plainly: coarsening is one-way by design, but it is not anonymity. A precisionprecisionOf the spans the model labeled as a given tag, the fraction it got right. High precision means few false positives. Paired with recall to compute F1.-6 cell over a rural address can still hold exactly one house. If the guarantee you need is "this point cannot be traced to a person," coarsening is a layerlayerOne transformer block — attention plus a feed-forward network, with normalization and residual connections — applied to every position. Stacking layers lets the model build up richer representations; Mailwoman's encoder has 6., not the whole answer — pair it with aggregation thresholds (suppress any cell with fewer than k records) before anything goes public. See Privacy policy & legal posture for where Mailwoman's own data-handling design sits relative to this.