按连接值对 R 数据帧进行分组

Grouping of R dataframe by connected values

我没有找到 R 中这个常见分组问题的解决方案:

这是我的原始数据集

ID  State
1   A
2   A
3   B
4   B
5   B
6   A
7   A
8   A
9   C
10  C

这应该是我分组的结果数据集

State   min(ID) max(ID)
A       1       2
B       3       5
A       6       8
C       9       10

所以想法是首先按 ID 列(或时间戳列)对数据集进行排序。然后应将所有没有间隙的连接状态组合在一起,并返回最小和最大 ID 值。它与 rle 方法有关,但这不允许计算组的最小值、最大值。

有什么想法吗?

你可以试试:

library(dplyr)
df %>%
  mutate(rleid = cumsum(State != lag(State, default = ""))) %>%
  group_by(rleid) %>%
  summarise(State = first(State), min = min(ID), max = max(ID)) %>%
  select(-rleid)

或者正如 @alistaire 在评论中提到的那样,您实际上可以在 group_by() 内使用相同的语法进行变异,结合前两个步骤。窃取 data.table::rleid() 并使用 summarise_all() 进行简化:

df %>% 
  group_by(State, rleid = data.table::rleid(State)) %>% 
  summarise_all(funs(min, max)) %>% 
  select(-rleid)

给出:

## A tibble: 4 × 3
#   State   min   max
#  <fctr> <int> <int>
#1      A     1     2
#2      B     3     5
#3      A     6     8
#4      C     9    10

一个想法data.table

require(data.table)

dt <- fread("ID  State
1   A
            2   A
            3   B
            4   B
            5   B
            6   A
            7   A
            8   A
            9   C
            10  C")

dt[,rle := rleid(State)]
dt2<-dt[,list(min=min(ID),max=max(ID)),by=c("rle","State")]

给出:

   rle State min max
1:   1     A   1   2
2:   2     B   3   5
3:   3     A   6   8
4:   4     C   9  10

思路是用rleid识别序列,然后通过元组rle和[=21得到IDminmax =].

您可以使用

删除 rle 列
dt2[,rle:=NULL]

链接:

 dt2<-dt[,list(min=min(ID),max=max(ID)),by=c("rle","State")][,rle:=NULL]

你可以直接在by中使用rleid进一步缩短上面的代码:

dt2 <- dt[, .(min=min(ID),max=max(ID)), by=.(State, rleid(State))][, rleid:=NULL]

这里有一个方法,它使用 base R 中的 rle 函数来处理您提供的数据集。

# get the run length encoding
temp <- rle(df$State)

# construct the data.frame
newDF <- data.frame(State=temp$values,
                    min.ID=c(1, head(cumsum(temp$lengths) + 1, -1)),
                    max.ID=cumsum(temp$lengths))

哪个returns

newDF
  State min.ID max.ID
1     A      1      2
2     B      3      5
3     A      6      8
4     C      9     10

请注意,rle 需要一个字符向量而不是一个因子,因此我在下面使用 as.is 参数。


正如@cryo111 在下面的评论中指出的那样,数据集可能是无序时间戳,与 rle 中计算的长度不对应。要使此方法起作用,您需要先将时间戳转换为日期时间格式,使用 as.POSIXct 之类的函数,使用 df <- df[order(df$ID),],然后对上述方法稍作改动:

# get the run length encoding
temp <- rle(df$State)

# construct the data.frame
newDF <- data.frame(State=temp$values,
                    min.ID=df$ID[c(1, head(cumsum(temp$lengths) + 1, -1))],
                    max.ID=df$ID[cumsum(temp$lengths)])

数据

df <- read.table(header=TRUE, as.is=TRUE, text="ID  State
1   A
2   A
3   B
4   B
5   B
6   A
7   A
8   A
9   C
10  C")

这是使用基础 R 中的 rleaggregate 的另一种尝试:

rl <- rle(df$State)
newdf <- data.frame(ID=df$ID, State=rep(1:length(rl$lengths),rl$lengths))
newdf <- aggregate(ID~State, newdf, FUN = function(x) c(minID=min(x), maxID=max(x)))
newdf$State <- rl$values

  # State ID.minID ID.maxID
# 1     A        1        2
# 2     B        3        5
# 3     A        6        8
# 4     C        9       10

数据

df <- structure(list(ID = 1:10, State = c("A", "A", "B", "B", "B", 
"A", "A", "A", "C", "C")), .Names = c("ID", "State"), class = "data.frame", 
row.names = c(NA, 
    -10L))