从列/列表中过滤尝试错误对象(dplyr 但也更通用)
Filter try-error objects from a column / list (dplyr but also more general)
我正在处理我在数据框中收集的一些数据,我想在其中对列的所有元素应用一个函数。通常我为此使用 purrr::map()
。但是,如果函数 return 对列的一个元素出错,有时这将不起作用:
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
所以我可以用 try()
包装我的函数 f
,并获得一列结果:
> 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
现在我最想使用 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)
我正在考虑的一个肮脏的技巧是使用 try_catch()
代替,并使它 return 成为与 f()
相同类型的对象以防出错,因为例如 -99999
这里,并过滤掉那些,但我正在寻找一个更干净的解决方案。
因为您已经在使用 purrr,您可以尝试用 safely
包装该函数。这个函数包装了一个函数并使它成为 return 两个元素 result
和 error
的列表。其中之一总是 NULL
.
这里是数据设置,类似于原来的post。
library(dplyr)
df <- data.frame(x = c(1:2, 1))
f <- function(x) {
if (x == 2) stop("I hate 2") else x
}
我们用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
我们可以确认 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
现在,我们只需要询问列表列中的每个元素其 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
我使用了 map_lgl
,因此结果不是一个列表列,而是一个 filter
-able 布尔向量。
df2 %>% filter(no_error)
#> x fxx no_error
#> 1 1 1 TRUE
#> 2 1 1 TRUE
如果我们想像使用常规向量一样使用 fxx
列,我们必须先 mutate(fxx = fxx %>% purrr::map_dbl("result"))
将其从列表列转换为简单向量。
编辑:另一个解决方案是用 dplyr::failwith
包装并使用 NA
或 error
之类的标记值来处理错误,然后过滤与标记值匹配的元素。
我正在处理我在数据框中收集的一些数据,我想在其中对列的所有元素应用一个函数。通常我为此使用 purrr::map()
。但是,如果函数 return 对列的一个元素出错,有时这将不起作用:
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
所以我可以用 try()
包装我的函数 f
,并获得一列结果:
> 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
现在我最想使用 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)
我正在考虑的一个肮脏的技巧是使用 try_catch()
代替,并使它 return 成为与 f()
相同类型的对象以防出错,因为例如 -99999
这里,并过滤掉那些,但我正在寻找一个更干净的解决方案。
因为您已经在使用 purrr,您可以尝试用 safely
包装该函数。这个函数包装了一个函数并使它成为 return 两个元素 result
和 error
的列表。其中之一总是 NULL
.
这里是数据设置,类似于原来的post。
library(dplyr)
df <- data.frame(x = c(1:2, 1))
f <- function(x) {
if (x == 2) stop("I hate 2") else x
}
我们用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
我们可以确认 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
现在,我们只需要询问列表列中的每个元素其 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
我使用了 map_lgl
,因此结果不是一个列表列,而是一个 filter
-able 布尔向量。
df2 %>% filter(no_error)
#> x fxx no_error
#> 1 1 1 TRUE
#> 2 1 1 TRUE
如果我们想像使用常规向量一样使用 fxx
列,我们必须先 mutate(fxx = fxx %>% purrr::map_dbl("result"))
将其从列表列转换为简单向量。
编辑:另一个解决方案是用 dplyr::failwith
包装并使用 NA
或 error
之类的标记值来处理错误,然后过滤与标记值匹配的元素。