在 purrr::map 循环中使用 rlang::last_error() 的函数 dplyr

Functions dplyr with rlang::last_error() in purrr::map loop in r

我正在使用一个函数通过 ID 计算每个单元格的线串长度并存储在列表中,将列表的每个元素转换为 RasterLayer 并将该列表转换为 RasterStack,对所有层进行平均并得到单个光栅。

#function

#  build_length_raster <- function(one_df) {

  intersect_list <- by(
    one_df , 
    one_df$sub_id,
    function(subid_df) sf::st_intersection(grid2, subid_df) %>% 
      dplyr::mutate(length = as.numeric(sf::st_length(.))) %>%
      sf::st_drop_geometry()
  ) 
  
  list_length_grid <- purrr::map(intersect_list, function(x) 
    x %>% dplyr::left_join(x=grid2, by="cell", copy=T) %>% 
      dplyr::mutate(length=length) %>%
      dplyr::mutate_if(is.numeric,coalesce,0)
  )
   list_length_raster <- purrr::map(list_length_grid, function(x) 
    raster::rasterize(x, r, field="length", na.rm=F, background=0)
  )

  list_length_raster2 <- unlist(list_length_raster, recursive=F)
  
  raster_stack <- raster::stack(list_length_raster2)
  
  raster_mean <- raster::stackApply(
    raster_stack, 
    indices = rep(1,nlayers(raster_stack)), 
    fun = "mean", na.rm = TRUE)
#}

该函数提供了一个步骤,为了使 st_intersection() 的结果网格具有与最初相同的单元格数量,我使用 left_join(by="cell " 列)。然后我使用 mutate() 将 NA 替换为 0。当我 运行 列表中的一个数据框的函数步骤时,它工作得很好,但是当我把它放在 map() 中来执行此操作时在列表中,我得到这个错误,它似乎是指 dplyr 函数:

final_list <- purrr::map(mylist, build_length_raster)

> rlang::last_error()
<error/rlang_error>
Join columns must be present in data.
x Problem with `cell`.
Backtrace:
  1. purrr::map(mylist, build_length_raster)
 15. dplyr:::left_join.data.frame(., x = grid, by = "cell", copy = T)
 16. dplyr:::join_mutate(...)
 17. dplyr:::join_cols(...)
 18. dplyr:::standardise_join_by(by, x_names = x_names, y_names = y_names)
 19. dplyr:::check_join_vars(by$y, y_names)
Run `rlang::last_trace()` to see the full context.

有没有办法解决这个问题?

MYDATA 示例

library(tidyverse)
library(sf)
library(purrr)
library(raster)

#data example
id <- c("844", "844", "844", "844", "844","844", "844", "844", "844", "844",
        "844", "844", "845", "845", "845", "845", "845","845", "845", "845", 
        "845","845", "845", "845")
sub_id <- c("2017_844_1", "2017_844_1", "2017_844_1", "2017_844_1", "2017_844_2",
        "2017_844_2", "2017_844_2", "2017_844_2", "2017_844_3", "2017_844_3",
        "2017_844_3", "2017_844_3", "2017_845_1", "2017_845_1", "2017_845_1", 
        "2017_845_1", "2017_845_2","2017_845_2", "2017_845_2", "2017_845_2", 
        "2017_845_3","2017_845_3", "2017_845_3", "2017_845_3")
lat <- c(-30.6456, -29.5648, -27.6667, -31.5587, -30.6934, -29.3147, -23.0538, 
         -26.5877, -26.6923, -23.40865, -23.1143, -23.28331, -31.6456, -24.5648, 
         -27.6867, -31.4587, -30.6784, -28.3447, -23.0466, -27.5877, -26.8524, 
         -23.8855, -24.1143, -23.5874)
long <- c(-50.4879, -49.8715, -51.8716, -50.4456, -50.9842, -51.9787, -41.2343, 
          -40.2859, -40.19599, -41.64302, -41.58042, -41.55057, -50.4576, -48.8715, 
          -51.4566, -51.4456, -50.4477, -50.9937, -41.4789, -41.3859, -40.2536, 
          -41.6502, -40.5442, -41.4057)

df <- tibble(id = as.factor(id), sub_id  = as.factor(sub_id), lat, long)

#converting ​to sf
df.sf <- df %>% 
 ​sf::st_as_sf(coords = c("long", "lat"), crs = 4326)


#creating grid
xy <- sf::st_coordinates(df.sf)

grid = sf::st_make_grid(sf::st_bbox(df.sf),
                       ​cellsize = .1, square = FALSE) %>%
 ​sf::st_as_sf() 

#creating raster
r <- raster::raster(grid, res=0.1) 

#return grid because raster function changes number of cells 
grid2 <- rasterToPolygons(r, na.rm=F) %>%
  st_as_sf() %>% mutate(cell=1:ncell(r))

#creating linestring to each sub_id
df.line <- df.sf %>% 
  dplyr::group_by(sub_id, id) %>%
  dplyr::summarize() %>%
  sf::st_cast("LINESTRING") 

#creating ID list
mylist<- split(df.line, df.line$id)

#separating one dataframe of list to test function
one_df <- df.line[df.line$id=="844",]
one_df$id <- droplevels(one_df$id)
one_df$sub_id <- droplevels(one_df$sub_id)

具体错误是因为intersect_list列表中有空项,因为它们是空的,所以无法连接,因此没有可连接的列。如果您将地图函数修改为仅使用 intersect_list 的 non-empty 项,您将不会收到该错误。

正如您在评论中指出的那样,在将 left_join 映射到列表项之前删除带有 keep(intersect_list, ~ !is.null(.)) 的空列表条目将修复错误。

但是,我认为这不是解决这个问题的最优雅的方法。我可能误解了目标是什么,但如果它是根据每个网格单元内的线的总长度生成栅格,我认为不使用 purrr 的更简单的方法可能会起作用。

这与您的产品并不完全相同,但我将其简化为说明替代方法。这是作为 stars 对象的每个单元格中的长度总和(类似于 raster 但与 tidyverse 和 sf 一起使用效果更好)。

我从你的对象开始 one_dfgrid:

# Turn multiple lines into single MULTILINESTRING:
one_df %>% 
  st_union() -> 
  union_df

# Intersection of each grid cell with the MULTILINESTRING geometry:
grid %>% 
  st_intersection(union_df) -> 
  grid_lines 

# Get lengths:
grid_lines %>% 
  mutate(length = st_length(x)) %>% 
  st_drop_geometry() ->
  grid_lengths

# Join the calculated lengths back with the spatial grid, 
# most of which will have NA for length
grid %>%
  left_join(grid_lengths, by = "cell") ->
  grid_with_lengths

# Rasterize the length field of the grid
grid_with_lengths %>% 
  dplyr::select(length) %>% 
  stars::st_rasterize() ->
  length_stars
  
  length_stars %>% mapview::mapview()