我想使用dplyr dplyr::mutate()在数据框中创建多个新列.应动态生成列名称及其内容.
来自虹膜的示例数据:
library(dplyr)
iris <- tbl_df(iris)
Run Code Online (Sandbox Code Playgroud)
我已经创建了一个函数来改变Petal.Width变量中的新列:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df <- mutate(df, varname = Petal.Width * n) ## problem arises here
df
}
Run Code Online (Sandbox Code Playgroud)
现在我创建一个循环来构建我的列:
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
Run Code Online (Sandbox Code Playgroud)
但是,由于mutate认为varname是一个文字变量名,因此循环只创建一个新变量(称为varname)而不是四个(称为petal.2 - petal.5).
如何将mutate()动态名称用作变量名?
MrF*_*ick 154
由于您正在将变量名称显着地构建为字符值,因此使用标准data.frame索引进行赋值更有意义,该索引允许列名称的字符值.例如:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df[[varname]] <- with(df, Petal.Width * n)
df
}
Run Code Online (Sandbox Code Playgroud)
该mutate函数使通过命名参数命名新列非常容易.但是,假设您在键入命令时知道名称.如果要动态指定列名,则还需要构建命名参数.
最新版本的dplyr(0.7)通过使用:=动态分配参数名称来实现此目的.您可以将您的功能编写为:
# --- dplyr version 0.7+---
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
mutate(df, !!varname := Petal.Width * n)
}
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅可用文档表单vignette("programming", "dplyr").
稍早版本的dplyr(> = 0.3 <0.7),鼓励使用"标准评估"替代许多功能.有关详细信息,请参阅非标准评估小插图(vignette("nse")).
所以在这里,答案是使用mutate_()而不是mutate():
# --- dplyr version 0.3-0.5---
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
varval <- lazyeval::interp(~Petal.Width * n, n=n)
mutate_(df, .dots= setNames(list(varval), varname))
}
Run Code Online (Sandbox Code Playgroud)
旧版本的dplyr
请注意,在最初提出问题时存在的旧版dplyr中也可以这样做.它需要小心使用quote和setName:
# --- dplyr versions < 0.3 ---
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
pp <- c(quote(df), setNames(list(quote(Petal.Width * n)), varname))
do.call("mutate", pp)
}
Run Code Online (Sandbox Code Playgroud)
akr*_*run 47
在dplyr(0.6.02017年4月等待)的新版本中,我们还可以执行赋值(:=)并通过unquoting(!!)将变量作为列名传递给不评估它
library(dplyr)
multipetalN <- function(df, n){
varname <- paste0("petal.", n)
df %>%
mutate(!!varname := Petal.Width * n)
}
data(iris)
iris1 <- tbl_df(iris)
iris2 <- tbl_df(iris)
for(i in 2:5) {
iris2 <- multipetalN(df=iris2, n=i)
}
Run Code Online (Sandbox Code Playgroud)
检查输出基于@ MrFlick multipetal应用于'iris1'
identical(iris1, iris2)
#[1] TRUE
Run Code Online (Sandbox Code Playgroud)
Tom*_*oth 20
经过大量的反复试验,我发现这个模式UQ(rlang::sym("some string here")))对于处理字符串和dplyr动词非常有用.它似乎在许多令人惊讶的情况下工作.
这是一个例子mutate.我们想要创建一个将两列相加的函数,您可以将函数作为字符串传递给列.我们可以使用此模式与赋值运算符:=一起执行此操作.
## Take column `name1`, add it to column `name2`, and call the result `new_name`
mutate_values <- function(new_name, name1, name2){
mtcars %>%
mutate(UQ(rlang::sym(new_name)) := UQ(rlang::sym(name1)) + UQ(rlang::sym(name2)))
}
mutate_values('test', 'mpg', 'cyl')
Run Code Online (Sandbox Code Playgroud)
该模式也适用于其他dplyr功能.这是filter:
## filter a column by a value
filter_values <- function(name, value){
mtcars %>%
filter(UQ(rlang::sym(name)) != value)
}
filter_values('gear', 4)
Run Code Online (Sandbox Code Playgroud)
或者arrange:
## transform a variable and then sort by it
arrange_values <- function(name, transform){
mtcars %>%
arrange(UQ(rlang::sym(name)) %>% UQ(rlang::sym(transform)))
}
arrange_values('mpg', 'sin')
Run Code Online (Sandbox Code Playgroud)
因为select,您不需要使用该模式.相反,你可以使用!!:
## select a column
select_name <- function(name){
mtcars %>%
select(!!name)
}
select_name('mpg')
Run Code Online (Sandbox Code Playgroud)
Ron*_*hah 15
有了rlang 0.4.0卷曲运算符 ( {{}}),这很容易。当动态列名称出现在赋值的左侧时,请使用:=.
library(dplyr)
library(rlang)
iris1 <- tbl_df(iris)
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
mutate(df, {{varname}} := Petal.Width * n)
}
multipetal(iris1, 4)
# A tibble: 150 x 6
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.4
# <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
# 1 5.1 3.5 1.4 0.2 setosa 0.8
# 2 4.9 3 1.4 0.2 setosa 0.8
# 3 4.7 3.2 1.3 0.2 setosa 0.8
# 4 4.6 3.1 1.5 0.2 setosa 0.8
# 5 5 3.6 1.4 0.2 setosa 0.8
# 6 5.4 3.9 1.7 0.4 setosa 1.6
# 7 4.6 3.4 1.4 0.3 setosa 1.2
# 8 5 3.4 1.5 0.2 setosa 0.8
# 9 4.4 2.9 1.4 0.2 setosa 0.8
#10 4.9 3.1 1.5 0.1 setosa 0.4
# … with 140 more rows
Run Code Online (Sandbox Code Playgroud)
我们还可以传递带引号/不带引号的变量名称以作为列名称分配。
multipetal <- function(df, name, n) {
mutate(df, {{name}} := Petal.Width * n)
}
multipetal(iris1, temp, 3)
# A tibble: 150 x 6
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species temp
# <dbl> <dbl> <dbl> <dbl> <fct> <dbl>
# 1 5.1 3.5 1.4 0.2 setosa 0.6
# 2 4.9 3 1.4 0.2 setosa 0.6
# 3 4.7 3.2 1.3 0.2 setosa 0.6
# 4 4.6 3.1 1.5 0.2 setosa 0.6
# 5 5 3.6 1.4 0.2 setosa 0.6
# 6 5.4 3.9 1.7 0.4 setosa 1.2
# 7 4.6 3.4 1.4 0.3 setosa 0.900
# 8 5 3.4 1.5 0.2 setosa 0.6
# 9 4.4 2.9 1.4 0.2 setosa 0.6
#10 4.9 3.1 1.5 0.1 setosa 0.3
# … with 140 more rows
Run Code Online (Sandbox Code Playgroud)
它与
multipetal(iris1, "temp", 3)
Run Code Online (Sandbox Code Playgroud)
use*_*432 12
这是另一个版本,它可以说有点简单.
multipetal <- function(df, n) {
varname <- paste("petal", n, sep=".")
df<-mutate_(df, .dots=setNames(paste0("Petal.Width*",n), varname))
df
}
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.2 petal.3 petal.4 petal.5
1 5.1 3.5 1.4 0.2 setosa 0.4 0.6 0.8 1
2 4.9 3.0 1.4 0.2 setosa 0.4 0.6 0.8 1
3 4.7 3.2 1.3 0.2 setosa 0.4 0.6 0.8 1
4 4.6 3.1 1.5 0.2 setosa 0.4 0.6 0.8 1
5 5.0 3.6 1.4 0.2 setosa 0.4 0.6 0.8 1
6 5.4 3.9 1.7 0.4 setosa 0.8 1.2 1.6 2
Run Code Online (Sandbox Code Playgroud)
您可能会喜欢这个软件包friendlyeval,它为新用户/临时用户提供了简化、整洁的评估 API 和文档dplyr。
您正在创建希望mutate将其视为列名称的字符串。所以使用friendlyeval你可以写:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df <- mutate(df, !!treat_string_as_col(varname) := Petal.Width * n)
df
}
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
Run Code Online (Sandbox Code Playgroud)
在幕后调用rlang函数来检查varname列名是否合法。
friendlyeval可以使用 RStudio 插件随时将代码转换为等效的简单整洁的评估代码。