如何在 dplyr 中创建动态的 mutate 调用编号和名称?
How to create a dynamic number and name of mutate calls in dplyr?
我正在制作一个动态排列函数来创建与顺序无关的参数。在函数之外,我已经能够使用 dplyr 对这种方法进行硬编码。但是,我想概括它,以便我可以使用相同的函数来排列 3 个因子或 6 个因子,而无需键入所有重复调用。但是,我还没有弄清楚如何让它工作。
这是一个包含 3 个变量的所有排列的简单数据框 df
:
#> dput(df)
structure(list(var1 = structure(c(1L, 1L, 2L, 2L, 3L, 3L), .Label = c("a",
"b", "c"), class = "factor"), var2 = structure(c(2L, 3L, 1L,
3L, 1L, 2L), .Label = c("a", "b", "c"), class = "factor"), var3 = structure(c(3L,
2L, 3L, 1L, 2L, 1L), .Label = c("a", "b", "c"), class = "factor"),
X1 = c(0.5, 0.5, 0.8, 0.8, 0.3, 0.3), X2 = c(0.8, 0.3, 0.5,
0.3, 0.5, 0.8), X3 = c(0.3, 0.8, 0.3, 0.5, 0.8, 0.5)), .Names = c("var1",
"var2", "var3", "X1", "X2", "X3"), row.names = c(NA, -6L), class = "data.frame")
我的目标是获得每个变量的平均顺序独立值。为此,我需要创建两个中间变量:一个是乘法 m1, m2, m3, m4
,一个是减法 s1, s2, s3, s4
。变量m1
和s1
是特殊的,m1 = X1
和s1 = X1-1
。但是,其他的需要参考前面的:m2 = X2*X1
和s2 = m2-m1
。
我试图将来自这个 SO 问题的想法结合起来:R - dplyr - mutate - use dynamic variable names 与一个 lazyeval interp,这样我就可以动态地引用其他变量并动态地命名变异的列。但是,它只保留了最后一个发送的内容,并且重命名不起作用,所以我得到了一个额外的列,例如,命名为 X2*X3
,在这个例子中使用 3 就可以了。当我有 5 个时,它给出了一个额外的列 X4*X5
。
for(n in 2:n_params) {
varname <- paste("m", n, sep=".")
df <- mutate_(df, .dots = setNames(interp(~one*two, one=as.name(paste0("X",n-1)),
two=as.name(paste0("X",n))),varname))
df
}
因为我不知道为什么这不起作用,所以我设置了一系列计算 m
s 和 s
s 的 if 语句。
xx <- data.frame(df) %>%
mutate(m1 = X1,
s1 = X1 - 1)
if(n_params >= 2) {
xx <- data.frame(xx) %>%
mutate(m2 = m1 * X2,
s2 = m2 - m1)
}
if(n_params >= 3) {
xx <- data.frame(xx) %>%
mutate(m3 = m2 * X3,
s3 = m3 - m2)
}
if(n_params >= 4) {
xx <- data.frame(xx) %>%
mutate(m4 = m3 * X4,
s4 = m4 - m3)
}
if(n_params >= 5) {
xx <- data.frame(xx) %>%
mutate(m5 = m4 * X5,
s5 = m5 - m4)
}
if(n_params >= 6) {
xx <- data.frame(xx) %>%
mutate(m6 = m5 * X6,
s6 = m6 - m5)
}
看来我应该能够编写一个创建它的函数,
在伪代码中:
function(n_params) {
function(x) {
new_df <- df %>%
mutate(m1 = X1,
s1 = X1 - 1)
for(i in 2:n_params){
new_df <- append(call to new_df,
mutate(mi = Xi*Xi-1,
si = mi-mi-1)
}
}
}
但是,我不知道如何组合 lazyeval interp
和 setNames 以允许引用先前的变异值。
我可以将它留在 if 函数中,但如果可能的话,我希望让它更紧凑。
感兴趣的最终最终输出是每个初始变量的所有排列的平均 s 值。我在一个单独的函数中这样做。
不是最漂亮的东西,但它确实有效:
n_params = 3
xx1 = df %>%
mutate(m1 = X1,
s1 = X1 - 1)
for (i in 2:n_params) {
xx1 = xx1 %>%
mutate_(.dots = setNames(list(varval = paste0("m", i - 1, " * X", i)),
paste0("m", i))) %>%
mutate_(.dots = setNames(list(varval = paste0("m", i, " - m", i - 1)),
paste0("s", i)))
}
可能有很多更好的方法来使用lazyeval
。希望其他人会给出一个很好的答案,但这确实与您的问题中产生的 xx
匹配(对于 n_params = 3
):
identical(xx, xx1)
# [1] TRUE
我正在制作一个动态排列函数来创建与顺序无关的参数。在函数之外,我已经能够使用 dplyr 对这种方法进行硬编码。但是,我想概括它,以便我可以使用相同的函数来排列 3 个因子或 6 个因子,而无需键入所有重复调用。但是,我还没有弄清楚如何让它工作。
这是一个包含 3 个变量的所有排列的简单数据框 df
:
#> dput(df)
structure(list(var1 = structure(c(1L, 1L, 2L, 2L, 3L, 3L), .Label = c("a",
"b", "c"), class = "factor"), var2 = structure(c(2L, 3L, 1L,
3L, 1L, 2L), .Label = c("a", "b", "c"), class = "factor"), var3 = structure(c(3L,
2L, 3L, 1L, 2L, 1L), .Label = c("a", "b", "c"), class = "factor"),
X1 = c(0.5, 0.5, 0.8, 0.8, 0.3, 0.3), X2 = c(0.8, 0.3, 0.5,
0.3, 0.5, 0.8), X3 = c(0.3, 0.8, 0.3, 0.5, 0.8, 0.5)), .Names = c("var1",
"var2", "var3", "X1", "X2", "X3"), row.names = c(NA, -6L), class = "data.frame")
我的目标是获得每个变量的平均顺序独立值。为此,我需要创建两个中间变量:一个是乘法 m1, m2, m3, m4
,一个是减法 s1, s2, s3, s4
。变量m1
和s1
是特殊的,m1 = X1
和s1 = X1-1
。但是,其他的需要参考前面的:m2 = X2*X1
和s2 = m2-m1
。
我试图将来自这个 SO 问题的想法结合起来:R - dplyr - mutate - use dynamic variable names 与一个 lazyeval interp,这样我就可以动态地引用其他变量并动态地命名变异的列。但是,它只保留了最后一个发送的内容,并且重命名不起作用,所以我得到了一个额外的列,例如,命名为 X2*X3
,在这个例子中使用 3 就可以了。当我有 5 个时,它给出了一个额外的列 X4*X5
。
for(n in 2:n_params) {
varname <- paste("m", n, sep=".")
df <- mutate_(df, .dots = setNames(interp(~one*two, one=as.name(paste0("X",n-1)),
two=as.name(paste0("X",n))),varname))
df
}
因为我不知道为什么这不起作用,所以我设置了一系列计算 m
s 和 s
s 的 if 语句。
xx <- data.frame(df) %>%
mutate(m1 = X1,
s1 = X1 - 1)
if(n_params >= 2) {
xx <- data.frame(xx) %>%
mutate(m2 = m1 * X2,
s2 = m2 - m1)
}
if(n_params >= 3) {
xx <- data.frame(xx) %>%
mutate(m3 = m2 * X3,
s3 = m3 - m2)
}
if(n_params >= 4) {
xx <- data.frame(xx) %>%
mutate(m4 = m3 * X4,
s4 = m4 - m3)
}
if(n_params >= 5) {
xx <- data.frame(xx) %>%
mutate(m5 = m4 * X5,
s5 = m5 - m4)
}
if(n_params >= 6) {
xx <- data.frame(xx) %>%
mutate(m6 = m5 * X6,
s6 = m6 - m5)
}
看来我应该能够编写一个创建它的函数,
在伪代码中:
function(n_params) {
function(x) {
new_df <- df %>%
mutate(m1 = X1,
s1 = X1 - 1)
for(i in 2:n_params){
new_df <- append(call to new_df,
mutate(mi = Xi*Xi-1,
si = mi-mi-1)
}
}
}
但是,我不知道如何组合 lazyeval interp
和 setNames 以允许引用先前的变异值。
我可以将它留在 if 函数中,但如果可能的话,我希望让它更紧凑。
感兴趣的最终最终输出是每个初始变量的所有排列的平均 s 值。我在一个单独的函数中这样做。
不是最漂亮的东西,但它确实有效:
n_params = 3
xx1 = df %>%
mutate(m1 = X1,
s1 = X1 - 1)
for (i in 2:n_params) {
xx1 = xx1 %>%
mutate_(.dots = setNames(list(varval = paste0("m", i - 1, " * X", i)),
paste0("m", i))) %>%
mutate_(.dots = setNames(list(varval = paste0("m", i, " - m", i - 1)),
paste0("s", i)))
}
可能有很多更好的方法来使用lazyeval
。希望其他人会给出一个很好的答案,但这确实与您的问题中产生的 xx
匹配(对于 n_params = 3
):
identical(xx, xx1)
# [1] TRUE