Skip to contents

spopt provides R-native implementations of spatial optimization algorithms for regionalization, facility location, and market analysis. The package is inspired by Python’s PySAL spopt library, my all-time favorite Python package. Its aim is to bring spopt’s powerful algorithms to R users with an sf-first API and a Rust backend for performance.

Installation

The easiest way to install spopt is from my r-universe repository, which provides pre-built binaries:

install.packages("spopt", repos = "https://walkerke.r-universe.dev")

Once installed, load the package along with sf for spatial data handling and tidyverse for data manipulation:

What can spopt do?

spopt includes three families of spatial optimization algorithms:

Regionalization: Build spatially-contiguous regions from smaller geographies. This is useful when you need to aggregate Census blocks into larger areas, create balanced sales territories, or design compact political districts. Algorithms include SKATER, AZP, Max-P, SPENC, and spatially-constrained Ward clustering.

Facility location: Find optimal locations for facilities given demand points and candidate sites. Whether you’re siting fire stations to minimize response times, placing retail stores to maximize coverage, or locating EV charging stations along highway corridors, spopt has algorithms for these problems. Options include P-Median, P-Center, MCLP, LSCP, CFLP, P-Dispersion, and FRLM.

Market analysis: Model consumer behavior and market competition with the Huff model. This classic retail gravity model predicts market share and expected sales based on store attractiveness and distance to consumers.

A quick example

Let’s run a quick facility location analysis to see spopt in action. We’ll find optimal locations for 5 facilities to serve Census tracts in Tarrant County, Texas (home of Fort Worth).

library(tidycensus)

# Get population data for Tarrant County tracts
tarrant <- get_acs(
  geography = "tract",
  variables = "B01003_001",
  state = "TX",
  county = "Tarrant",
  geometry = TRUE,
  year = 2023
)

# Use tract centroids as both demand points and candidate facility sites
tarrant_pts <- tarrant |>
  st_centroid() |>
  filter(!is.na(estimate))

# Solve the P-Median problem: minimize total weighted distance
result <- p_median(
  demand = tarrant_pts,
  facilities = tarrant_pts,
  n_facilities = 5,
  weight_col = "estimate"
)

# View selected facility locations
result$facilities |> filter(.selected)
Simple feature collection with 5 features and 7 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -97.40206 ymin: 32.66538 xmax: -97.12351 ymax: 32.89751
Geodetic CRS:  NAD83
        GEOID                                        NAME   variable estimate
1 48439111516 Census Tract 1115.16; Tarrant County; Texas B01003_001     7137
2 48439113637 Census Tract 1136.37; Tarrant County; Texas B01003_001     4732
3 48439110402 Census Tract 1104.02; Tarrant County; Texas B01003_001     5387
4 48439105800    Census Tract 1058; Tarrant County; Texas B01003_001     4430
5 48439113946 Census Tract 1139.46; Tarrant County; Texas B01003_001     6509
   moe                   geometry .selected .n_assigned
1 1007 POINT (-97.12453 32.66618)      TRUE         112
2  703 POINT (-97.12351 32.84844)      TRUE          90
3   16 POINT (-97.40206 32.80275)      TRUE          69
4  698 POINT (-97.34284 32.66538)      TRUE         107
5 3880 POINT (-97.28117 32.89751)      TRUE          71

The p_median() function returns a list with allocated demand points and selected facilities. Each demand point is assigned to its nearest selected facility, and the solution minimizes the total population-weighted distance.

SF-first design

All spopt functions accept and return sf objects. This means you can:

  • Pass sf polygons or points directly to functions
  • Use any coordinate reference system (functions handle transformations internally)
  • Pipe results directly into visualization with ggplot2, mapview, or mapgl
  • Integrate seamlessly with tidycensus, tigris, and other spatial R packages

Next steps