Используйте rle для группировки по запускам при использовании dplyr

В 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.

Эту сгруппированную операцию в базе R легко выполнить, используя tapply, передав 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'

Для полноты картины я мог бы сам переопределить rle идентификатор запуска, используя cumsum, head и tail, чтобы обойти это, но это затрудняет чтение кода группировки и требует небольшого изобретения колеса:

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, и есть ли какое-либо решение, которое позволяет мне продолжать использовать rle при группировке по идентификатору выполнения?


person josliber♦    schedule 06.02.2016    source источник


Ответы (2)


Один из вариантов, по-видимому, заключается в использовании {}, например:

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 также был эквивалент функции rleid из data.table.


Я заметил, что эта проблема возникает при использовании ввода data.frame или tbl_df, но не при использовании ввода tbl_dt или data.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.

person talat    schedule 10.02.2016
comment
Похоже, проблема закрыта на примере github.com/hadley/dplyr/issues/1400, над которым они, похоже, работают. - person josliber♦; 01.03.2016
comment
Отлично. Не могли бы вы объяснить или направить меня к документации о том, как {} работает в вызове group_by здесь? Поиск в Google dplyr {} не особенно эффективен, но, может быть, здесь есть причудливое название для этого типа вызова? Спасибо! - person Jordan; 04.06.2016
comment
@Jordan, это не специфическая функция dplyr. Это из базы R, и вы можете проверить help("{") - person talat; 05.06.2016

Если вы явно создаете группирующую переменную 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 выдает ошибку.

person Neal Fultz    schedule 06.02.2016