Home > other >  Create a value associated with a member of each list and store it with that member in R
Create a value associated with a member of each list and store it with that member in R

Time:12-09

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.

  • Related