dplyr mutate/transmute:仅删除公式中使用的列

Dav*_*man 5 r dplyr

假设我的数据框有A,B,C,D,E列.

我想生成一个包含A,B,C,X列的数据框,其中X = D*E.

显然我可以使用%>% mutate(X = D * E) %>% select (-D, -E),但对于更精细的情况,有没有办法在一个命令中完成它?喜欢transmute(),但只丢弃提到的列.

傻,但我一直希望这一点简洁.

Art*_*lov 9

如果您要组合这两个操作,可以使用NULLin mutate来指定应删除哪些列:

df %>% mutate( X=D*E, D=NULL, E=NULL )
Run Code Online (Sandbox Code Playgroud)

不幸的是,你仍然需要两次提到每个变量,所以也许它只是略微简洁一点.

更新:所以,我真的很喜欢这个问题,因为它本质上要求一个具有两者mutate和一些特征的mutator transmute.这样的mutator将需要解析所提供的表达式以识别计算正在使用哪些符号,然后从结果中删除那些符号.

要实现这样的mutator,我们需要一些工具.首先,让我们定义一个检索表达式的抽象语法树(AST)的函数.

library( tidyverse )

## Recursively constructs the abstract syntax tree (AST) of the provided expression
getAST <- function( ee ) { as.list(ee) %>% map_if(is.call, getAST) }
Run Code Online (Sandbox Code Playgroud)

这是一个实际的例子getAST:

z <- quote( a*log10(x)+b )   ## Captures the expression a*log10(x)+b
getAST( z ) %>% str
# List of 3
#  $ : symbol +
#  $ :List of 3
#   ..$ : symbol *
#   ..$ : symbol a
#   ..$ :List of 2
#   .. ..$ : symbol log10
#   .. ..$ : symbol x
#  $ : symbol b
Run Code Online (Sandbox Code Playgroud)

检索表达式使用的符号列表只需要展平和去除此树.

## Retrieves all symbols (as strings) used in a given expression
getSyms <- function( ee ) { getAST(ee) %>% unlist %>% map_chr(deparse) }
getSyms(z)
# [1] "+"     "*"     "a"     "log10" "x"     "b"
Run Code Online (Sandbox Code Playgroud)

我们现在准备实现我们的新mutator来计算新列(类似于mutate)并删除计算中使用的变量(类似于transmute):

## A new mutator that removes all variables used by the computations
transmutate <- function( .data, ... )
{
    ## Capture the provided expressions and retrieve their symbols
    vSyms <- enquos(...) %>% map( ~getSyms(get_expr(.x)) )

    ## Identify symbols that are in common with the provided dataset
    ## These columns are to be removed
    vToRemove <- intersect( colnames(.data), unlist(vSyms) )

    ## Pass on the expressions to mutate to do the work
    ## Remove the identified columns from the result
    mutate( .data, ... ) %>% select( -one_of(vToRemove) )
}
Run Code Online (Sandbox Code Playgroud)

让我们把新功能带出去:

## Expected output should include new columns X, Y
##    removed columns vs, drat, wt, mpg, and cyl
##    and everything else the same
## (Note that in the classical tidyverse spirit, rownames are not preserved)
transmutate( mtcars, X = ifelse( vs, drat, wt ), Y = mpg*cyl )
#     disp  hp  qsec am gear carb     X     Y
# 1  160.0 110 16.46  1    4    4 2.620 126.0
# 2  160.0 110 17.02  1    4    4 2.875 126.0
# 3  108.0  93 18.61  1    4    1 3.850  91.2
# 4  258.0 110 19.44  0    3    1 3.080 128.4
# ...
Run Code Online (Sandbox Code Playgroud)

  • @DavidKaufman:你可能有兴趣知道我提议将 `transmutate()` 作为 `dplyr` 的一个新特性。请随意参与其行为的讨论:https://github.com/tidyverse/dplyr/issues/3721 (2认同)

akr*_*run 7

我们需要指定感兴趣的列,transmute因为它只返回传入的列

df %>% 
    transmute(A, B, C, X = D*E)
Run Code Online (Sandbox Code Playgroud)

如果有很多列,那么一个不逐个输入的选项就是将其转换为符号,然后进行评估(!!!)

df %>% 
  transmute(!!! rlang::syms(names(.)[1:3]), X = D*E)
Run Code Online (Sandbox Code Playgroud)

或者,如果我们不知道感兴趣的列的索引,但只知道要删除的列的名称

df %>% 
    transmute(!!! rlang::syms(setdiff(names(.), c('D', 'E'))), X = D*E)
Run Code Online (Sandbox Code Playgroud)

数据

set.seed(24)
df <- as.data.frame(matrix(sample(1:9, 5*10, replace = TRUE), 
          ncol = 5, dimnames = list(NULL, LETTERS[1:5])))
Run Code Online (Sandbox Code Playgroud)