我正在玩一些我在数据框中收集的数据,我希望将函数应用于列的所有元素.通常我会用purrr::map()它.但是,如果函数为列的其中一个元素返回错误,有时这将无效:
f <- function(x) {
if(x==2) stop("I hate 2") else x
}
library(dplyr)
dd <- data.frame(x = c(1:2))
dd2 <- dd %>%
mutate(fx = purrr::map(.x = x, .f = ~f(.)))
Error: I hate 2
Run Code Online (Sandbox Code Playgroud)
所以,我可以换我的功能f有try(),并获得结果的列:
> dd2 <- dd %>%
+ mutate(fx = purrr::map(.x = x, .f = ~try(f(.))))
Error in f(.) : I hate 2
> dd2
x fx
1 1 1
2 2 Error in f(.) : I hate 2\n
Run Code Online (Sandbox Code Playgroud)
现在我理想地想用过filter()滤掉带有错误的行,但我似乎无法做到这一点.这些都不会产生只有第一行的数据框:
dd2 %>% filter(is.integer(fx) )
dd2 %>% filter(is.integer(.$fx) )
dd2 %>% filter(class(fx) != "try-error")
dd2 %>% filter(class(.$fx) != "try-error")
lapply(dd2, is.numeric)
Run Code Online (Sandbox Code Playgroud)
我正在考虑的一个肮脏的技巧是使用try_catch(),并使其返回f()与错误情况相同类型的对象,例如-99999在这里,并过滤掉那些,但我正在寻找一个更清洁的解决方案.
因为您已经在使用purrr,所以您可能会尝试使用包装函数safely.该功能包的功能,并使其返回两个元素的列表result和error.其中一个总是如此NULL.
这是数据设置,类似于原始帖子.
library(dplyr)
df <- data.frame(x = c(1:2, 1))
f <- function(x) {
if (x == 2) stop("I hate 2") else x
}
Run Code Online (Sandbox Code Playgroud)
我们用函数包装safely并调用它.
f_safe <- purrr::safely(f)
df2 <- df %>% mutate(fxx = x %>% purrr::map(.f = f_safe))
df2
#> x fxx
#> 1 1 1
#> 2 2 I hate 2, .f(...)
#> 3 1 1
Run Code Online (Sandbox Code Playgroud)
我们可以确认这fxx是一个列表列,result其中error包含每个列表中的元素.
str(df2$fxx)
#> List of 3
#> $ :List of 2
#> ..$ result: num 1
#> ..$ error : NULL
#> $ :List of 2
#> ..$ result: NULL
#> ..$ error :List of 2
#> .. ..$ message: chr "I hate 2"
#> .. ..$ call : language .f(...)
#> .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
#> $ :List of 2
#> ..$ result: num 1
#> ..$ error : NULL
Run Code Online (Sandbox Code Playgroud)
现在,我们只询问list-column中的每个元素是否error为null.
df2 <- df2 %>%
mutate(no_error = fxx %>% purrr::map_lgl(.f = ~ is.null(.x$error)))
df2
#> x fxx no_error
#> 1 1 1 TRUE
#> 2 2 I hate 2, .f(...) FALSE
#> 3 1 1 TRUE
Run Code Online (Sandbox Code Playgroud)
我使用map_lgl的结果不是列表列,而是filter布尔值的可用向量.
df2 %>% filter(no_error)
#> x fxx no_error
#> 1 1 1 TRUE
#> 2 1 1 TRUE
Run Code Online (Sandbox Code Playgroud)
如果我们想像fxx常规向量一样使用列,我们必须mutate(fxx = fxx %>% purrr::map_dbl("result"))首先将它从列表列转换为简单向量.
编辑:另一个解决方案是使用dplyr::failwith和使用Sentinel值NA或像error错误一样包装,然后过滤与sentinel值匹配的元素.