累积分组

时间:2018-12-02 15:19:48

标签: r dplyr grouping tidyverse cumulative-sum

我有以下数据框:

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的所有行。这些不是不相交的组,但我仍然想分别总结每个组。

例如,如果对于每个组我想要b的均值,结果将是:

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

请注意,在此简化示例背后的实际场景中,我无法单独分析不相交的组,然后再汇总相关的组。 summary函数必须“知道”该组中的所有行才能执行计算。

因此,当然,我可以使用一些应用函数并以良好的旧方法计算事物,并从中创建新的df,但我会寻找类似dplyr / tidyverse的函数来实现。

有什么建议吗?

4 个答案:

答案 0 :(得分:3)

这样的事情怎么样?

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进行取整,然后取累加总和加上该组以前的值在下一个组中。

答案 1 :(得分:1)

我看了一下,但看不到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  

答案 2 :(得分:0)

一种方法是将基本函数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

答案 3 :(得分:0)

我会这样:

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