假设我的数据框有A,B,C,D,E列.
我想生成一个包含A,B,C,X列的数据框,其中X = D*E.
显然我可以使用%>% mutate(X = D * E) %>% select (-D, -E),但对于更精细的情况,有没有办法在一个命令中完成它?喜欢transmute(),但只丢弃提到的列.
傻,但我一直希望这一点简洁.
如果您要组合这两个操作,可以使用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)
我们需要指定感兴趣的列,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)