Spatial Analysis with nomisdata

Introduction

The nomisdata package integrates with R’s spatial ecosystem, particularly the sf package, to enable geographic analysis and mapping of UK labour market data.

Getting Spatial Data

Fetch with Boundaries

library(nomisdata)
library(sf)
library(ggplot2)

# Download data with KML boundaries
unemployment_spatial <- fetch_spatial(
  "NM_1_1",
  time = "latest",
  geography = "TYPE480",  # Regions
  measures = 20201,       # Rate
  sex = 7,
  parse_sf = TRUE         # Parse to sf object
)

# Check the object
class(unemployment_spatial)
#> [1] "sf"         "data.frame"

st_crs(unemployment_spatial)
#> Coordinate Reference System:
#>   WGS 84 / Pseudo-Mercator

Basic Mapping

# Simple choropleth
ggplot(unemployment_spatial) +
  geom_sf(aes(fill = OBS_VALUE), colour = "white", size = 0.3) +
  scale_fill_viridis_c(option = "magma", name = "Rate (%)") +
  labs(
    title = "Unemployment Rate by Region",
    caption = "Source: Nomis / ONS"
  ) +
  theme_void() +
  theme(
    plot.title = element_text(hjust = 0.5, face = "bold", size = 14),
    legend.position = "right"
  )

Advanced Spatial Analysis

Joining Non-Spatial and Spatial Data

# Get detailed data without boundaries
detailed_data <- fetch_nomis(
  "NM_1_1",
  time = "latest",
  geography = "TYPE464",  # Local authorities
  measures = c(20100, 20201, 20203),
  sex = 7
)

# Get boundaries separately (lighter download)
boundaries <- fetch_spatial(
  "NM_1_1",
  time = "latest",
  geography = "TYPE464",
  measures = 20100,  # Just need one measure for boundaries
  sex = 7
)

# Join
combined <- boundaries |>
  select(GEOGRAPHY_CODE, geometry) |>
  left_join(
    detailed_data |> select(GEOGRAPHY_CODE, MEASURES_NAME, OBS_VALUE),
    by = "GEOGRAPHY_CODE"
  )

Spatial Aggregation

# Aggregate local authorities to regional level
la_data <- fetch_spatial(
  "NM_1_1",
  time = "latest",
  geography = "TYPE464",
  measures = 20100,
  sex = 7
)

# Get regional boundaries
regional_bounds <- fetch_spatial(
  "NM_1_1",
  time = "latest",
  geography = "TYPE480",
  measures = 20100,
  sex = 7
)

# Spatial join and aggregate
regional_aggregated <- st_join(
  la_data,
  regional_bounds |> select(region_name = GEOGRAPHY_NAME),
  join = st_within
) |>
  st_drop_geometry() |>
  group_by(region_name) |>
  summarise(total_claimants = sum(OBS_VALUE, na.rm = TRUE)) |>
  left_join(regional_bounds, by = c("region_name" = "GEOGRAPHY_NAME"))

Distance Analysis

# Calculate distance to nearest urban center
urban_centers <- data.frame(
  name = c("London", "Manchester", "Birmingham"),
  lon = c(-0.1276, -2.2426, -1.8904),
  lat = c(51.5074, 53.4808, 52.4862)
) |>
  st_as_sf(coords = c("lon", "lat"), crs = 4326)

# Calculate distances
la_centroids <- st_centroid(la_data)
distances <- st_distance(la_centroids, urban_centers)

# Add minimum distance to data
la_data$dist_to_urban <- apply(distances, 1, min)

Interactive Maps

library(leaflet)

# Create interactive map
unemployment_spatial |>
  st_transform(4326) |>  # Transform to WGS84 for leaflet
  leaflet() |>
  addTiles() |>
  addPolygons(
    fillColor = ~colorNumeric("YlOrRd", OBS_VALUE)(OBS_VALUE),
    fillOpacity = 0.7,
    color = "white",
    weight = 1,
    label = ~paste0(GEOGRAPHY_NAME, ": ", round(OBS_VALUE, 2), "%"),
    highlightOptions = highlightOptions(
      weight = 3,
      color = "#666",
      fillOpacity = 0.9,
      bringToFront = TRUE
    )
  ) |>
  addLegend(
    pal = colorNumeric("YlOrRd", unemployment_spatial$OBS_VALUE),
    values = ~OBS_VALUE,
    title = "Unemployment Rate (%)",
    position = "bottomright"
  )

Limitations

Resources