如何仅在键值相同并且在连续行中键重复时对行的值求和?

5

我有一个如下的数据表结构:

+-------------------+
| id  | key | value |
+-----+-----+-------+
| 1   | A   | 1000  |
| 1   | A   | 2000  |
| 1   | B   | 2001  |
| 1   | A   | 2002  |
| 1   | A   | 2004  |
| 2   | B   | 2002  |
| 2   | C   | 2002  |
+-------------------+

我的目标是按照id和key将值进行求和,但是除了仅仅按照id和key进行分组之外,我只想在连续行的id和key相同的情况下才对值进行求和。

结果应该是:

+-------------------+
| id  | key | value |
+-----+-----+-------+
| 1   | A   | 3000  |
| 1   | B   | 2001  |
| 1   | A   | 4006  |
| 2   | B   | 2002  |
| 2   | C   | 2002  |
+-------------------+

有没有办法实现这个结果?
1个回答

8
我们可以使用data.table中的rleid函数。
我们将“data.frame”转换为“data.table”。从“key”列创建另一个分组列“ind”。按“id”和“ind”进行分组,得到“value”的总和,并获取“key”的第一个元素。由于预期输出不需要“ind”,因此我们可以将其赋值为NULL。
library(data.table)
setDT(df1)[,list(value = sum(value), key=key[1L]),
                    by = .(ind=rleid(key), id)][, ind:=NULL][]
#   id value key
#1:  1  3000   A
#2:  1  2001   B
#3:  1  4006   A
#4:  2  2002   B
#5:  2  2002   C

或者,如@Frank所建议的那样,我们可以在rleid内使用多个列,将其用作分组变量,获取其他变量的第一个元素和'value'的sum,将不需要的列分配给NULL以避免复制。

setDT(df1)[, list(id=id[1L], key=key[1L], value=sum(value)) ,
        by = .(r=rleid(id, key))][, r:= NULL][]
#   id key value
#1:  1   A  3000
#2:  1   B  2001
#3:  1   A  4006
#4:  2   B  2002
#5:  2   C  2002

或者我们可以使用 dplyr。通过比较'key'的相邻元素,我们创建分组变量'ind',并使用summarise获取'value'的sum和'key'的first元素。

library(dplyr)
df1 %>%
     group_by(ind= cumsum(key!=lag(key, default=TRUE)), id) %>%
     summarise(value=sum(value), key=first(key)) %>% 
     ungroup() %>%
     select(-ind)
#  id value key
#1  1  3000   A
#2  1  2001   B
#3  1  4006   A
#4  2  2002   B
#5  2  2002   C

注意:在dplyrdata.table中,我们还可以将“key”列作为分组变量,并删除key=key[1L]key=first(key))
或者我们通过创建“ind”列来transform数据集,并使用base R中的aggregate来获得期望的输出。
df1 <- transform(df1, ind = cumsum(c(TRUE,head(key,-1)!=tail(key,-1))))
aggregate(value~., df1, FUN=sum)[-3]
#  id key value
#1  1   A  3000
#2  1   B  2001
#3  1   A  4006
#4  2   B  2002
#5  2   C  2002

数据

df1 <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 2L, 2L), key = c("A", 
"A", "B", "A", "A", "B", "C"), value = c(1000L, 2000L, 2001L, 
2002L, 2004L, 2002L, 2002L)), .Names = c("id", "key", "value"
), class = "data.frame", row.names = c(NA, -7L))

4
你似乎依赖于按id排序?值得一提的是,rleid可以接受两个参数,解决了这个问题:DT[, .(id=id[1],key=key[1],value=sum(value)), by=.(r=rleid(id,key))][,!"r",with=FALSE] - Frank
@Frank 谢谢。我还没有进行基准测试,希望你的方法是高效的。 - akrun
1
(不是很重要)我没有考虑效率,只是逻辑上的连贯性——将“如果连续行的id和key成对相同”翻译成代码。我不确定它是否应该更有效率。 - Frank

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接