按连接值对 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得到ID
的min
和max
=].
您可以使用
删除 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 中的 rle
和 aggregate
的另一种尝试:
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))
我没有找到 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得到ID
的min
和max
=].
您可以使用
删除 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 中的 rle
和 aggregate
的另一种尝试:
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))