我有以下数据框:
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的函数来实现。
有什么建议吗?
答案 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