A fast alternative to do.call(rbind, <list_of_sf>)
st_rbindlist(l, ..., geometry_name = NULL)
list
of objects of the class sf
arguments of the function rbindlist
except l
a single character string giving a user-defined name to
the returned active list-column with simple feature geometries. If
unspecified / by default (NULL
), the geometry column name is inherited
from the first sf
object list
ed in the input.
a sf
object
st_rbindlist()
is basically a wrapper for
sf::st_as_sf(data.table::rbindlist(<list_of_sf>))
, which is a fast
alternative to do.call(rbind, <list_of_sf>)
. It comes with some
properties which rbind
has, but rbindlist
misses:
checks if all sf
objects included in the input share the same
CRS
returns a sf
object with a correct bbox
(more
information on how to handle this rbindlist
issue can be found
here).
allows binding sf
objects with different geometry types
Remarks about matching geometry columns of input sf
objects:
If the attribute columns are identically named, but differ by
position within
each sf
object, set use.names = TRUE
and fill = TRUE
.
If the geometry and/or attribute columns are differently named, but
have
same position within every sf
object, set use.names = FALSE
.
In any case the active geometry columns of the listed
sf
-objects are matched due to the identical internal naming of
these columns.
library(sf)
# a set of geometries to work with
nc <- st_read(system.file("gpkg/nc.gpkg", package = "sf"), quiet = TRUE)
# proof of concept with a list of sf-objects having identically named and
# positioned attribute and geometry columns:
list_of_sf <- lapply(1:nrow(nc), function(x) nc[x, ])
nc_new <- st_rbindlist(list_of_sf)
all.equal(nc, nc_new)
#> [1] TRUE
# a list of sf-objects having ...
l <- lapply(
1:3,
function(x){data.frame(nc[x, 1:3]) %>% setNames(., c(paste0(names(.), "_", x))) %>% st_sf()}
)
# ... differently named, but identically positioned geometry & attribute columns
sapply(l, names) %>% t()
#> [,1] [,2] [,3] [,4]
#> [1,] "AREA_1" "PERIMETER_1" "CNTY__1" "geom_1"
#> [2,] "AREA_2" "PERIMETER_2" "CNTY__2" "geom_2"
#> [3,] "AREA_3" "PERIMETER_3" "CNTY__3" "geom_3"
# by setting use.name = FALSE such a list of sf-objects is easy to stack:
st_rbindlist(l, use.name = FALSE)
#> Simple feature collection with 3 features and 3 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -81.74107 ymin: 36.23388 xmax: -80.43531 ymax: 36.58965
#> Geodetic CRS: NAD27
#> AREA_1 PERIMETER_1 CNTY__1 geom_1
#> 1 0.114 1.442 1825 MULTIPOLYGON (((-81.47276 3...
#> 2 0.061 1.231 1827 MULTIPOLYGON (((-81.23989 3...
#> 3 0.143 1.630 1828 MULTIPOLYGON (((-80.45634 3...
# from which listed element / sf-object do the stacked features originate?
l <- list(A = nc[1, 1:3], B = NULL, C = nc[2:3, 1:3])
st_rbindlist(l, idcol = "ID")
#> Simple feature collection with 3 features and 4 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -81.74107 ymin: 36.23388 xmax: -80.43531 ymax: 36.58965
#> Geodetic CRS: NAD27
#> ID AREA PERIMETER CNTY_ geom
#> 1 A 0.114 1.442 1825 MULTIPOLYGON (((-81.47276 3...
#> 2 C 0.061 1.231 1827 MULTIPOLYGON (((-81.23989 3...
#> 3 C 0.143 1.630 1828 MULTIPOLYGON (((-80.45634 3...
# list of sf-objects with identically named, but differently positioned
# geometry and attribute columns (the latter might not be included in
# each listed sf-object) ...
l <- list(nc[1, NULL], nc[2, 1], nc[3, 2:3])
# ... requires use.name = TRUE and fill = TRUE:
st_rbindlist(l, use.name = TRUE, fill = TRUE, idcol = TRUE)
#> Simple feature collection with 3 features and 4 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -81.74107 ymin: 36.23388 xmax: -80.43531 ymax: 36.58965
#> Geodetic CRS: NAD27
#> .id AREA PERIMETER CNTY_ geom
#> 1 1 NA NA NA MULTIPOLYGON (((-81.47276 3...
#> 2 2 0.061 NA NA MULTIPOLYGON (((-81.23989 3...
#> 3 3 NA 1.63 1828 MULTIPOLYGON (((-80.45634 3...
# list of sf-objects with differently named and positioned geometry columns,
# and (if existing) identically named attribute columns ...
l <- lapply(
seq_along(l),
function(x) st_set_geometry(l[[x]], paste0("geom_", x))
)
sapply(l, attr, "sf_column") # geometry column names of listed sf-objects
#> [1] "geom_1" "geom_2" "geom_3"
# ... can be handled also by setting use.name = TRUE and fill = TRUE:
st_rbindlist(l, use.name = TRUE, fill = TRUE, idcol = TRUE)
#> Simple feature collection with 3 features and 4 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -81.74107 ymin: 36.23388 xmax: -80.43531 ymax: 36.58965
#> Geodetic CRS: NAD27
#> .id AREA PERIMETER CNTY_ geom_1
#> 1 1 NA NA NA MULTIPOLYGON (((-81.47276 3...
#> 2 2 0.061 NA NA MULTIPOLYGON (((-81.23989 3...
#> 3 3 NA 1.63 1828 MULTIPOLYGON (((-80.45634 3...
# this works because st_rbindlist() internally renames all active geometry columns identically!
# note: as shown above the name of the returned geometry column ...
# ... is inherited by default from the first listed sf-object. The user ...
# ... can set an alternative geometry column name:
st_rbindlist(l, use.name = TRUE, fill = TRUE, idcol = TRUE,
geometry_name = "geom_named_by_user")
#> Simple feature collection with 3 features and 4 fields
#> Geometry type: MULTIPOLYGON
#> Dimension: XY
#> Bounding box: xmin: -81.74107 ymin: 36.23388 xmax: -80.43531 ymax: 36.58965
#> Geodetic CRS: NAD27
#> .id AREA PERIMETER CNTY_ geom_named_by_user
#> 1 1 NA NA NA MULTIPOLYGON (((-81.47276 3...
#> 2 2 0.061 NA NA MULTIPOLYGON (((-81.23989 3...
#> 3 3 NA 1.63 1828 MULTIPOLYGON (((-80.45634 3...