lehdr

CRAN_Status_Badge metacran downloads R-CMD-check pkgdown

lehdr (pronounced: lee dur like a metric litre) is an R package that allows users to interface with the Longitudinal Employer-Household Dynamics (LEHD) Origin-Destination Employment Statistics (LODES) dataset, returned as tidy data frames. The package is designed to download LODES flat files (origin-destination, workplace area characteristics, and residential area characteristics), aggregate them to common Census geographies, and now includes built-in analytical functions for commute flow statistics, longitudinal job change, and earnings tier shares.

Full documentation and the Getting Started vignette are available at the lehdr pkgdown site. For a detailed walkthrough of all analytical functions and maps, see the Getting Started vignette.

Installation

You can install the released version of lehdr from CRAN with:

install.packages("lehdr")

And the development version from GitHub with:

# install.packages("pak")
pak::pak("jamgreen/lehdr")

Usage

Load the library and optionally set the lehdr_use_cache option to save downloaded files for reuse across sessions:

library(lehdr)

options(lehdr_use_cache = TRUE)

The grab_lodes() function downloads data for a specific state, year, and LODES version. The table type is set with lodes_type: origin-destination ("od"), residential area characteristics ("rac"), or workplace area characteristics ("wac").

For example, Oregon (state = "or") for 2020 (year = 2020) from LODES version 8 (version = "LODES8", the default), origin-destination (lodes_type = "od"), primary jobs (job_type = "JT01", the default), all worker segments (segment = "S000", the default), aggregated to the Census tract level (agg_geo = "tract"):

or_od <- grab_lodes(
  state      = "or",
  year       = 2020,
  version    = "LODES8",
  lodes_type = "od",
  job_type   = "JT01",
  segment    = "S000",
  state_part = "main",
  agg_geo    = "tract"
)

head(or_od)

Pass character vectors to state and numeric vectors to year to retrieve multiple states and years in a single call:

or_ri_od <- grab_lodes(
  state      = c("or", "ri"),
  year       = c(2013, 2014),
  lodes_type = "od",
  job_type   = "JT01",
  segment    = "S000",
  state_part = "main",
  agg_geo    = "tract"
)

head(or_ri_od)

Not all years are available for each state. For the full availability matrix, see the LEHD Technical Document at https://lehd.ces.census.gov/data/lodes/LODES8/.

Set geometry = TRUE to join Census geometries via the tigris package. When lodes_type = "rac" or "wac", grab_lodes() returns an sf data frame directly:

ri_rac_geo <- grab_lodes(
  state      = "ri",
  year       = 2020,
  lodes_type = "rac",
  agg_geo    = "county",
  geometry   = TRUE
)

plot(ri_rac_geo["C000"])

For lodes_type = "od", separate h_geometry and w_geometry columns are returned. These can be combined into flow lines:

or_od_geo <- grab_lodes(
  state      = "or",
  year       = 2020,
  lodes_type = "od",
  agg_geo    = "county",
  geometry   = TRUE,
  state_part = "main"
)

h_to_w_geometry <- lapply(
  seq(nrow(or_od_geo)),
  function(i) {
    sf::st_linestring(
      c(
        sf::st_centroid(or_od_geo[["h_geometry"]][[i]]),
        sf::st_centroid(or_od_geo[["w_geometry"]][[i]])
      )
    )
  }
)

h_to_w_lines <- sf::st_as_sfc(h_to_w_geometry, crs = 4269)

or_od_lines <- sf::st_set_geometry(
  or_od_geo[, c("w_county", "h_county", "h_geometry", "S000")],
  h_to_w_lines
)

multnomah_od_lines <- dplyr::filter(or_od_lines, w_county == "41051")

plot(multnomah_od_lines["S000"], reset = FALSE)
plot(or_od_geo["h_geometry"], lwd = 0.25, add = TRUE)

The optional version parameter selects the LODES vintage. LODES8 (default) uses 2020 Census blocks. LODES7 uses 2010 Census blocks and ends in 2019. LODES5 uses 2000 Census blocks and ends in 2009.

Analytical Functions

lehdr includes three functions for common LODES analyses.

Commute flow statistics

compute_commute_stats() takes an OD data frame and returns per-geography inflow, outflow, internal flow, net flow, and self-containment ratio. Self-containment is the share of employed residents who also work within the same geographic unit.

od_md <- grab_lodes(
  state      = "md",
  year       = 2019,
  lodes_type = "od",
  job_type   = "JT00",
  segment    = "S000",
  state_part = "main",
  agg_geo    = "county"
)

