【问题标题】:Perform a cumulative group operations with R and dplyr使用 R 和 dplyr 执行累积组操作
【发布时间】:2016-08-03 14:17:53
【问题描述】:

我正在尝试根据顺序组 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
  : |     : |     :

但是这似乎很乏味且效率低下,因为我的数据将被复制很多次。

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

【问题讨论】:

  • 您的问题不清楚,尤其是因为您以“so that group $i”之类的句子结尾
  • 谢谢弗兰克,看来数学不再渲染了……让我来解决这个问题
  • 这可能是有趣的吗? stackoverflow.com/questions/32529854/…
  • 哦,是的,tex math 从未在此站点上可用,即使它位于 stackexchange 网络的其他地方。解决这个问题有点痛苦,所以人们通常只是在代码块中编写数学。
  • 这可以用SQL在一行中完成; library(sqldf); sqldf("select a.[group], b.value from (select distinct [group] from DF) a join DF b on a.[group] >= b.[group]")

标签: r dplyr


【解决方案1】:

这里有三个例子,一个是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

【讨论】:

  • 这只是一个普通的老分组操作。在此示例中,我希望结果为 4140、39803、40364。在您的示例中,这可以通过按操作获取组的累积总和来完成 - 但此策略在其他情况下不起作用(执行更复杂的处理)。跨度>
  • 所以你想要按组的累积和然后显示在其他组中累积的结果?因为按组的累计总和是 4140、35663 和 561 对吧?
  • 函数是任意的,但处理应该是累积的。也就是说先对第1组的数据执行函数,然后对第1组和第2组,然后对第1组、第2组和第3组,以此类推。
【解决方案2】:

这里应该使用的一种方法是按组 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,...,如您的示例所示。

【讨论】:

  • 这就是我最终(基本上)采用的方法,但我希望有一种更清洁的方法。
  • 还有其他方法,例如使用 Reduce 和累积 = TRUE 构建第二个 data.frames 列表,其中包含您所需的每个分析 data.frames,然后循环通过它进行分析,但是在我看来,它只是移动分析(并占用更多内存)而不是使其更清晰。
【解决方案3】:

这里有几种方法:

1) sqldf 这是从 cmets 传输的。我最初把它放在那里,因为它不是 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) 中相同的结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    • 2015-07-31
    • 1970-01-01
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多