我想使用dplyr::mutate()在数据帧中创建多个新列。列名及其内容应该是动态生成的。
虹膜数据示例:
library(dplyr)
iris <- as_tibble(iris)
我创建了一个函数来改变花瓣中的新列。宽度变量:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df <- mutate(df, varname = Petal.Width * n) ## problem arises here
df
}
现在我创建了一个循环来构建我的列:
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
然而,由于mutate认为varname是一个字面变量名,因此循环只创建了一个新变量(称为varname),而不是四个(称为花瓣)。2 -花瓣。5)。
我怎么能得到mutate()使用我的动态名称作为变量名?
在dplyr的新版本(0.6.0将于2017年4月发布)中,我们还可以执行赋值操作(:=),并通过取消引号(!!)将变量作为列名传递,从而不计算它
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)
}
根据应用在'iris1'上的@MrFlick的多瓣检查输出
identical(iris1, iris2)
#[1] TRUE
如果您需要多次执行相同的操作,它通常会告诉您,您的数据格式不是最佳的。你想要一个更长的格式,n是data.frame中的一列,可以通过交叉连接来实现:
library(tidyverse)
iris %>% mutate(identifier = 1:n()) %>% #necessary to disambiguate row 102 from row 143 (complete duplicates)
full_join(tibble(n = 1:5), by=character()) %>% #cross join for long format
mutate(petal = Petal.Width * n) %>% #calculation in long format
pivot_wider(names_from=n, values_from=petal, names_prefix="petal.width.") #back to wider format (if desired)
结果:
# A tibble: 150 x 11
Sepal.Length Sepal.Width Petal.Length Petal.Width Species identifier petal.width.1 petal.width.2 petal.width.3
<dbl> <dbl> <dbl> <dbl> <fct> <int> <dbl> <dbl> <dbl>
1 5.1 3.5 1.4 0.2 setosa 1 0.2 0.4 0.6
2 4.9 3 1.4 0.2 setosa 2 0.2 0.4 0.6
3 4.7 3.2 1.3 0.2 setosa 3 0.2 0.4 0.6
4 4.6 3.1 1.5 0.2 setosa 4 0.2 0.4 0.6
5 5 3.6 1.4 0.2 setosa 5 0.2 0.4 0.6
6 5.4 3.9 1.7 0.4 setosa 6 0.4 0.8 1.2
7 4.6 3.4 1.4 0.3 setosa 7 0.3 0.6 0.9
8 5 3.4 1.5 0.2 setosa 8 0.2 0.4 0.6
9 4.4 2.9 1.4 0.2 setosa 9 0.2 0.4 0.6
10 4.9 3.1 1.5 0.1 setosa 10 0.1 0.2 0.3
# ... with 140 more rows, and 2 more variables: petal.width.4 <dbl>, petal.width.5 <dbl>
你可能会喜欢package friendlyeval,它为新用户提供了一个简化的整洁的eval API和文档。
您正在创建希望更改为列名的字符串。所以使用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)
}
它在底层调用rlang函数,检查varname作为列名是否合法。
friendlyeval代码可以在任何时候通过RStudio插件转换为同等的简单整洁的eval代码。
经过大量的尝试和错误之后,我发现模式UQ(rlang::sym(“这里有一些字符串”))对于处理字符串和dplyr动词非常有用。它似乎在很多令人惊讶的情况下都起作用。
这是一个关于变异的例子。我们想要创建一个将两个列相加的函数,将两个列名作为字符串传递给函数。我们可以使用此模式和赋值操作符:=来完成此操作。
## 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')
该模式也适用于其他dplyr函数。过滤器:
## filter a column by a value
filter_values <- function(name, value){
mtcars %>%
filter(UQ(rlang::sym(name)) != value)
}
filter_values('gear', 4)
或安排:
## 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')
对于select,您不需要使用模式。相反,你可以使用!!:
## select a column
select_name <- function(name){
mtcars %>%
select(!!name)
}
select_name('mpg')
在dplyr的新版本(0.6.0将于2017年4月发布)中,我们还可以执行赋值操作(:=),并通过取消引号(!!)将变量作为列名传递,从而不计算它
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)
}
根据应用在'iris1'上的@MrFlick的多瓣检查输出
identical(iris1, iris2)
#[1] TRUE
这是另一个版本,可以说更简单一点。
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