commute_md <- compute_commute_stats(od_md, agg_geo = "county")

commute_md |>
  dplyr::arrange(dplyr::desc(self_containment)) |>
  dplyr::select(county, workers_in, workers_out, net_flow, self_containment) |>
  head(10)

The map below shows self-containment at the Census tract level for Baltimore City, Maryland (FIPS 24510):

Longitudinal change

compute_lodes_change() computes absolute and percentage change in any LODES variable between two years. Output can be wide (one row per geography) or long (suitable for ggplot2).

wac_md_panel <- grab_lodes(
  state      = "md",
  year       = c(2010, 2019),
  lodes_type = "wac",
  job_type   = "JT00",
  segment    = "S000",
  agg_geo    = "county"
)

change_md <- compute_lodes_change(
  wac_md_panel,
  geo_col      = "w_county",
  base_year    = 2010,
  compare_year = 2019,
  variables    = c("C000", "CE01", "CE02", "CE03")
)

change_md |>
  dplyr::arrange(dplyr::desc(C000_change)) |>
  dplyr::select(w_county, C000_base, C000_compare, C000_change, C000_pct_change) |>
  head(10)

The map below shows percent change in total jobs across Maryland counties from 2010 to 2019:

Earnings tier shares

compute_earnings_share() computes the distribution of jobs across LODES earnings tiers: low (up to $1,250/month), mid ($1,251-$3,333/month), and high (above $3,333/month). Use output = "long" for a ggplot2-ready format.

wac_md <- grab_lodes(
  state      = "md",
  year       = 2019,
  lodes_type = "wac",
  job_type   = "JT00",
  segment    = "S000",
  agg_geo    = "county"
)

earn_shares <- compute_earnings_share(wac_md, type = "wac", geo_col = "w_county")

earn_shares |>
  dplyr::arrange(dplyr::desc(share_low)) |>
  dplyr::select(w_county, share_low, share_mid, share_high) |>
  head(10)

Mapping LODES Data

The figures above were produced with ggplot2 and tigris. The general pattern is: download with grab_lodes(), compute a derived variable, fetch geometries with tigris::tracts() or tigris::counties(), join on GEOID, and plot with geom_sf(). See vignette("getting_started") for full figure code.

The map below shows a flow-weighted job accessibility index for Baltimore City tracts, computed directly from LODES OD data:

Maps

All figures can be reproduced by running source("data-raw/render_vignette_figures.R") locally. The map below shows self-containment at the Census tract level for Baltimore City, Maryland — a simple but powerful use of LODES OD data to understand which neighborhoods have strong local job access versus heavy out-commuting:

See vignette("getting_started") for the full figure code and additional maps of Maryland county job change, earnings tier composition, and tract-level job accessibility.

Caching

Downloaded files are deleted after reading by default. Set use_cache = TRUE in grab_lodes() or options(lehdr_use_cache = TRUE) globally to retain files in the user cache directory (tools::R_user_dir("lehdr", "cache")). Cache filenames include the LODES version (e.g., lodes8_md_wac_S000_JT00_2019.csv.gz), so switching between versions will not serve stale data from a different vintage.

Upgrading from lehdr < 1.2.0? Earlier versions cached files without a version prefix (e.g., md_wac_S000_JT00_2019.csv.gz). Those files will never be matched by the new naming scheme and can be safely deleted. To clear the entire cache:

cache_dir <- tools::R_user_dir("lehdr", "cache")
list.files(cache_dir)          # inspect before deleting
unlink(cache_dir, recursive = TRUE)

Why lehdr?

The LODES dataset is frequently used by transportation and economic development planners, regional economists, disaster managers, and other public servants who need fine-grained understanding of the spatial distribution of employment. Such data underpins regional travel demand models, workforce and industrial policy analysis, and, as a Census product, can be joined to Decennial or American Community Survey data to illuminate the relationship between population and employment geography.

LODES is the only source of detailed geographic information on employment for the entire country, and lehdr is designed to make it more accessible for researchers and analysts working on regional development issues.

Citation

If you use lehdr in published work, please cite it:

citation("lehdr")

Green, Jamaal, Liming Wang, and Dillon Mahmoudi. 2025. “lehdr: Grab Longitudinal Employer-Household Dynamics (LEHD) Flat Files.” R package version 1.2.0. https://github.com/jamgreen/lehdr/

Acknowledgements

This package was developed by Jamaal Green, University of Pennsylvania; Dillon Mahmoudi, University of Maryland Baltimore County; and Liming Wang, Portland State University.

This package would not exist in its current format without the inspiration of Bob Rudis’s lodes package.