Используйте 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
на основе rle
не работает в dplyr
, и существует ли какое-либо решение, которое позволяет мне продолжать использовать rle
при группировке по идентификатору запуска?
Ответы
Ответ 1
Одним из вариантов является использование {}
, как в:
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.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
Я сообщил об этом как issue на странице dplyr github.
Ответ 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
выдает ошибку.