I have a list of many dataframes, all of the same format. For each member of this list, I would like to generate a spatial extent, and store it with that dataframe (this data is all lat/long data, and I am using functions from the terra package to analyze it). I am not super experienced with working with lists, and so I took the following stab at trying to generate it:
library(terra)
library(dplyr)
lat_1 <- c(23.2, 14.5, 28.6)
lon_1 <- c(12.1, 8.5, 2.2)
lat_2 <- c(89.3, 94.4, 72.3)
lon_2 <- c(45.2, 47, 48.5)
coords_1 <- data.frame(lon_1, lat_1)
coords_2 <- data.frame(lon_2, lat_2)
list_coords <- list(coords_1, coords_2)
write_extent <- function(lon, lat) {
max_lat <- ceiling(max(lat))
min_lat <- floor(min(lat))
max_lon <- ceiling(max(lon))
min_lon <- floor(min(lon))
extent <- extent(x = c(max_lat, min_lat, max_lon, min_lon))
}
However, this function has errors, and I can't conceptualize how I can store the spatial extent that corresponds with each member of the list with that specific list- should I be using mutate()? Should I not be designing a function and rather be using lapply?
CodePudding user response:
You can do this a couple of different ways. First, you'll want to make the data frames have the same column names for longitude and latitude lon
and lat
, but that's arbitrary. Once you've done that, then one way is to produce a new list where each element of the list has both a data frame and an extent object:
library(terra)
library(raster)
library(dplyr)
lat_1 <- c(23.2, 14.5, 28.6)
lon_1 <- c(12.1, 8.5, 2.2)
lat_2 <- c(89.3, 94.4, 72.3)
lon_2 <- c(45.2, 47, 48.5)
coords_1 <- data.frame(lon = lon_1, lat = lat_1)
coords_2 <- data.frame(lon = lon_2, lat = lat_2)
list_coords <- list(coords_1, coords_2)
write_extent <- function(lon, lat) {
max_lat <- ceiling(max(lat))
min_lat <- floor(min(lat))
max_lon <- ceiling(max(lon))
min_lon <- floor(min(lon))
extent <- extent(x = min_lat, xmax=max_lat, ymin = min_lon, ymax=max_lon)
extent
}
res <- lapply(list_coords, function(x){
list(data=x, extent = write_extent(x$lon, x$lat))
})
res
#> [[1]]
#> [[1]]$data
#> lon lat
#> 1 12.1 23.2
#> 2 8.5 14.5
#> 3 2.2 28.6
#>
#> [[1]]$extent
#> class : Extent
#> xmin : 14
#> xmax : 29
#> ymin : 2
#> ymax : 13
#>
#>
#> [[2]]
#> [[2]]$data
#> lon lat
#> 1 45.2 89.3
#> 2 47.0 94.4
#> 3 48.5 72.3
#>
#> [[2]]$extent
#> class : Extent
#> xmin : 72
#> xmax : 95
#> ymin : 45
#> ymax : 49
In the output above, you could get the data for the first object with res[[1]]$data
and the extent for the first object with res[[1]]$extent
. Or you could get a list of all the extents with lapply(res, function(x)x$extent)
. Another option would be to store the extent as an attribute of the data. This way, it always follows the data around:
res <- lapply(list_coords, function(x){
e <- write_extent(x$lon, x$lat)
attr(x, "extent") <- e
x
})
res
#> [[1]]
#> lon lat
#> 1 12.1 23.2
#> 2 8.5 14.5
#> 3 2.2 28.6
#>
#> [[2]]
#> lon lat
#> 1 45.2 89.3
#> 2 47.0 94.4
#> 3 48.5 72.3
You don't see the extent when you print the data frame, but you can retrieve it either for a single data frame with:
attr(res[[1]], "extent")
#> class : Extent
#> xmin : 14
#> xmax : 29
#> ymin : 2
#> ymax : 13
Or for all of them with:
lapply(res, function(x)attr(x, "extent"))
#> [[1]]
#> class : Extent
#> xmin : 14
#> xmax : 29
#> ymin : 2
#> ymax : 13
#>
#> [[2]]
#> class : Extent
#> xmin : 72
#> xmax : 95
#> ymin : 45
#> ymax : 49
Created on 2022-12-08 by the reprex package (v2.0.1)
While I think it's a bit less conventional to set object attributes, this answer suggests it is not bad practice to do so.