【问题标题】:How to group_by without creating a grouping variable?如何在不创建分组变量的情况下进行分组?
【发布时间】:2018-03-16 16:27:09
【问题描述】:

我需要使用辅助分组变量执行基本的group_by / mutate 操作。例如:

df <- data.frame(
  u = c(0, 0, 1, 0, 1),
  v = c(8, 4, 2, 3, 5)
)

df %>%
  group_by(tmp = cumsum(u)) %>%
  mutate(w = cumprod(v)) %>%
  ungroup %>%
  select(-tmp)

我的问题是,如果 df 恰好已经包含一个名为 tmp 的列,我会丢失它。

当然,我可以选择一个非常奇特的名称而不是 tmp 以减少发生冲突的可能性(或者我什至可以选择类似 strrep("z", max(nchar(names(df))) + 1) 的名称,但我希望有一个更简洁的解决方案。

换句话说,我正在寻找与data.table 行等效的dplyr

setDT(df)[, w := cumprod(v), by = cumsum(u)]

【问题讨论】:

  • df %&gt;% group_by(cumsum(u)) %&gt;% mutate(w = cumprod(v)) %&gt;% ungroup %&gt;% select(-`cumsum(u)`)?
  • 可能会有一个名为cumsum(u) 的列。 (我同意这不太可能,但仍然......)

标签: r dplyr data.table


【解决方案1】:

我们可以创建一个函数来处理这个问题。假设要创建的临时分组变量是'tmp',通过与数据集的列名连接并调用make.unique,如果数据集中已经存在'tmp'列,则重复的将重命名为' tmp.1'。使用!!,用'tmp.1'(来自nm1)命名列不会影响数据集中已经存在的'tmp'。如果没有 'tmp' 列,则分组列将被命名为 'tmp' 并随后用select 删除

f1 <- function(dat, grpCol, Col) {
  grpCol <- enquo(grpCol)
  Col <- enquo(Col)

 changeCol <- "tmp"
 nm1 <-  tail(make.unique(c(names(dat), changeCol)), 1)
 dat %>%
    group_by(!! (nm1) := cumsum(!! grpCol)) %>%
    mutate(w = cumprod(!!Col)) %>%
     ungroup %>%
     select(-one_of(nm1)) 


}

-运行函数

f1(df, u, v)
# A tibble: 5 x 3
#      u     v     w
#  <dbl> <dbl> <dbl>
#1  0     8.00  8.00
#2  0     4.00 32.0 
#3  1.00  2.00  2.00
#4  0     3.00  6.00
#5  1.00  5.00  5.00


 f1(df %>% mutate(tmp = 1), u, v) #create a 'tmp' column in dataset
# A tibble: 5 x 4
#      u     v   tmp     w
#  <dbl> <dbl> <dbl> <dbl>
#1  0     8.00  1.00  8.00
#2  0     4.00  1.00 32.0 
#3  1.00  2.00  1.00  2.00
#4  0     3.00  1.00  6.00
#5  1.00  5.00  1.00  5.00

作为关于传递表达式的后续(来自@Frank 的 cmets)

expr <- quos(tmp = cumsum(u), w = cumprod(v))
#additional checks outside the function
names(expr)[1] <- if(names(expr)[1] %in% names(df)) 
             strrep(names(expr)[1], 2) else names(expr)[1]


f2 <- function(dat, exprs ){

dat %>%
    group_by(!!! exprs[1]) %>%
    mutate(!!! exprs[2])

}

f2(df, expr)
# A tibble: 5 x 4
# Groups: tmp [3]
#      u     v   tmp     w
#  <dbl> <dbl> <dbl> <dbl> 
#1  0     8.00  0     8.00
#2  0     4.00  0    32.0 
#3  1.00  2.00  1.00  2.00
#4  0     3.00  1.00  6.00
#5  1.00  5.00  2.00  5.00

【讨论】:

  • 嗯,我想知道您能否推广到一个函数,该函数分别支持 mutate 和 group_by 的任意一对表达式,如 data.table 的 jby= 中的表达式? (此函数特定于一列的 cumsum 和另一列的 cumprod。)我不太了解 rlang,不知道从哪里开始,但无论如何,看看它会很有教育意义。
  • 是的,可以写表达式,所以可以打电话给f(DT, grp_expr = list(cumsum(x)), mutate_expr = list (w = cumprod(y)) 什么的。似乎很麻烦,必须为 cumsum + cumprod 的特殊情况编写一个函数,然后再为所有其他情况编写一个函数。 (不知道这是否是 rlang 能做的最好的事情......)
  • @Frank 可能是 f1(df, u, v, "cumsum", "cumprod") 并更改为 get(fn1)get(fn2)
  • @Frank 受到您的启发,我添加了一个选项
【解决方案2】:

您可以改用ave

df %>% mutate(w = ave(v, cumsum(u), FUN = cumprod))

by 也可以:

df %>% 
   by(cumsum(.$u), mutate, w = cumprod(v)) %>% 
   unclass %>% 
   bind_rows

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-16
    • 1970-01-01
    • 2013-10-24
    • 2016-10-31
    • 2015-11-14
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多