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

R: How to sum values from rows only if the key value is the same and also if the key duplicated in consecutive rows?

我有以下结构的数据table:

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

我的 objective 是按 id 和 key 对值求和,但我不只是按 id 和 key 分组,我只想对连续行的 id 和 key 对相同的值求和.

结果应该是:

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

有没有办法达到这个效果?

我们可以使用 data.table 中的 rleid

我们将 'data.frame' 转换为 'data.table'。从 'key' 列创建另一个分组列 'ind'。按'id'和'ind'分组,得到'value'的sum,得到'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',得到'value'的sum和'key'的first元素summarise

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))