Skip to content

Neighborhood scores

The earlier stages score individual neighbourhoods. This final stage aggregates those per-block scores into city-level summary statistics — the overall 0–100 rating and per-category scores that appear in PeopleForBikes city rankings.

The headline BNA score

The overall BNA score is a population-weighted average of every block's overall score, restricted to blocks that have at least one reachable block (connected to the network):

overall_score = Σ(block.overall_score / 100 × block.pop20) / Σ(pop20 for reachable blocks)

Why population-weighted? Blocks with more residents represent more people's daily experience. A 70-score block with 1,000 residents matters more than a 90-score block with 50 residents.

Why exclude unconnected blocks? Blocks with reachable_blocks == 0 have no bicycle connectivity at all — no roads connect them to any destination. Including them would penalise cities for having a few isolated parcels at the fringe, which isn't a useful signal.

The score is on a 0–100 scale. Higher is better.

Category and sub-category scores

The pipeline computes scores at three levels:

Level Examples
Sub-category opportunity_k12_education, core_services_grocery
Category opportunity, core_services, recreation
Overall overall_score

Each sub-category score is the population-weighted average of per-block scores for that destination type, using only the population of blocks that can reach at least one such destination by high-stress cycling (i.e. blocks where at least one destination is theoretically reachable):

sub_score = Σ(pop20 × dest_score) / Σ(pop20 for blocks where dest_high_stress > 0)

Category scores combine sub-category scores with fixed weights — but only for sub-categories where destinations actually exist in the city. Missing destinations are dropped from the denominator, so a city without hospitals still gets a valid core_services score:

Category Sub-categories and weights
Opportunity Employment 35%, K12 schools 35%, Tech colleges 10%, Universities 20%
Core services Doctors 20%, Dentists 10%, Hospitals 20%, Pharmacies 10%, Grocery 25%, Social services 15%
Recreation Parks 40%, Trails 35%, Community centers 25%

What the score_inputs table contains

neighborhood_score_inputs has 132 rows, one per statistical measure across all destination types. For each destination there are typically:

  • Percentile ratios (median, 70th, 30th): distribution of the per-block low/high ratio
  • Average ratio: city-wide sum(low_stress) / sum(high_stress) (integer division for destination counts)
  • Population-weighted average: the sub-category score (has a use_* flag set to True)
  • Bike-shed statistics: destination-level measures using pop_low_stress / pop_high_stress at each destination point (not available for trails)

Only the rows with use_* flags set feed into neighborhood_overall_scores.

Mileage statistics

The mileage table reports total miles of bike infrastructure by type:

Type Description
lane Dedicated bike lane
buffered_lane Buffered bike lane
track Protected cycle track
sharrow Shared-lane marking
path Off-street path (excludes crosswalks when xwalk data is available)

Mileage measures infrastructure quantity — not quality or connectivity. A city can have many miles of sharrows but a low overall score if those sharrows don't connect people to destinations.

total_miles_low_stress and total_miles_high_stress in neighborhood_overall_scores measure the total road network miles that are low-stress or high-stress cycling, clipped to the city boundary.

Score normalization

score_normalized = score_original × 100 for all sub-category and category scores.

Exceptions: - population_total: no normalization (raw count) - total_miles_*: rounded to 1 decimal place

Relationship to brokenspoke-analyzer

The SQL scripts this stage replaces — and any points where the output intentionally differs — are catalogued in the Differences from brokenspoke-analyzer section.