如何使用 R 有效地将整数向量折叠成 data.table 序列?
How to efficiently collapse a vector of integers into a data.table of sequences, using R?
给定一个大向量。例如:
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
如何有效地将其折叠成一个数据表,为所有连续整数提供开始和结束坐标。我目前正在使用以下代码:
in_vec <- sort(in_vec) # sort by sequence
library(data.table)
interval_id <- findInterval(in_vec, in_vec[which(c(1, diff(in_vec)) > 1)]) # add unique IDs for sequences
dt <- data.table(vec = in_vec, # make data.table
int_id = interval_id)
long_to_short <- function(sub){ data.table(start = sub$vec[1], end = sub$vec[nrow(sub)]) } # custom function
library(plyr)
output <- ddply(dt, "int_id", long_to_short)
output$int_id <- NULL
但是,我应用它的向量非常大,因此我需要最大化性能。有data.table方法吗?任何帮助将不胜感激!
是这样的吗?
dt[, .(start = first(vec), end = last(vec)), int_id]
编辑:我认为以下将在 data.table 内完成您需要的操作,根据您的值的实际范围调整 fill = -1
。
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
dt <- data.table(vec = in_vec, key = 'vec')
dt[, int_id := cumsum(!shift(vec, 1, fill = -1) == vec - 1)]
dt[,.(start = first(vec), end = last(vec)), int_id]
你快完成了,只需要使用排序向量之间的差异来创建一个组。然后对它们进行射程。
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
in_vec <- sort(in_vec)
grps <- cumsum(c(1,diff(in_vec)>1))
output <- data.frame(do.call(rbind,tapply(in_vec,grps,range)))
names(output) <- c("start","end")
还有一个 dplyr 解决方案
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
data.frame(x=in_vec) %>%
arrange(x) %>%
mutate(grps=cumsum(c(1,diff(x)>1))) %>%
group_by(grps) %>%
summarise(start=min(x),end=max(x)) %>%
select(start,end)
使用 data.table 中的 rleid()
很有帮助:
library(data.table)
set.seed(1)
dt <- data.table(in_vec = sample(1:10000, 5000, replace = F))
dt[order(in_vec),
.(start = min(in_vec),
end = max(in_vec)),
by = .(grp = rleid(c(0, cumsum(diff(in_vec) > 1))))
]
grp start end
1: 1 4 4
2: 2 6 7
3: 3 14 16
4: 4 19 19
5: 5 26 27
---
2483: 2483 9980 9980
2484: 2484 9988 9988
2485: 2485 9991 9992
2486: 2486 9994 9994
2487: 2487 9997 9998
对于完全基本的解决方案,这应该是性能最高的,因为它不是分组操作:
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
in_vec <- sort(in_vec)
grp <- c(0, cumsum(diff(in_vec) > 1))
data.frame(grp = unique(grp),
start = in_vec[!duplicated(grp)],
end = in_vec[!duplicated(grp, fromLast = T)]
)
给定一个大向量。例如:
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
如何有效地将其折叠成一个数据表,为所有连续整数提供开始和结束坐标。我目前正在使用以下代码:
in_vec <- sort(in_vec) # sort by sequence
library(data.table)
interval_id <- findInterval(in_vec, in_vec[which(c(1, diff(in_vec)) > 1)]) # add unique IDs for sequences
dt <- data.table(vec = in_vec, # make data.table
int_id = interval_id)
long_to_short <- function(sub){ data.table(start = sub$vec[1], end = sub$vec[nrow(sub)]) } # custom function
library(plyr)
output <- ddply(dt, "int_id", long_to_short)
output$int_id <- NULL
但是,我应用它的向量非常大,因此我需要最大化性能。有data.table方法吗?任何帮助将不胜感激!
是这样的吗?
dt[, .(start = first(vec), end = last(vec)), int_id]
编辑:我认为以下将在 data.table 内完成您需要的操作,根据您的值的实际范围调整 fill = -1
。
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
dt <- data.table(vec = in_vec, key = 'vec')
dt[, int_id := cumsum(!shift(vec, 1, fill = -1) == vec - 1)]
dt[,.(start = first(vec), end = last(vec)), int_id]
你快完成了,只需要使用排序向量之间的差异来创建一个组。然后对它们进行射程。
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
in_vec <- sort(in_vec)
grps <- cumsum(c(1,diff(in_vec)>1))
output <- data.frame(do.call(rbind,tapply(in_vec,grps,range)))
names(output) <- c("start","end")
还有一个 dplyr 解决方案
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
data.frame(x=in_vec) %>%
arrange(x) %>%
mutate(grps=cumsum(c(1,diff(x)>1))) %>%
group_by(grps) %>%
summarise(start=min(x),end=max(x)) %>%
select(start,end)
使用 data.table 中的 rleid()
很有帮助:
library(data.table)
set.seed(1)
dt <- data.table(in_vec = sample(1:10000, 5000, replace = F))
dt[order(in_vec),
.(start = min(in_vec),
end = max(in_vec)),
by = .(grp = rleid(c(0, cumsum(diff(in_vec) > 1))))
]
grp start end
1: 1 4 4
2: 2 6 7
3: 3 14 16
4: 4 19 19
5: 5 26 27
---
2483: 2483 9980 9980
2484: 2484 9988 9988
2485: 2485 9991 9992
2486: 2486 9994 9994
2487: 2487 9997 9998
对于完全基本的解决方案,这应该是性能最高的,因为它不是分组操作:
set.seed(1)
in_vec <- sample(1:10000, 5000, replace = F)
in_vec <- sort(in_vec)
grp <- c(0, cumsum(diff(in_vec) > 1))
data.frame(grp = unique(grp),
start = in_vec[!duplicated(grp)],
end = in_vec[!duplicated(grp, fromLast = T)]
)