如何在列上使用 tidyeval 进行变异?
How to use tidyeval on a column to mutate?
对于造成的混乱,我深表歉意,但最终,我发布的第一个示例(在页面底部)并没有帮助我弄清楚 tidyeval 如何与 mutate 一起工作,所以我添加了一个新示例.
我想创建一个带有三个参数的函数:
- 一个数据框
- 要改变的列
- 一个变量(来自数据框)来替换正在变异的值
例如,要用 carb
中的值替换 mpg
中的值,我试过这个:
I tried this
colToX <- function(dt, ..., repl) {
cols <- quos(...)
repl <- quo(repl)
mutate_at(dt, vars(!!!cols), funs(function(x) !!repl))
}
colToX(mtcars, mpg, repl = carb)
这不起作用,因为:
Error in mutate_impl(.data, dots) : Column mpg
is of unsupported
type function
我的第一个例子(@MrFlick 和@UseR 都适用于此):
例如,下面的应该 mutate()
将所有 1 分配给传入的变量 ...
colTo1 <- function(dt, ...) {
col <- quo(...)
mutate(mtcars, !!col := 1)
}
colTo1(mtcars, mpg)
Error: LHS must be a name or string
最终,输出应该与mutate(mtcars, mpg = 1)
相同
如错误所述 "Error: LHS must be a name or string" 其中 LHS 表示 left-hand 侧,它具体指的是 !!col := 1
部分。你需要把你做的quosure变成一个字符串或者符号。使用 quo_name
获取 quosure 的字符串版本可能是最简单的
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(mtcars, !!col := 1)
}
@MrFlick 的解决方案适用于一列情况,但由于 OP 使用 ...
作为参数,我假设 OP 也希望该函数能够接收多列。例如,以下将不起作用:
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(dt, !!col := 1)
}
colTo1(mtcars, mpg, cyl)
Error in inherits(x, "quosure") : object 'cyl' not found
我们能做的就是用quos
代替quo
,用mutate_at
代替mutate
:
colTo1 <- function(dt, ...) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=1)
}
quos
将每个参数从 ...
转换为 quosures 向量。使用 mutate_at
的 vars
语法和从 rlang
显式拼接 !!!
,我们可以取消引用 cols
中的每个 quosure,并在那些指定的列上进行变异。
现在这按预期工作了:
colTo1(mtcars, mpg, cyl)
结果:
mpg cyl disp hp drat wt qsec vs am gear carb
1 1 1 160.0 110 3.90 2.620 16.46 0 1 4 4
2 1 1 160.0 110 3.90 2.875 17.02 0 1 4 4
3 1 1 108.0 93 3.85 2.320 18.61 1 1 4 1
4 1 1 258.0 110 3.08 3.215 19.44 1 0 3 1
5 1 1 360.0 175 3.15 3.440 17.02 0 0 3 2
6 1 1 225.0 105 2.76 3.460 20.22 1 0 3 1
7 1 1 360.0 245 3.21 3.570 15.84 0 0 3 4
8 1 1 146.7 62 3.69 3.190 20.00 1 0 4 2
9 1 1 140.8 95 3.92 3.150 22.90 1 0 4 2
10 1 1 167.6 123 3.92 3.440 18.30 1 0 4 4
...
让“1
”成为要传递给函数的另一个参数也很容易:
colToX <- function(dt, ..., X) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=X)
}
colToX(mtcars, mpg, cyl, X = 2)
编辑: OP 更改了问题以要求 X
应该是同一数据框中的另一列。以下是我的新解决方案:
colToX <- function(dt, ..., X) {
cols <- quos(...)
X_quo <- enquo(X)
mutate_at(dt, vars(!!!cols), funs(.data$`!!`(X_quo)))
}
colToX(mtcars, mpg, cyl, X = hp)
在这里,我使用 funs
函数来创建对从 vars
引用的每个列的函数调用列表。 .data
指的是 mutate_at
中的输入数据帧(在本例中为 dt
)。我使用 enquo
将 X
中调用的内容转换为 quosure 并使用 !!
取消引用。
结果:
mpg cyl disp hp drat wt qsec vs am gear carb
1 110 110 160.0 110 3.90 2.620 16.46 0 1 4 4
2 110 110 160.0 110 3.90 2.875 17.02 0 1 4 4
3 93 93 108.0 93 3.85 2.320 18.61 1 1 4 1
4 110 110 258.0 110 3.08 3.215 19.44 1 0 3 1
5 175 175 360.0 175 3.15 3.440 17.02 0 0 3 2
6 105 105 225.0 105 2.76 3.460 20.22 1 0 3 1
7 245 245 360.0 245 3.21 3.570 15.84 0 0 3 4
8 62 62 146.7 62 3.69 3.190 20.00 1 0 4 2
9 95 95 140.8 95 3.92 3.150 22.90 1 0 4 2
10 123 123 167.6 123 3.92 3.440 18.30 1 0 4 4
...
对于造成的混乱,我深表歉意,但最终,我发布的第一个示例(在页面底部)并没有帮助我弄清楚 tidyeval 如何与 mutate 一起工作,所以我添加了一个新示例.
我想创建一个带有三个参数的函数:
- 一个数据框
- 要改变的列
- 一个变量(来自数据框)来替换正在变异的值
例如,要用 carb
中的值替换 mpg
中的值,我试过这个:
I tried this
colToX <- function(dt, ..., repl) {
cols <- quos(...)
repl <- quo(repl)
mutate_at(dt, vars(!!!cols), funs(function(x) !!repl))
}
colToX(mtcars, mpg, repl = carb)
这不起作用,因为:
Error in mutate_impl(.data, dots) : Column
mpg
is of unsupported type function
我的第一个例子(@MrFlick 和@UseR 都适用于此):
例如,下面的应该 mutate()
将所有 1 分配给传入的变量 ...
colTo1 <- function(dt, ...) {
col <- quo(...)
mutate(mtcars, !!col := 1)
}
colTo1(mtcars, mpg)
Error: LHS must be a name or string
最终,输出应该与mutate(mtcars, mpg = 1)
如错误所述 "Error: LHS must be a name or string" 其中 LHS 表示 left-hand 侧,它具体指的是 !!col := 1
部分。你需要把你做的quosure变成一个字符串或者符号。使用 quo_name
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(mtcars, !!col := 1)
}
@MrFlick 的解决方案适用于一列情况,但由于 OP 使用 ...
作为参数,我假设 OP 也希望该函数能够接收多列。例如,以下将不起作用:
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(dt, !!col := 1)
}
colTo1(mtcars, mpg, cyl)
Error in inherits(x, "quosure") : object 'cyl' not found
我们能做的就是用quos
代替quo
,用mutate_at
代替mutate
:
colTo1 <- function(dt, ...) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=1)
}
quos
将每个参数从 ...
转换为 quosures 向量。使用 mutate_at
的 vars
语法和从 rlang
显式拼接 !!!
,我们可以取消引用 cols
中的每个 quosure,并在那些指定的列上进行变异。
现在这按预期工作了:
colTo1(mtcars, mpg, cyl)
结果:
mpg cyl disp hp drat wt qsec vs am gear carb
1 1 1 160.0 110 3.90 2.620 16.46 0 1 4 4
2 1 1 160.0 110 3.90 2.875 17.02 0 1 4 4
3 1 1 108.0 93 3.85 2.320 18.61 1 1 4 1
4 1 1 258.0 110 3.08 3.215 19.44 1 0 3 1
5 1 1 360.0 175 3.15 3.440 17.02 0 0 3 2
6 1 1 225.0 105 2.76 3.460 20.22 1 0 3 1
7 1 1 360.0 245 3.21 3.570 15.84 0 0 3 4
8 1 1 146.7 62 3.69 3.190 20.00 1 0 4 2
9 1 1 140.8 95 3.92 3.150 22.90 1 0 4 2
10 1 1 167.6 123 3.92 3.440 18.30 1 0 4 4
...
让“1
”成为要传递给函数的另一个参数也很容易:
colToX <- function(dt, ..., X) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=X)
}
colToX(mtcars, mpg, cyl, X = 2)
编辑: OP 更改了问题以要求 X
应该是同一数据框中的另一列。以下是我的新解决方案:
colToX <- function(dt, ..., X) {
cols <- quos(...)
X_quo <- enquo(X)
mutate_at(dt, vars(!!!cols), funs(.data$`!!`(X_quo)))
}
colToX(mtcars, mpg, cyl, X = hp)
在这里,我使用 funs
函数来创建对从 vars
引用的每个列的函数调用列表。 .data
指的是 mutate_at
中的输入数据帧(在本例中为 dt
)。我使用 enquo
将 X
中调用的内容转换为 quosure 并使用 !!
取消引用。
结果:
mpg cyl disp hp drat wt qsec vs am gear carb
1 110 110 160.0 110 3.90 2.620 16.46 0 1 4 4
2 110 110 160.0 110 3.90 2.875 17.02 0 1 4 4
3 93 93 108.0 93 3.85 2.320 18.61 1 1 4 1
4 110 110 258.0 110 3.08 3.215 19.44 1 0 3 1
5 175 175 360.0 175 3.15 3.440 17.02 0 0 3 2
6 105 105 225.0 105 2.76 3.460 20.22 1 0 3 1
7 245 245 360.0 245 3.21 3.570 15.84 0 0 3 4
8 62 62 146.7 62 3.69 3.190 20.00 1 0 4 2
9 95 95 140.8 95 3.92 3.150 22.90 1 0 4 2
10 123 123 167.6 123 3.92 3.440 18.30 1 0 4 4
...