使用 dplyr 时使用 rle 按运行分组

Use rle to group by runs when using dplyr

在 R 中,我想在根据变量 x 的 运行 分组后总结我的数据(也就是每组数据对应于连续的数据子集x 值相同)。例如,考虑以下数据框,我想计算 x 的每个 运行 中的平均 y 值:

(dat <- data.frame(x=c(1, 1, 1, 2, 2, 1, 2), y=1:7))
#   x y
# 1 1 1
# 2 1 2
# 3 1 3
# 4 2 4
# 5 2 5
# 6 1 6
# 7 2 7

在这个例子中,x 变量有 运行 个长度为 3,然后是 2,然后是 1,最后是 1,在这四个中取值 1、2、1 和 2 运行秒。 y 在这些组中的对应均值是 2、4.5、6 和 7。

使用 tapply 在基数 R 中很容易执行此分组操作,将 dat$y 作为数据传递,使用 rle 从中计算 运行 数字dat$x,并传递所需的汇总函数:

tapply(dat$y, with(rle(dat$x), rep(seq_along(lengths), lengths)), mean)
#   1   2   3   4 
# 2.0 4.5 6.0 7.0 

我想我可以直接将这个逻辑转移到 dplyr,但我的尝试到目前为止都以错误告终:

library(dplyr)
# First attempt
dat %>%
  group_by(with(rle(x), rep(seq_along(lengths), lengths))) %>%
  summarize(mean(y))
# Error: cannot coerce type 'closure' to vector of type 'integer'

# Attempt 2 -- maybe "with" is the problem?
dat %>%
  group_by(rep(seq_along(rle(x)$lengths), rle(x)$lengths)) %>%
  summarize(mean(y))
# Error: invalid subscript type 'closure'

为了完整起见,我可以使用 cumsumheadtail 自己重新实现 rle 运行 id 来解决这个问题,但是它使分组代码更难阅读并且需要重新发明轮子:

dat %>%
  group_by(run=cumsum(c(1, head(x, -1) != tail(x, -1)))) %>%
  summarize(mean(y))
#     run mean(y)
#   (dbl)   (dbl)
# 1     1     2.0
# 2     2     4.5
# 3     3     6.0
# 4     4     7.0

是什么导致我的基于 rle 的分组代码在 dplyr 中失败,有什么解决方案可以让我在按 [=43= 分组时继续使用 rle ] id?

如果您显式创建分组变量 g 它或多或少会起作用:

> dat %>% transform(g=with(rle(dat$x),{ rep(seq_along(lengths), lengths)}))%>%                                   
 group_by(g) %>% summarize(mean(y))
Source: local data frame [4 x 2]

      g mean(y)
  (int)   (dbl)
1     1     2.0
2     2     4.5
3     3     6.0
4     4     7.0

我在这里使用 transform 因为 mutate 会抛出错误。

一个选项似乎是使用 {},如:

dat %>%
    group_by(yy = {yy = rle(x); rep(seq_along(yy$lengths), yy$lengths)}) %>%
    summarize(mean(y))
#Source: local data frame [4 x 2]
#
#     yy mean(y)
#  (int)   (dbl)
#1     1     2.0
#2     2     4.5
#3     3     6.0
#4     4     7.0

如果未来的 dplyr 版本也有等同于 data.table 的 rleid 功能就好了。


我注意到使用 data.frametbl_df 输入时会出现此问题,但在使用 tbl_dtdata.table 输入时不会:

dat %>% 
    tbl_df %>% 
    group_by(yy = with(rle(x), rep(seq_along(lengths), lengths))) %>%
    summarize(mean(y))
Error: cannot coerce type 'closure' to vector of type 'integer'

dat %>% 
    tbl_dt %>% 
    group_by(yy = with(rle(x), rep(seq_along(lengths), lengths))) %>%
    summarize(mean(y))
Source: local data table [4 x 2]

     yy mean(y)
  (int)   (dbl)
1     1     2.0
2     2     4.5
3     3     6.0
4     4     7.0

我在 dplyr 的 github 页面上将其报告为 issue