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

时间:2016-02-06 21:05:51

标签: r dplyr run-length-encoding

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

(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使用dat$y作为数据,使用rle计算dat$x的运行编号,可以很容易地在基准R中执行此分组操作,并传递所需的汇总函数:

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'

为了完整起见,我可以使用rlecumsumhead重新实现tail运行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中失败的原因是什么,是否有任何解决方案可以让我在按运行ID分组时继续使用rle

2 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分:2)

如果明确创建分组变量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会抛出错误。