使用dplyr进行多条件变异

5

我有一个数据框(df),如下所示,我想使用dplyr添加一个额外的列result,如果z == "gone"且组y中的x是最大值,则该列将取值为1。

   y  x    z
1  a  3 gone
2  a  5 gone
3  a  8 gone
4  a  9 gone
5  a 10 gone
6  b  1     
7  b  2     
8  b  4     
9  b  6     
10 b  7     

如果我只是选择每个组的最大值,那么结果将是:

df %>%
  group_by(y) %>%
  slice(which.max(x))

这将返回:

   y  x  z
1  a 10  gone
2  b  7      

这不是我想要的。我需要利用每个 y 组中 x 的最大值,同时检查是否满足 z == "gone",如果满足则为 1,否则为 0。代码如下:

   y  x    z result
1  a  3 gone      0
2  a  5 gone      0
3  a  8 gone      0
4  a  9 gone      0
5  a 10 gone      1
6  b  1           0
7  b  2           0
8  b  4           0
9  b  6           0
10 b  7           0

我认为我需要在mutate()内使用条件语句,但是我似乎找不到一个例子。请给予建议。

2个回答

6

我们可以使用 data.table 来完成这个任务。我们将 'data.frame' 转换为 'data.table' (setDT(df)),按 'y' 分组,创建 'x' 的最大值和 'z' 中的 'gone' 元素的逻辑条件,将其转换为整数 (as.integer) 并将输出分配 (:=) 到新列 ('result')。

library(data.table)
setDT(df)[, result := as.integer(x==max(x) & z=='gone') , by = y]
df
#    y  x    z result
# 1: a  3 gone      0
# 2: a  5 gone      0
# 3: a  8 gone      0
# 4: a  9 gone      0
# 5: a 10 gone      1
# 6: b  1           0
# 7: b  2           0
# 8: b  4           0
# 9: b  6           0
#10: b  7           0

或者我们可以使用base R中的ave函数。

df$result <- with(df, +(ave(x, y, FUN=max)==x & z=='gone' ))

1
谢谢!这个也可以工作:df %>% group_by(y) %>% mutate(result = ifelse(x == max(x) & z == "gone", 1, 0)) - Ryan Erwin
1
@RyanErwin 那也可以,我认为PierreLafortune的评论中使用+(x==max(x) & z=='gone'应该对dplyr来说非常快速。 - akrun
1
谢谢:我不熟悉+(....)。你知道在哪里可以找到关于这个语法的文档吗? - Ryan Erwin
2
很好的解决方案,使用了平均值。 - Pierre L

6
使用dplyr,您可以使用以下功能:
df %>% group_by(y) %>% mutate(result = +(x == max(x) & z == 'gone'))
+(..)符号是as.integer的简写,将逻辑输出强制转换为1和0。有些人不喜欢它,这是代码长度与可读性的取舍问题。效率收益可以根据情况进行讨论。
此外,为了欣赏data.tabledplyr对R数据处理所做的贡献,让我们以老式的“拆分-应用-合并”方式完成同样的操作:
#split data.frame by group
split.df <- split(df, df$y)

#apply required function to each group
lst <- lapply(split.df, function(dfx) {
        dfx$result <- +(dfx$x == max(dfx$x) & dfx$z == "gone")
        dfx})

#combine result in new data.frame
newdf <- do.call(rbind, lst)

3
备选代码高尔夫(可能更易读):(x == max(x))*(z == 'gone') - Frank

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