Intro to accessibility: Transport Accessibility Metrics

Rafael H. M. Pereira, Daniel Herszenhut

2022-06-29

1. Introduction

accessibility is an R package that offers a set of fast and convenient functions to calculate multiple transport accessibility measures. Given a pre-computed travel cost matrix in long format combined with land-use data (e.g. location of jobs, healthcare, population), the package allows one to calculate active and passive accessibility levels using multiple accessibility metrics such as: cumulative opportunity measure (using either travel time cutoff or interval), minimum travel cost to closest N number of activities, gravitational measures and different floating catchment area methods.

This vignette gives a brief introduction to the accessibility package with a reproducible example.

2. Installation

You can install accessibility from CRAN, or use the development version from github.

# CRAN
install.packages('accessibility')

# github
devtools::install_github("ipeaGIT/accessibility", subdir = "r-package")

3. Overview of the functions

The accessibility package currently includes:

  1. time_to_closest() Minimum travel cost to closest N number of opportunities
  2. cumulative_time_cutoff(). Traditional time threshold-based cumulative opportunity metric
  3. cumulative_time_interval(). A novel time interval-based cumulative opportunity metric (forthcoming paper)
  4. gravity_access() Gravity-based accessibility
  5. floating_catchment_area() Floating catchment area accessibility using 2SFCA
  6. floating_catchment_area() Floating catchment area accessibility using BFCA

The gravity_access() and floating_catchment_area() functions can use any custom decay function that converts travel cost t_ij to and impedance factor. For convenience, the package currently includes the following decay functions:

  1. decay_binary() Binary (aka step) decay function
  2. decay_exponential() Negative exponential decay function
  3. decay_linear() Linear decay function
  4. decay_power(). Inverse power decay function

5. Demonstration on sample data

Let’s first load the libraries used in this vignette:

library(accessibility)
library(data.table)
library(ggplot2)
library(sf)

Data requirements:

To use accessibility, you will need a pre-computed travel cost matrix in long format combined with land-use data (e.g. location of jobs, healthcare, population). Travel costs can be presented in terms of travel times, distances or monetary costs. This input data must be presented as a data.frame containing the columns with origin ids, destination ids, the travel cost between each origin-destination pair, and the number of opportunities located in each destination. Note that to to calculate floating_catchment_area(), your input data should also include a column with the size of the population in each origin.

The data input should look similar to this:

data_path <- system.file("extdata/ttm_bho.rds", package = "accessibility")
ttm <- readRDS(data_path)
head(ttm)
#>            from_id           to_id travel_time population jobs schools
#> 1: 89a88cdb57bffff 89a88cdb57bffff         5.8        606   82       0
#> 2: 89a88cdb57bffff 89a88cdb597ffff        47.0        606  308       2
#> 3: 89a88cdb57bffff 89a88cdb5b3ffff        48.0        606  100       0
#> 4: 89a88cdb57bffff 89a88cdb5cfffff        47.0        606  109       0
#> 5: 89a88cdb57bffff 89a88cd909bffff        64.0        606    0       0
#> 6: 89a88cdb57bffff 89a88cd90b7ffff        59.0        606  480       0

This is a small sample data with public transport travel times and land use data for the city of Belo Horizonte (Brazil) included in the package and that we will be using to illustrate its functionalities.

If you would like to estimate such travel cost matrices, there are several computational packages to do that in R, such as: - r5r - dodgr - gtfsrouter - hereR - opentripplanner

5.1 Minimum travel cost:

time_to_closest() allows you to easily calculate the minimum travel cost accessibility. In the example below, the output shows the minimum travel cost from each origin to the closet school and the id of the destination where the school is located.

tmi <- time_to_closest(data = ttm,
         opportunity_col = 'schools',
         n_opportunities = 1,
         travel_cost_col = 'travel_time',
         by_col = 'from_id'
         )

head(tmi)
#>            from_id travel_cost     destination
#> 1: 89a88cdb57bffff        28.0 89a88cdb53bffff
#> 2: 89a88cdb597ffff         5.8 89a88cdb597ffff
#> 3: 89a88cdb5b3ffff        11.0 89a88cdb5bbffff
#> 4: 89a88cdb5cfffff        13.0 89a88cdb5c7ffff
#> 5: 89a88cd909bffff         7.0 89a88cd9093ffff
#> 6: 89a88cd90b7ffff         6.0 89a88cd90a7ffff

5.2 Cumulative opportunity measures:

Time threshold

The cumulative_time_cutoff() functions calculates traditional cumulative opportunity accessibility, indicating the number of opportunities that are accessible in under a given travel cost threshold. In this example, we estimate the number of jobs accessible in up to 30 minutes.

cum_cutoff <- cumulative_time_cutoff(
                data = ttm,
                cutoff = 30,
                opportunity_col = 'jobs',
                travel_cost_col = 'travel_time',
                by_col = 'from_id'
                )

head(cum_cutoff)
#>            from_id access
#> 1: 89a88cdb57bffff  22239
#> 2: 89a88cdb597ffff  36567
#> 3: 89a88cdb5b3ffff  42372
#> 4: 89a88cdb5cfffff  55571
#> 5: 89a88cd909bffff  26774
#> 6: 89a88cd90b7ffff  36991

Time interval

cumulative_time_interval() calculates the the time interval cumulative accessibility measure. This is a novel accessibility metric developed by Tomasiello et al. (forthcoming paper) to mitigate the impacts of arbitrary choices of trip duration in traditional threshold-based cumulative opportunity metrics. In the example below, we calculate the average number of jobs accessible considering multiple minute-by-minute time thresholds between 20 and 60 minutes.

