如何在 dplyr::mutate_each 中使用命名空间函数?
How to use namespaced function with dplyr::mutate_each?
我正在尝试将 dplyr::mutate_each
与一些外部函数一起使用,而不附加实际的库
dplyr::tbl_df(iris) %>%
dplyr::mutate_each(dplyr::funs(stringi::stri_trim_both))
但失败并出现以下错误:
Error: unsupported type for column 'Sepal.Length' (CLOSXP, classes = function)
当我使用 data.table
而不是 data.frame
时:
Error in `[.data.table`(`_dt`, , `:=`(Sepal.Length, stringi::stri_trim_both), :
RHS of assignment is not NULL, not an an atomic vector (see ?is.atomic) and not a list column.
如果我使用下面的局部变量,一切都会按预期工作。
trim_both <- stringi::stri_trim_both
dplyr::tbl_df(iris) %>% dplyr::mutate_each(dplyr::funs(trim_both))
这不是最佳解决方案,但我可以接受。不过,如果能解释问题的根源是什么,我将不胜感激。
Session 信息:
R version 3.1.1 (2014-07-10)
Platform: x86_64-pc-linux-gnu (64-bit)
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] dplyr_0.4.1
loaded via a namespace (and not attached):
[1] assertthat_0.1 DBI_0.3.1 lazyeval_0.1.10.9000
[4] magrittr_1.5 parallel_3.1.1 Rcpp_0.11.4
[7] stringi_0.4-1 tools_3.1.1
注意:这个问题在dplyr
0.7.2中不再出现。
根本原因是 dplyr::funs_
调用了 dplyr:::make_call
。并且 dplyr:::make_call
使用 lazyeval::lazy_dots
生成的对象的 class
区分大小写。
class(lazyeval::lazy_dots(trim_both)[[1]]$expr)
## "name"
class(lazyeval::lazy_dots(stringi::stri_trim_both)[[1]]$expr)
## "call"
请参阅下面的函数 my_funs
以了解此问题的解决方案。我没有对此进行任何详细测试,我确信这在 dplyr
中有所不同是有原因的,所以不要将其用作默认值。主要是为了澄清问题
# calling my_funs_ (instead of funs_)
my_funs <- function (...)
my_funs_(lazyeval::lazy_dots(...))
my_funs_ <- function(dots){
dots <- lazyeval::as.lazy_dots(dots)
env <- lazyeval::common_env(dots)
names(dots) <- dplyr:::names2(dots)
# difference here
dots[] <- lapply(dots, function(x) {
if (is.character(x$expr)) {
x$expr <- substitute(f(.), list(f = as.name(x$expr)))
}
else if (is.name(x$expr)) {
x$expr <- substitute(f(.), list(f = x$expr))
}
else if (is.call(x$expr)) {
x$expr <- substitute(f(.), list(f = x$expr)) #### this line was different
# originally x$expr <- x$expr
}
else {
stop("Unknown inputs")
}
x
})
missing_names <- names(dots) == ""
### this is also different
default_names <- vapply(dots[missing_names], function(x) as.character(x)[1],
character(1))
## originally dplyr:::make_name(x) instead of as.character(x)[1]
names(dots)[missing_names] <- default_names
class(dots) <- c("fun_list", "lazy_dots")
dots
}
dplyr::tbl_df(iris) %>%
dplyr::mutate_each(my_funs(stringi::stri_trim_both))
我正在尝试将 dplyr::mutate_each
与一些外部函数一起使用,而不附加实际的库
dplyr::tbl_df(iris) %>%
dplyr::mutate_each(dplyr::funs(stringi::stri_trim_both))
但失败并出现以下错误:
Error: unsupported type for column 'Sepal.Length' (CLOSXP, classes = function)
当我使用 data.table
而不是 data.frame
时:
Error in `[.data.table`(`_dt`, , `:=`(Sepal.Length, stringi::stri_trim_both), : RHS of assignment is not NULL, not an an atomic vector (see ?is.atomic) and not a list column.
如果我使用下面的局部变量,一切都会按预期工作。
trim_both <- stringi::stri_trim_both
dplyr::tbl_df(iris) %>% dplyr::mutate_each(dplyr::funs(trim_both))
这不是最佳解决方案,但我可以接受。不过,如果能解释问题的根源是什么,我将不胜感激。
Session 信息:
R version 3.1.1 (2014-07-10)
Platform: x86_64-pc-linux-gnu (64-bit)
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] dplyr_0.4.1
loaded via a namespace (and not attached):
[1] assertthat_0.1 DBI_0.3.1 lazyeval_0.1.10.9000
[4] magrittr_1.5 parallel_3.1.1 Rcpp_0.11.4
[7] stringi_0.4-1 tools_3.1.1
注意:这个问题在dplyr
0.7.2中不再出现。
根本原因是 dplyr::funs_
调用了 dplyr:::make_call
。并且 dplyr:::make_call
使用 lazyeval::lazy_dots
生成的对象的 class
区分大小写。
class(lazyeval::lazy_dots(trim_both)[[1]]$expr)
## "name"
class(lazyeval::lazy_dots(stringi::stri_trim_both)[[1]]$expr)
## "call"
请参阅下面的函数 my_funs
以了解此问题的解决方案。我没有对此进行任何详细测试,我确信这在 dplyr
中有所不同是有原因的,所以不要将其用作默认值。主要是为了澄清问题
# calling my_funs_ (instead of funs_)
my_funs <- function (...)
my_funs_(lazyeval::lazy_dots(...))
my_funs_ <- function(dots){
dots <- lazyeval::as.lazy_dots(dots)
env <- lazyeval::common_env(dots)
names(dots) <- dplyr:::names2(dots)
# difference here
dots[] <- lapply(dots, function(x) {
if (is.character(x$expr)) {
x$expr <- substitute(f(.), list(f = as.name(x$expr)))
}
else if (is.name(x$expr)) {
x$expr <- substitute(f(.), list(f = x$expr))
}
else if (is.call(x$expr)) {
x$expr <- substitute(f(.), list(f = x$expr)) #### this line was different
# originally x$expr <- x$expr
}
else {
stop("Unknown inputs")
}
x
})
missing_names <- names(dots) == ""
### this is also different
default_names <- vapply(dots[missing_names], function(x) as.character(x)[1],
character(1))
## originally dplyr:::make_name(x) instead of as.character(x)[1]
names(dots)[missing_names] <- default_names
class(dots) <- c("fun_list", "lazy_dots")
dots
}
dplyr::tbl_df(iris) %>%
dplyr::mutate_each(my_funs(stringi::stri_trim_both))