Accessibility Measures Explained: Nearest, Cumulative and Gravity

Problem statement

"Which neighbourhoods have poor access to pharmacies?" sounds like a question with one answer. It has at least three, because "access" can be measured three standard ways:

  • Nearest: the travel time to the closest facility.
  • Cumulative opportunities: how many facilities lie within a set travel time.
  • Gravity: every facility counts, weighted down the further away it is.

Measured on the 2,240 reachable populated census blocks of Chittenden County, Vermont, with drive times to 27 pharmacies, the three measures agree broadly and part company exactly where a decision gets made. Across the county's 40 census tracts:

  • The rank correlation between nearest time and a gravity score was 0.906.
  • Yet 22 of the 40 tracts landed in a different fifth of the ranking under the two measures.
  • Each measure put 8 tracts in the worst-served fifth; only 6 tracts were in it under all three.

Picking a measure is picking which residents a programme reaches.

Quick answer

All three come from the same origins ร— facilities matrix of travel times:

import numpy as np

nearest = T.min(axis=1)                                   # minutes to the closest facility
within_10 = (T <= 10).sum(axis=1)                         # facilities within 10 minutes
gravity_5 = np.exp(-np.log(2) / 5 * T).sum(axis=1)        # half-life of 5 minutes

Use nearest when one facility is enough and people go to the closest โ€” emergency services, a single clinic. Use cumulative when choice matters and a clear standard exists โ€” "at least two supermarkets within 10 minutes". Use gravity when choice matters and no hard cutoff is defensible. Where facilities have limited capacity relative to demand, use a two-step floating catchment (2SFCA), which divides supply by the population competing for it.

Table of three accessibility measures โ€” nearest time, cumulative count and gravity score โ€” with what each ignores and how each distributed across the county.
Each measure throws away something the others keep, which is why they rank places differently.

Step-by-step solution

1. Build the travel-time matrix once

Every measure is a summary of one matrix: rows are homes or small areas, columns are facilities, values are travel times. For 2,241 blocks and 27 pharmacies on the county's drive network the matrix took 51 ms, running one shortest-path search per pharmacy on the reversed graph. One block could not reach any pharmacy and was dropped, which should always be counted and reported.

2. Compute the nearest-facility time

nearest drive time: pop-weighted median 2.8 min, p90 9.8, max 22.9

It is the easiest measure to explain and the easiest to map. It ignores everything except the closest facility: a home with one pharmacy three minutes away scores the same as a home with ten.

3. Compute cumulative opportunities at a cutoff you can defend

within  5 min: pop-weighted median  2, max 11, people with 0: 31.4%, distinct values 11
within 10 min: pop-weighted median  7, max 18, people with 0:  9.5%, distinct values 19
within 15 min: pop-weighted median 16, max 21, people with 0:  3.0%, distinct values 22

A cumulative measure captures choice, but it is coarse. At 10 minutes it took only 19 distinct values across 2,240 blocks, and 43 blocks tied at the top value of 18. And it is blind below the cutoff: the 16,018 residents with no pharmacy within 10 minutes all score 0, although their nearest pharmacy ranged from 10.0 to 22.9 minutes away.

The cutoff also changes the map. The rank correlation between counts within 5 and within 15 minutes was 0.811.

4. Compute a gravity score with a stated decay

A negative exponential with a half-life is the easiest decay to explain: a facility one half-life away counts as half, two half-lives as a quarter.

half-life 2.5 min: median 1.72, max  5.47, distinct values 1,906
half-life   5 min: median 5.14, max  9.95, distinct values 1,908
half-life  10 min: median 10.52, max 14.75, distinct values 1,909

Gravity scores separate almost every block, including the ones cumulative measures lump together: among residents with no pharmacy within 10 minutes, the 5-minute gravity score still ranged from 0.131 to 2.300. The half-life matters less than you might fear here โ€” scores with 2.5- and 10-minute half-lives correlated at 0.980 โ€” but it has to be chosen and reported.

5. Compare the rankings before choosing

Spearman rank correlation across blocks
  nearest ~ within 10    0.834
  nearest ~ gravity 5    0.858
  within 10 ~ gravity 5  0.965

Cumulative and gravity measures agree with each other much more than either agrees with nearest time, because both count several facilities.

6. Aggregate to the areas you will report

Programmes are usually targeted at tracts, not blocks. Take population-weighted means within each tract and compare quintiles:

tract Spearman nearest ~ within 10: 0.897; tracts in a different fifth: 19 of 40
tract Spearman nearest ~ gravity 5: 0.906; tracts in a different fifth: 22 of 40
tract Spearman within 10 ~ gravity 5: 0.971; tracts in a different fifth: 6 of 40

A correlation around 0.9 sounds like agreement. Half the tracts still changed class.

7. Choose the measure from the service, and say so

State the measure, the travel mode and speeds, the cutoff or half-life, and the facility list with its date. A published "access score" without those is unrepeatable.

Bar chart of the number of the county's 40 tracts that change quintile between each pair of accessibility measures.
High correlations still move half the tracts across a class boundary.

Code examples

Example 1 โ€” cumulative counts, with the ties they create

import numpy as np


def cumulative_access(T, w, cutoffs=(5, 10, 15)):
    """Facilities within each cutoff, and how coarse each count is."""
    out = {}
    for c in cutoffs:
        counts = (T <= c).sum(axis=1)
        none = w[counts == 0].sum() / w.sum()
        top_ties = int((counts == counts.max()).sum())
        print(f"within {c:>2} min: {len(np.unique(counts))} distinct values, "
              f"{none:.1%} of people with none, {top_ties} areas tied at the top ({counts.max()})")
        out[c] = counts
    return out