cum_interval <- cumulative_time_interval(
                  data = ttm,
                  interval = c(20, 60),
                  stat = 'mean',
                  opportunity_col = 'jobs',
                  travel_cost_col = 'travel_time',
                  by_col = 'from_id'
                  )

head(cum_interval)
#>            from_id   access
#> 1: 89a88cdb57bffff 177395.5
#> 2: 89a88cdb597ffff 145321.4
#> 3: 89a88cdb5b3ffff 178228.1
#> 4: 89a88cdb5cfffff 229087.1
#> 5: 89a88cd909bffff 179526.9
#> 6: 89a88cd90b7ffff 204975.2

5.3 Gravity-based accessibility:

The gravity_access() allows one to continuously discount the weight of opportunities as travel costs become larger. See the vignette on decay functions for more information on the decay functions available in the package and how to use custom functions. In this case here, we use a negative exponential function with a decay_value of 0.2

grav_exp <- gravity_access(
              data = ttm,
              opportunity_col = 'jobs',
              decay_function = decay_exponential(decay_value = 0.2),
              travel_cost_col = 'travel_time',
              by_col = 'from_id'
              )

head(grav_exp)
#>            from_id    access
#> 1: 89a88cdb57bffff  413.5854
#> 2: 89a88cdb597ffff  729.7845
#> 3: 89a88cdb5b3ffff  744.2263
#> 4: 89a88cdb5cfffff  886.9436
#> 5: 89a88cd909bffff  543.4047
#> 6: 89a88cd90b7ffff 1947.6067

5.4 Floating catchment accessibility:

The floating_catchment_area() functions provides users with floating catchment area (FCA) indicators that allow you to calculate accessibility accounting for the competition of resources. The use can use the parameter fca_metric to specify which indicator of the broad FCA family should be used. The package currently supports two FCA metrics.

2SFCA: 2-Step Floating Catchment Area

2SFCA was the first accessibility metric in the floating catchment area family. It was originally proposed by Luo & Wang (Luo and Wang 2003).

fca_2sfca <- floating_catchment_area(
                data = ttm,
                fca_metric = '2SFCA',
                population_col = 'population', 
                opportunity_col = 'schools',
                decay_function = decay_exponential(decay_value = 0.2),
                travel_cost_col = 'travel_time',
                orig_col = 'from_id',
                dest_col = 'to_id'
                )

head(fca_2sfca)
#>            from_id   access_2sfca
#> 1: 89a88cdb57bffff 0.000008219232
#> 2: 89a88cdb597ffff 0.000392296809
#> 3: 89a88cdb5b3ffff 0.000181524093
#> 4: 89a88cdb5cfffff 0.000099700375
#> 5: 89a88cd909bffff 0.000142171505
#> 6: 89a88cd90b7ffff 0.000221000580

BFCA - Balanced Floating Catchment Area

The BFCA metric calculates accessibility accounting for competition effects while simultaneously correcting for issues of inflation of demand and service levels that are present in previous floating catchment area measures. It is the most recent FCA metric and it was originally proposed by Paez et al. (Paez, Higgins, and Vivona 2019) and named in Pereira et al. (Pereira et al. 2021).

fca_bfca <- floating_catchment_area(
                data = ttm,
                fca_metric = 'BFCA',
                population_col = 'population', 
                opportunity_col = 'schools',
                decay_function = decay_exponential(decay_value = 0.2),
                travel_cost_col = 'travel_time',
                orig_col = 'from_id',
                dest_col = 'to_id'
                )

head(fca_bfca)
#>            from_id    access_bfca
#> 1: 89a88cdb57bffff 0.000008953755
#> 2: 89a88cdb597ffff 0.000357273221
#> 3: 89a88cdb5b3ffff 0.000171592450
#> 4: 89a88cdb5cfffff 0.000121565261
#> 5: 89a88cd909bffff 0.000143809692
#> 6: 89a88cd90b7ffff 0.000203323698

Visualize results

If you have a spatial data with the geometries of your origin/destination data set, you can easily merge it with the accessibility to map the results. Here is a quick example using ggplot2.

library(ggplot2)
library(sf)
library(data.table)

# load spatial data
grid <- system.file("extdata/grid_bho.rds", package = "accessibility")
grid <- readRDS(grid)

# merge accessibility output to spatial data
df <- merge(grid, cum_cutoff, by.x='id', by.y='from_id')

# for large data sets, this can be done much faster with `data.table` as follows:
df2 <- setDT(grid)[cum_cutoff, on=c('id'='from_id'), access := i.access]
df2 <- st_sf(df2, crs = 4326)


# plot
ggplot() +
  geom_sf(data = df2, aes(fill = access), color=NA, alpha=0.9) +
  labs(title = 'Employment accessibility by transit in under 30 min.', 
       fill='Number of jobs\naccessible') +
  scale_fill_viridis_c() + 
  theme_void()

If you have any suggestions or want to report an error, please visit the package GitHub page.

References

Luo, Wei, and Fahui Wang. 2003. “Measures of Spatial Accessibility to Health Care in a GIS Environment: Synthesis and a Case Study in the Chicago Region.” Environment and Planning B: Planning and Design 30 (6): 865–84.
Paez, Antonio, Christopher D Higgins, and Salvatore F Vivona. 2019. “Demand and Level of Service Inflation in Floating Catchment Area (FCA) Methods.” Plos One 14 (6): e0218773.
Pereira, Rafael HM, Carlos Kauê Vieira Braga, Luciana Mendes Servo, Bernardo Serra, Pedro Amaral, Nelson Gouveia, and Antonio Paez. 2021. “Geographic Access to COVID-19 Healthcare in Brazil Using a Balanced Float Catchment Area Approach.” Social Science & Medicine 273: 113773.