2015-02-22 32 views
6

Tôi cố gắng để sử dụng dplyr::mutate_each với một số chức năng bên ngoài mà không cần gắn các thư viện thực tếLàm thế nào để sử dụng chức năng không gian tên với dplyr :: mutate_each?

dplyr::tbl_df(iris) %>% 
    dplyr::mutate_each(dplyr::funs(stringi::stri_trim_both)) 

nhưng nó không thành công với lỗi sau:

Error: unsupported type for column 'Sepal.Length' (CLOSXP, classes = function)

Khi tôi sử dụng data.table thay vì 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.

Nếu tôi sử dụng biến cục bộ như dưới đây, mọi thứ hoạt động như mong đợi.

trim_both <- stringi::stri_trim_both 
dplyr::tbl_df(iris) %>% dplyr::mutate_each(dplyr::funs(trim_both)) 

Nó không phải là một giải pháp tối ưu nhưng tôi có thể sống với điều đó. Tuy nhiên tôi sẽ biết ơn vì một lời giải thích nguồn gốc của vấn đề là gì.

thông tin 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   

Note: Vấn đề này không còn xảy ra trong dplyr 0.7.2.

Trả lời

7

Lý do cơ bản là dplyr::funs_ gọi dplyr:::make_call. Và dplyr:::make_call phân biệt giữa các trường hợp bằng cách sử dụng class của đối tượng được tạo bởi lazyeval::lazy_dots.

class(lazyeval::lazy_dots(trim_both)[[1]]$expr) 
## "name" 
class(lazyeval::lazy_dots(stringi::stri_trim_both)[[1]]$expr) 
## "call" 

Xem hàm my_funs bên dưới để biết giải pháp cho điều này. Tôi đã không kiểm tra điều này trong bất kỳ chi tiết nào và tôi chắc chắn rằng có một lý do khác biệt trong dplyr, do đó, không sử dụng điều này làm mặc định. Điều này chủ yếu nhằm làm rõ vấn đề

# 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)) 
Các vấn đề liên quan