within  5 min: 11 distinct values, 31.4% of people with none, 3 areas tied at the top (11)
within 10 min: 19 distinct values, 9.5% of people with none, 43 areas tied at the top (18)
within 15 min: 22 distinct values, 3.0% of people with none, 32 areas tied at the top (21)

Few distinct values and large ties mean a quantile classification will split tied areas arbitrarily. That is a property of the measure, not of the classification.

Example 2 โ€” gravity scores with a half-life

def gravity_access(T, half_life):
    """Sum of facilities weighted by exp(-ln 2 * t / half_life); a facility one half-life away counts 0.5."""
    return np.exp(-np.log(2) / half_life * T).sum(axis=1)
scores = {h: gravity_access(T, h) for h in (2.5, 5, 10)}
zero = (T <= 10).sum(axis=1) == 0
print(f"no pharmacy within 10 min: gravity(5) from {scores[5][zero].min():.3f} to {scores[5][zero].max():.3f}")
no pharmacy within 10 min: gravity(5) from 0.131 to 2.300

Choosing a half-life is choosing a behaviour: 2.5 minutes says people barely consider a second pharmacy; 10 minutes says a pharmacy across town is nearly as good as the one down the road.

Example 3 โ€” how much the choice of measure moves a tract

import pandas as pd
from scipy.stats import spearmanr


def measure_agreement(areas, pairs):
    """Rank correlation and fifth-of-ranking changes between pairs of measures (higher = better access)."""
    fifths = {m: pd.qcut(areas[m].rank(method="first"), 5, labels=False) for m in areas}
    for a, b in pairs:
        rho = spearmanr(areas[a], areas[b])[0]
        moved = int((fifths[a] != fifths[b]).sum())
        print(f"{a} ~ {b}: rho {rho:.3f}; {moved} of {len(areas)} areas in a different fifth")
tracts = pd.DataFrame({"nearest": -tract_nearest, "within_10": tract_within_10, "gravity_5": tract_gravity_5})
measure_agreement(tracts, [("nearest", "within_10"), ("nearest", "gravity_5"), ("within_10", "gravity_5")])

Negate nearest time so that every column reads "higher is better". Run it before any list of priority areas is published.

Explanation

What each measure throws away

Nearest time keeps distance and discards choice. Cumulative counts keep choice and discard distance, both inside the cutoff and beyond it. Gravity keeps both, at the price of a decay parameter that has no natural value. None is neutral: each assumes something about how people choose where to go.

Why cumulative measures clump

A count of facilities within a cutoff can only take whole-number values up to the number of facilities in reach. In a county with 27 pharmacies, most blocks share a handful of values, and whole towns tie. Gravity scores are continuous, so almost every block gets its own value โ€” 1,908 distinct scores against 19 distinct counts.

Why block correlation overstates tract agreement

A rank correlation weighs every pair of areas equally, and most pairs are far apart in any sensible ranking, so the correlation stays high. Classification cares about the areas near each class boundary. With 40 tracts in five classes, a small reordering near each boundary moves many tracts, which is why a correlation of 0.906 coexisted with 22 class changes.

When none of these is enough

All three measures treat a facility as equally available to everyone who can reach it. A pharmacy in a dense neighbourhood serves more people than one in a village, so the same travel time buys less service. Measures that divide supply by the population competing for it โ€” the two-step floating catchment method โ€” address that, and they disagree with the simpler measures most in dense areas with few facilities.

Decision diagram choosing between nearest-facility time, cumulative opportunities, gravity accessibility and two-step floating catchment by the kind of service.
The service decides the measure: whether people need one facility, a choice, or a share of limited capacity.

Edge cases or notes

  • Report unreachable areas separately. One block could not reach any pharmacy; leaving it as infinity breaks means and rankings.
  • Cutoffs create cliffs. A home at 10.1 minutes scores like a home at 30 minutes under a 10-minute cumulative measure.
  • Half-lives are not universal. Driving, walking and public transport need different decays.
  • Facilities outside the study area count. Omitting them understates access near every boundary.
  • Population weighting changes medians. Report both area-weighted and population-weighted summaries if areas differ in size.
  • Speeds are a model. Free-flow network speeds understate peak travel times, which lowers every measure at busy hours.
  • Gravity sums grow with the number of facilities. Compare scores only within one facility set.

FAQ

What is an accessibility measure?

A score for each place describing how easily its residents can reach a type of facility. The standard forms are time to the nearest facility, the number within a cutoff, and a distance-weighted sum of all facilities.

Which accessibility measure should I use?

Match it to the service. Nearest time suits services where people use the closest one; cumulative counts suit standards such as two supermarkets within 10 minutes; gravity suits choice with no hard cutoff.

Do different measures give different results?

At the margins, yes. For 40 tracts, nearest time and a gravity score correlated at 0.906, yet 22 tracts fell in a different fifth of the ranking.

What is a gravity accessibility score?

A sum over all facilities in which each is weighted by a decay of travel time. With a half-life of 5 minutes, a pharmacy 5 minutes away counts as half and one 10 minutes away as a quarter.

Why do cumulative measures have so many ties?

They are whole-number counts. Across 2,240 blocks the count of pharmacies within 10 minutes took only 19 values, and 43 blocks tied at the maximum.

How do I choose a half-life or cutoff?

From travel behaviour or a service standard, and then test the sensitivity. Here gravity scores with 2.5- and 10-minute half-lives correlated at 0.980, while counts within 5 and 15 minutes correlated at 0.811.