如何在不创建分组变量的情况下进行group_by?

时间:2018-03-16 16:27:10

标签: r dplyr data.table

我需要使用辅助分组变量执行基本的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)这样的东西来确定)但是我更喜欢有一个更清洁的解决方案。

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

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

2 个答案:

答案 0 :(得分:5)

我们可以创建一个功能来处理这个问题。假设要创建的临时分组变量是“tmp&#39;”,通过与数据集的列名连接并调用make.unique,如果已经有一个&t; tmp&#39;数据集中的列,重复的列将重命名为&#39; tmp.1&#39;。使用!!,使用&#39; tmp.1&#39;命名列。 (来自nm1)不会影响&#39; tmp&#39;已存在于数据集中。如果没有&#39; tmp&#39;列,则分组列将被命名为&#39; tmp&#39;然后使用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的评论)关于传递表达式

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

答案 1 :(得分:3)

您可以改为使用ave

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

by也有效:

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