накопительная группировка - PullRequest
       27

накопительная группировка

0 голосов
/ 02 декабря 2018

У меня есть следующий фрейм данных:

df = data.frame(a = c(1,1,3,2,2), b=6:10)
##  a    b
##  1    6
##  1    7
##  3    3
##  2    9
##  2    10

Я хочу анализировать данные по группам (a является параметром группировки), но вместо обычного (например, каждое значение указывает группу строк,и группы не пересекаются) Мне нужны "кумулятивные группы".то есть для значения a = i группа должна содержать все строки, в которых a <= i.Это не непересекающиеся группы, но все же я хочу обобщить каждую группу отдельно. </p>

Так, например, если для каждой группы я хочу получить среднее значение b, результат будет:

##    a    mean_b
##    1    6.5
##    2    8
##    3    7

обратите внимание, что в реальном сценарии, лежащем в основе этого упрощенного примера, я не могу отдельно анализировать непересекающуюся группу, а затем объединять соответствующие группы.функция суммирования должна быть «осведомлена» обо всех строках в этой группе, чтобы выполнить вычисление.

Поэтому, конечно, я могу использовать некоторые функции применения и вычислить вещи старым добрым способом, и сделать новый dfиз этого, но я ищу dplyr / tidyverse-подобные функции, чтобы сделать это.

есть предложения?

Ответы [ 4 ]

0 голосов
/ 04 декабря 2018

Я бы сделал это следующим образом:

df %>% 
  arrange(a) %>%
  map_dfr(seq_along(as <- unique(.$a)),
          ~filter(.y, a %in% as[1:.]),.y = ., .id = "a") %>%
  group_by(a = meta_group) %>%
  summarise(b = mean(b))

# # A tibble: 3 x 2
# a     b
# <chr> <dbl>
# 1     1   6.5
# 2     2   7.0
# 3     3   8.0

Если вы хотите отдельную функцию, вы можете сделать:

summarize2 <- function(.data, ..., .by){
  grps <- select_at(.data,.by) %>% pull %>% unique
  .data %>%
    arrange_at(.by) %>%
    map_dfr(seq_along(grps),
             ~ filter_at(.y, .by,all_vars(. %in% grps[1:.x])),
             .y = .,
             .id = "meta_group") %>%
    group_by(meta_group) %>%
    summarise(...)
}

df %>% 
  summarize2(b = mean(b), .by = "a")
# # A tibble: 3 x 2
#   meta_group     b
#        <chr> <dbl>
# 1          1   6.5
# 2          2   7.0
# 3          3   8.0

df %>% 
  summarize2(b = mean(b), .by = vars(a))
# # A tibble: 3 x 2
#   meta_group     b
#        <chr> <dbl>
# 1          1   6.5
# 2          2   7.0
# 3          3   8.0
0 голосов
/ 02 декабря 2018

Один из способов - использовать базовую функцию Reduce с аргументом accumulate = TRUE.После объединения можно применить любую функцию, например,

Reduce(c, split(df$b,df$a), accumulate = TRUE)
#[[1]]
#[1] 6 7

#[[2]]
#[1]  6  7  9 10

#[[3]]
#[1]  6  7  9 10  3

, а затем для среднего значения

sapply(Reduce(c, split(df$b,df$a), accumulate = TRUE), mean)
[1] 6.5 8.0 7.0
.
0 голосов
/ 02 декабря 2018

Я посмотрел, и я не понимаю, как это возможно с самой dplyr.Однако мы можем взломать функцию group_by, чтобы сделать ее кумулятивной.Я быстро проведу вас через это:

Во-первых, я делаю ваш df.Это не совсем соответствует вашему выводу, поэтому я немного изменил его.

df = data.frame(a = c(1,1,3,2,2), b=6:10)
df$b[3] <- 3

Теперь я использую обычный group_by, чтобы проверить, что он на самом деле делает с data.frame.

library(dplyr)
df_grouped <- df %>%
  arrange(a) %>%
  group_by(a)
> attributes(df_grouped)
$class
[1] "grouped_df" "tbl_df"     "tbl"        "data.frame"

$row.names
[1] 1 2 3 4 5

$names
[1] "a" "b"

$vars
[1] "a"

$drop
[1] TRUE

$indices
$indices[[1]]
[1] 0 1

$indices[[2]]
[1] 2 3

$indices[[3]]
[1] 4


$group_sizes
[1] 2 2 1

$biggest_group_size
[1] 2

$labels
  a
1 1
2 2
3 3

Таким образом, помимо прочего, существует новый атрибут с именем indices, на который ссылается группа каждого элемента в сгруппированной переменной.На самом деле мы можем просто изменить это, чтобы сделать его кумулятивным.

for (i in seq_along(attributes(df_grouped)[["indices"]])[-1]) {
  attributes(df_grouped)[["indices"]][[i]] <- c(
    attributes(df_grouped)[["indices"]][[i - 1]],
    attributes(df_grouped)[["indices"]][[i]]
  )
}

Это выглядит немного странно, но просто.Элементы каждой группы добавляются в следующую группу.Например, все элементы из группы 1 добавляются в группу 2.

> attributes(df_grouped)$indices
[[1]]
[1] 0 1

[[2]]
[1] 0 1 3 4

[[3]]
[1] 0 1 3 4 2

Мы можем использовать измененные группы обычным образом dplyr.

> df_grouped %>%
+   summarise(sum_b = mean(b))
# A tibble: 3 x 2
      a sum_b
  <dbl> <dbl>
1     1   6.5
2     2   8  
3     3   7 

Теперь, конечно, это довольнонекрасиво и выглядит очень нахальноНо внутри функции, которая на самом деле не имеет значения, пока она все еще эффективна (что это такое).Итак, давайте сделаем пользовательский group_by.

group_by_cuml <- function(.data, ...) {
  .data_grouped <- group_by(.data, ...)
  for (i in seq_along(attributes(.data_grouped)[["indices"]])[-1]) {
    attributes(.data_grouped)[["indices"]][[i]] <- c(
      attributes(.data_grouped)[["indices"]][[i - 1]],
      attributes(.data_grouped)[["indices"]][[i]]
    )
  }
  return(.data_grouped)
}

Теперь вы можете использовать пользовательскую функцию в чистой dplyr трубе.

> df %>%
+   group_by_cuml(a) %>% 
+   summarise(sum_b = mean(b))
# A tibble: 3 x 2
      a sum_b
  <dbl> <dbl>
1     1   6.5
2     2   8  
3     3   7  
0 голосов
/ 02 декабря 2018

Как насчет этого?

library(dplyr)

df %>%
 arrange(a) %>%
 group_by(a) %>%
 summarise(sum_b = sum(b)) %>%
 ungroup() %>%
 mutate(sum_b = cumsum(sum_b))

#     a sum_b
#  <dbl> <int>
#1    1.    13
#2    2.    32
#3    3.    40

Мы берем sum по группе (a), а затем берем накопленную сумму, добавляя предыдущее значение группы в следующую группу.

...