提问人:Timm S. 提问时间:9/24/2014 最后编辑:Gregor ThomasTimm S. 更新时间:3/17/2023 访问量:213426
为“dplyr”中的新列/变量使用动态名称
Use dynamic name for new column/variable in `dplyr`
问:
我想用于在数据框中创建多个新列。列名及其内容应动态生成。dplyr::mutate()
鸢尾花数据示例:
library(dplyr)
iris <- as_tibble(iris)
我创建了一个函数来改变变量中的新列:Petal.Width
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)而不是四个变量(称为 petal.2 - petal.5)。
如何将我的动态名称用作变量名称?mutate()
答:
由于您正在动态构建变量名称作为字符值,因此使用标准 data.frame 索引进行赋值更有意义,该索引允许列名的字符值。例如:
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
df[[varname]] <- with(df, Petal.Width * n)
df
}
该函数使通过命名参数命名新列变得非常容易。但这假设您在键入命令时知道名称。如果要动态指定列名,则还需要生成命名参数。mutate
DPLYR 版本 >= 1.0
使用最新的 dplyr 版本,您可以在命名参数时使用软件包中的语法。因此,在这里,名称中的通过计算内部表达式来获取值。glue
:=
{}
multipetal <- function(df, n) {
mutate(df, "petal.{n}" := Petal.Width * n)
}
如果要将列名传递给函数,则可以在字符串中使用,也可以用于列名{{}}
meanofcol <- function(df, col) {
mutate(df, "Mean of {{col}}" := mean({{col}}))
}
meanofcol(iris, Petal.Width)
DPLYR 版本 >= 0.7
dplyr
从版本 0.7 开始,您可以使用动态分配参数名称。您可以将函数编写为::=
# --- dplyr version 0.7+---
multipetal <- function(df, n) {
varname <- paste("petal", n , sep=".")
mutate(df, !!varname := Petal.Width * n)
}
有关更多信息,请参阅文档可用表单。vignette("programming", "dplyr")
dplyr (>=0.3 & <0.7)
稍早的版本(>=0.3<0.7)鼓励使用“标准评估”替代许多功能。有关详细信息,请参阅非标准评估小插图 ()。dplyr
vignette("nse")
所以在这里,答案是 use 而不是 and do: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))
}
dplyr < 0.3
请注意,这在最初提出问题时存在的旧版本中也是可能的。它需要谨慎使用和: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)
}
评论
do.call
do.call("mutate")
df
lazyeval
dplyr
mutate_(df, .dots= setNames(list(~Petal.Width * n), varname))
mutate(df, !!newVar := (!!var1 + !!var2) / 2)
{{
!!
:=
我还添加了一个答案,因为它在搜索答案时来到了这个条目,这几乎有我需要的东西,但我需要更多,这是我通过 @MrFlik 的答案和 R 懒惰的小插曲得到的。
我想创建一个函数,该函数可以接受数据帧和列名向量(作为字符串),我想将其从字符串转换为 Date 对象。我不知道如何获取字符串参数并将其转换为列,所以我如下所示。as.Date()
以下是我如何通过 SE mutate () 和参数做到这一点。欢迎提出改进的批评。mutate_()
.dots
library(dplyr)
dat <- data.frame(a="leave alone",
dt="2015-08-03 00:00:00",
dt2="2015-01-20 00:00:00")
# This function takes a dataframe and list of column names
# that have strings that need to be
# converted to dates in the data frame
convertSelectDates <- function(df, dtnames=character(0)) {
for (col in dtnames) {
varval <- sprintf("as.Date(%s)", col)
df <- df %>% mutate_(.dots= setNames(list(varval), col))
}
return(df)
}
dat <- convertSelectDates(dat, c("dt", "dt2"))
dat %>% str
这是另一个版本,可以说更简单一些。
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
虽然我喜欢使用 dplyr 进行交互使用,但我发现使用 dplyr 来做到这一点非常棘手,因为您必须通过重重障碍才能使用 lazyeval::interp()、setNames 等解决方法。
这是一个使用基础 R 的更简单版本,至少在我看来,将循环放在函数中似乎更直观,并且扩展了 @MrFlicks 的解决方案。
multipetal <- function(df, n) {
for (i in 1:n){
varname <- paste("petal", i , sep=".")
df[[varname]] <- with(df, Petal.Width * i)
}
df
}
multipetal(iris, 3)
评论
dplyr
在 ( 等待 2017 年 4 月) 的新版本中,我们还可以执行赋值 () 并通过取消引号 () 将变量作为列名传递,以不计算它dplyr
0.6.0
:=
!!
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检查输出multipetal
identical(iris1, iris2)
#[1] TRUE
评论
:=
!!varname
经过大量的试验和错误,我发现该模式(在早期的 R 版本中:)对于处理字符串和 dplyr 动词非常有用。它似乎在很多令人惊讶的情况下都有效。!!rlang::sym("my variable"))
UQ(rlang::sym("my variable")))
下面是 的示例。我们想创建一个将两列相加的函数,您可以在其中将两个列名作为字符串传递该函数。我们可以将此模式与 赋值运算符 一起使用来执行此操作。mutate
:=
## Take column `name1`, add it to column `name2`, and call the result `new_name`
mutate_values <- function(new_name, name1, name2){
mtcars %>%
mutate(!!rlang::sym(new_name) := !!rlang::sym(name1) + !!rlang::sym(name2))
}
mutate_values('test', 'mpg', 'cyl')
该模式也适用于其他函数。这里是:dplyr
filter
## filter a column by a value
filter_values <- function(name, value){
mtcars %>%
filter(!!rlang::sym(name) != value)
}
filter_values('gear', 4)
或:arrange
## transform a variable and then sort by it
arrange_values <- function(name, transform){
mtcars %>%
arrange((!!rlang::sym(name)) %>% (!!rlang::sym(transform)))
}
arrange_values('mpg', 'sin')
对于 ,您不需要使用该模式。相反,您可以使用:select
!!
## select a column
select_name <- function(name){
mtcars %>%
select(!!name)
}
select_name('mpg')
评论
myCol
myColInitialValue
df
which(colnames(df)=='myCol')
myColInitialValue
escape
DT::datatable()
escape=FALSE
来转义
DT::d atatable
varname = sym("Petal.Width"); ggplot(iris, aes(x=!!varname)) + geom_histogram()
!!rlang::sym(name)
UQ(rlang::sym(name))
您可以享受软件包友好的 Friendlyeval
,它为新用户/临时用户提供了简化整洁的 eval 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)
}
在引擎盖下调用检查作为列名是合法的函数。rlang
varname
friendlyeval
可以使用 RStudio 加载项随时将代码转换为等效的纯整洁的评估代码。
评论
在 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
我们还可以将带引号/不带引号的变量名称作为列名进行分配。
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
它的工作原理相同
multipetal(iris1, "temp", 3)
评论
mutate(df, 'petal.{n}' := Petal.Width * n)
另一种选择:使用内引号轻松创建动态名称。这与其他解决方案类似,但并不完全相同,我发现它更容易。{}
library(dplyr)
library(tibble)
iris <- as_tibble(iris)
multipetal <- function(df, n) {
df <- mutate(df, "petal.{n}" := Petal.Width * n) ## problem arises here
df
}
for(i in 2:5) {
iris <- multipetal(df=iris, n=i)
}
iris
我认为这来自但不确定(如果重要的话,我也有)。dplyr 1.0.0
rlang 4.7.0
如果您多次需要相同的操作,它通常会告诉您您的数据格式不是最佳的。您需要一个更长的格式,作为 data.frame 中的一列,这可以通过交叉联接来实现:n
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>
评论
mutate_
quosure
as.symbol
substitute