使用R和dplyr执行累积组操作

时间:2016-08-03 14:17:54

标签: r dplyr

我正在尝试根据顺序组ID处理数据。有J组,我想为组i < j=1..J

运行数据处理功能

最简单的情况是每一行都是它自己的组,你计算累积总和。但是我在每个组中有多行,处理比求和更复杂。

以下是我的数据格式的最小示例:

row | group | value
----|-------|------
  1 |     1 |  2065
  2 |     1 |  2075
  3 |     2 | 18008
  4 |     2 | 17655
  : |     : |     :
N-1 |   J-1 |  2345
  N |     J |  5432

我想到的一个解决方案是复制我的数据,将其堆叠并重新分配每个数据中的组,以便将组i<j添加到j。这将导致非常长的数据帧,如:

row | group | value
----|-------|------
  1 |     1 |  2065
  2 |     1 |  2075
  3 |     2 |  2065
  4 |     2 |  2075
  5 |     2 | 18008
  6 |     2 | 17655
  : |     : |     :

然而,由于我的数据会被多次复制,这似乎很乏味且效率低下。

有没有人知道以更有效的方式处理累积组中的数据?

3 个答案:

答案 0 :(得分:2)

以下是三个示例,一个包含aggregate,一个包含data.table,最后一个包含dplyr

首先创建数据框

library(data.table)
library(dplyr)

group <- c(1,1,2,2,3)
value <- c(2065, 2075, 18008, 17655, 561)

使用 data.table ,您可以使用此功能

dat <- data.table(group, value)
recap <- dat[, list(somma = sum(value)), by = group]

使用包统计信息中的聚合

dat <- data.frame(group, value)
aggregate(dat$value, by=list(Group=dat$group), FUN=sum)

然后使用 dplyr

dat %>%
    group_by(group) %>%
    summarise(result = sum(value))

这些会给你

group | result
---------------
  1   |  4140
  2   |  35663
  3   |  561

答案 1 :(得分:1)

这里应该使用的一种方法是按组ID拆分data.frame,然后使用累积组运行for循环(或lapply)。下面是使用for循环的示例,因为我认为它将更直接实现。

# split data.frame by group ID
myList <- split(df, df$group)
# initialize empty output list
myOutputList <- list()

# loop through group IDs, including the next one
for(i in seq_along(unique(df$group))) {
  # create temporary df for analysis
  myTempDf <- do.call(rbind, myList[seq_len(i)])

  ## perform analysis on myTempDf here ##

  # save results
  myOutputList[[i]] <- list(<list of analysis ouput>)
}

输出将是嵌套列表。我建议命名嵌套列表中的每个项目,以便更轻松地访问,例如myOutputList[[i]][["regression.1"]]

请注意,这假设组在原始data.frame中正确排序正确,并且组ID是计数数字1,2,3,4,...在您的示例中。

答案 2 :(得分:1)

以下是几种方法:

1)sqldf 这是从评论转移的。我最初把它放在那里,因为它不是一个dplyr解决方案,但似乎你正在考虑其他人。我们将指定条件下的数据框加入唯一的组值。单个SQL语句将执行此操作:

DF <- data.frame(group = c(1, 1, 2, 2), value = 1:4) # test data

library(sqldf)
outDF <- sqldf("select a.[group], b.value 
                from 
                     (select distinct [group] from DF) a 
                     join DF b on a.[group] >= b.[group]")

,并提供:

> outDF
  group value
1     1     1
2     1     2
3     2     1
4     2     2
5     2     3
6     2     4

现在我们可以处理这些群组了。取决于fun看起来如何,其中一个可能会这样做:

aggregate(value ~ group, outDF, fun)

tapply(outDF$value, outDF$group, fun)

by(outDF, outDF$group, fun)

ave(outDF$value, outDF$group, FUN = fun)

如果操作是总和,比如说,而不是单独的聚合,它可以与上面这样组合。

sqldf("select a.[group], sum(b.value) cumsum
       from (select distinct [group] from DF) a join DF b on a.[group] >= b.[group] 
       group by a.[group]")

,并提供:

  group cumsum
1     1      3
2     2     10

请注意

  • group是一个SQL关键字,这就是我们使用[group]

  • 对其进行转义的原因
  • 我们假设希望累积在数值上等于或小于当前组的组,这是问题中的示例中的情况。如果需要不同的顺序,我们可以创建另一个分组变量,其顺序反映了所需的顺序。

2)base 这不使用任何包。我们假设希望在分割中累积出现在它之前的当前组和组,以便按数字顺序累积组;但是,如果我们想要一个不同的顺序,我们可以将group变成一个因子并根据需要对这些级别进行排序,因为split输出将按照分组因子的级别顺序排列。

L <- Reduce(rbind, split(DF, DF$group), acc = TRUE)
do.call("rbind", lapply(L, transform, group = tail(group, 1)))

,并提供:

  group value
1     1     1
2     1     2
3     2     1
4     2     2
5     2     3
6     2     4

3)magrittr (2)可以使用magrittr这样重写:

library(magrittr)

DF %>%
  split(.$group) %>%
  Reduce(f = rbind, acc = TRUE) %>%
  lapply(transform, group = tail(group, 1)) %>%
  do.call(what = "rbind")

给出与(2)中相同的结果。