使用 magrittr 和 dplyr 在 R 中进行条件数据框变异

18

我希望利用magrittr和dplyr的简洁性,根据其他列中的值,在子集列之间复制单个值。这是一个简单的例子; 我想将此想法应用于多个条件下的大型数据集的许多列,并在一系列命令中使用。

以数据框df <- data.frame(a = 1:5, b = 6:10, x = 11:15, y = 16:20)为例:

a   b   x   y

1   6   11  16
2   7   12  17
3   8   13  18
4   9   14  19
5   10  15  20

对于当 a = 5 的那一行,我想用 b = 7 的那一行的值替换 xy 的值,以得到:

a   b   x   y

1   6   11  16
2   7   12  17
3   8   13  18
4   9   14  19
5   10  12  17

这个尝试失败了:

foo <- function(x){ifelse(df$a == 5, df[df$b == 7, .(df$x)], x)}
df %<>%  mutate_each(funs(foo), x, y)

我能够提供的最接近的翻译是:

bar <- function(x){ifelse(df$a == 5, df[df$b == 7, "x"], x)}
df %<>%  mutate_each(funs(bar), x, y)

但是这是错误的,因为它将两个值都替换为x的值,而不是分别替换为xy的值。

感谢您的建议。


1
%<>%%>% 有什么区别? - Marcin
5
x %<>% f 出自于 magrittr 包,等同于常见的模式 x <- x %>% f - asachet
2
%>% 也来自于 magrittr 包... - David Arenburg
1
@DavidArenburg %>% 只需要加载 dplyr,而在目前和 CRAN 版本中使用 %<>% 则需要加载 magrittr。确实,%>% 是通过 dplyrmagrittr 中引入的,但对于最终用户来说,知道要加载哪些包更为相关! - asachet
1
@antoine-sac 嗯,那只是你的观点而已,伙计。 - David Arenburg
3个回答

13
你可以使用 mutate_eachreplace 函数来完成这个任务:
df %>% mutate_each(funs(replace(., a==5, nth(., which(b==7)))), x, y)

输出:

  a  b  x  y
1 1  6 11 16
2 2  7 12 17
3 3  8 13 18
4 4  9 14 19
5 5 10 12 17

根据@docendodiscimus的评论,它可以进一步缩短为以下内容(也许[which更好):

df %>% mutate_each(funs(replace(., a==5, .[b==7])), x, y)

2
谢谢,但我想将这种方法应用于大量的列,可能是在其他地方定义的,所以这会很快变得难以处理。 - Patrick Hogan
更新了答案。现在我认为这应该符合您的需求。 - LyzandeR
1
不错!可以缩短为df %>% mutate_each(funs(replace(., a==5, .[b==7])), x, y) - talat
这正是我正在寻找的 - 非常感谢! - Patrick Hogan
非常欢迎您,@PatrickHogan。很高兴我能帮到您 :) - LyzandeR
显示剩余2条评论

9

提到 data.table 解决方案,如下:

require(data.table)
setDT(df)[a == 5, c("x", "y") := df[b == 7, .SD, .SDcols = c("x", "y")]]

> df
   a  b  x  y
1: 1  6 11 16
2: 2  7 12 17
3: 3  8 13 18
4: 4  9 14 19
5: 5 10 12 17

或者,你还可以使用以下方法:

cols <- c("x", "y")
setDT(df)[a == 5, (cols) := df[b == 7, .SD, .SDcols = cols]]
# or 
cols <- c("x", "y")
setDT(df)[a == 5, (cols) := df[b == 7, cols, with = FALSE]]

2
DT <- setDT(df) 真的没有多少意义,因为 df 现在也是一个 data.table,并且已经通过引用进行了更新。 - Arun
@Arun:我完全同意。当我开始使用data.table时,“按引用更新”的概念对我来说非常陌生。由于问题要求使用dplyr解决方案,我想让我的答案更容易理解一些。 - Rentrop
我明白了,最好使用as.data.table。同时,使用with=FALSE.SD + .SDcols可以帮助展示它可以轻松扩展到许多列。 - Arun
@Arun:看一下我的编辑。我不明白你使用 with=FALSE 的意思。随意编辑。 - Rentrop
好的,谢谢!这也可以使用管道操作:df %<>% as.data.table %>% .[a == 5, c("x", "y") := .[b == 7, .SD, .SDcols = c("x", "y")]] - Patrick Hogan
2
我冒昧改善了您的答案,希望您不介意。 - Jaap

5

如果您的主要需求是在较长的dplyr管道中应用该函数,您可以执行以下示例操作:

foo <- function(df, cols = c("x", "y")) {
  df[df$a == 5, cols] <- df[df$b == 7, cols]
  df
}

df %>% ... %>% foo(c("x", "y")) %>% ... 
#  a  b  x  y
#1 1  6 11 16
#2 2  7 12 17
#3 3  8 13 18
#4 4  9 14 19
#5 5 10 12 17

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接