如何在列上使用tidyeval进行突变? [英] How to use tidyeval on a column to mutate?
问题描述
很抱歉造成混乱,但是最终,我发布的第一个示例(在页面底部)并没有帮助我弄清楚tidyeval如何与mutate配合使用,因此我添加了一个新示例./p>
我想创建一个带有三个参数的函数:
- 数据框
- 要突变的列
- 一个变量(来自数据框),用于替换要变异的值
例如,要将mpg
中的值替换为carb
中的值,我尝试了以下方法:
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)
由于以下原因不起作用:
mutate_impl(.data,点)中的错误:不支持列
mpg
类型功能
我的第一个示例(为此,@ MrFlick和@UseR都可以正常工作):
例如,下面的一个应mutate()
将所有1分配给在...
中传递的变量
colTo1 <- function(dt, ...) {
col <- quo(...)
mutate(mtcars, !!col := 1)
}
colTo1(mtcars, mpg)
错误:LHS必须是名称或字符串
最终,输出应与mutate(mtcars, mpg = 1)
@MrFlick的解决方案适用于单列情况,但是由于OP使用...
作为参数,因此我认为OP也希望该函数能够分成多列.例如,以下操作将不起作用:
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(dt, !!col := 1)
}
colTo1(mtcars, mpg, cyl)
inherits(x,"quosure")中的错误:找不到对象'cyl'
我们可以做的是使用quos
代替quo
和mutate_at
代替mutate
:
colTo1 <- function(dt, ...) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=1)
}
quos
将每个参数从...
转换为向量的向量.使用mutate_at
的vars
语法并使用rlang
中的!!!
进行显式拼接,我们可以取消引用cols
中的每个等价单,并在那些指定的列上进行变异.
现在这可以按预期工作了:
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
...
I'm sorry for the confusion but eventually, the first example I posted (at the bottom of the page), did not help me to figure out how tidyeval works with mutate, so I'm adding a new example.
I would like to create a function that takes three args:
- a dataframe
- the column(s) to mutate
- a variable (from the dataframe) to replace the values that are being mutated
For instance, to replace the values in mpg
with the values from carb
I tried this:
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)
which doesn't work since:
Error in mutate_impl(.data, dots) : Column
mpg
is of unsupported type function
My first example (@MrFlick's and @UseR's both work fine for this):
For instance, the one below should mutate()
assigning all 1 to the variable passed in ...
colTo1 <- function(dt, ...) {
col <- quo(...)
mutate(mtcars, !!col := 1)
}
colTo1(mtcars, mpg)
Error: LHS must be a name or string
Eventually, the output should be the same as mutate(mtcars, mpg = 1)
@MrFlick's solution works for the one column case, but since OP used ...
as an argument, I assume OP would also want the function to be able to take in multiple columns. For example, the following would not work:
colTo1 <- function(dt, ...) {
col <- quo_name(quo(...))
mutate(dt, !!col := 1)
}
colTo1(mtcars, mpg, cyl)
Error in inherits(x, "quosure") : object 'cyl' not found
What we can do is to use quos
instead of quo
and mutate_at
instead of mutate
:
colTo1 <- function(dt, ...) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=1)
}
quos
converts each argument from ...
into vector of quosures. Using mutate_at
's vars
syntax and explicit splicing with !!!
from rlang
, we can unquote each quosure in cols
, and mutate on those specified columns.
Now this works as intended:
colTo1(mtcars, mpg, cyl)
Result:
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
...
It's also easy enough to let "1
" be another argument to be passed into the function:
colToX <- function(dt, ..., X) {
cols <- quos(...)
mutate_at(dt, vars(!!!cols), function(x) x=X)
}
colToX(mtcars, mpg, cyl, X = 2)
Edit: OP changed the question to require that X
should be another column in the same dataframe. Below is my new solution:
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)
Here, I am using the funs
function to create a list of function calls to each column referenced from vars
. .data
refers to the input dataframe into mutate_at
(in this case dt
). I used enquo
to convert what's called from X
into a quosure and unquote it using !!
.
Result:
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进行突变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!