如何识别“to”和“from”位置未涵盖的所有序号?

问题描述 投票:6回答:3

我有一个数据表,定义了一组sequnces的开始和结束坐标。例如:

df1 <- data.frame(from = c(7, 22, 35, 21, 50),
              to = c(13, 29, 43, 31, 60))

给定开始和结束坐标(即1和100),我试图用相同的输出格式识别序列未涵盖的所有整数。例如:

df2 <- data.frame(from = c(1, 14, 32, 44, 61),
              to = c(6, 20, 34, 49, 100))

这是我当前的尝试,在其中我在df1中对序列进行矢量化,然后识别与序列1:100不匹配的所有整数。

seq2 <- Vectorize(seq.default, vectorize.args = c("from", "to"))
seq <- c(1:100)
df1_int <- unlist(seq2(from = df1$from, to = df1$to))
df1_int <- unique(df1_int)
df2_int <- seq[!seq %in% df1_int]
all(diff(df2_int) == 1)

但是,这个方法对于我想要应用它的数据集来说太慢了(~100,000,000个整数),我不知道如何将矢量df2_int重新格式化为df2格式的数据帧。

任何帮助将不胜感激!

注意:df1中的序列并不总是以最小整数开始(例如,序列可以从13到7,而不是从7到13)。也可能存在仅具有一个整数的序列(例如,从7到7)。

r sequence large-data
3个回答
2
投票

由于您需要快速解决方案,我们可以尝试使用setdiffsplit进行基本R方法。矢量化我们留给mapply。为了找到split的因素,我们使用findInterval。为了获得元素的结果列表的起点和终点,我们用range清除。

d <- setdiff(1:100, unlist(mapply(seq.default, df1[, 1], df1[, 2])))
t(sapply(split(d, findInterval(d, d[which(c(1, diff(d)) > 1)])), range))
#   [,1] [,2]
# 0    1    6
# 1   14   20
# 2   32   34
# 3   44   49
# 4   61  100

基准

从基准测试中我们可以看出,我们已经实现了一个非常快速的解决方案。

Unit: microseconds
         expr      min        lq      mean    median       uq      max neval cld
        purrr 1575.479 1593.2110 1634.3573 1604.9475 1634.033 2028.095   100   b
 findInterval  250.801  256.9245  276.8609  273.3815  281.673  498.285   100  a 

2
投票

编辑:应该更好地阅读问题。这基本上是您目前的做法。

您可以使用pmap函数对seq进行输入,使用unlist来获取所有值的向量。然后setdiff得到缺失值。使用diffcumsum,您可以为缺失值创建分组变量,将它们分组为从 - 到对。然后通过分组var和map对缺失值向量进行拆分,为每个组创建一行输出。

library(purrr)

miss <- setdiff(1:100, unlist(pmap(df1, seq)))
i <- 
  miss %>% 
    diff %>% 
    `>`(1) %>% 
    rev %>%
    cumsum %>% 
    rev 

map_df(split(miss, c(i, 0)), ~list(from = head(.x, 1), to = tail(.x, 1))) %>% 
  dplyr::arrange(from)


# # A tibble: 5 x 2
#    from    to
#   <int> <int>
# 1     1     6
# 2    14    20
# 3    32    34
# 4    44    49
# 5    61   100

1
投票

How to flatten / merge overlapping time periods借用的想法,但在data.table方法:

library(data.table)
setDT(df1)
setorder(df1, from, to)

maxn <- 100L    

#see linked post
df1[, g := c(0, cumsum(shift(from, -1L) > cummax(to))[-.N])]

#get desired output
df1[, .(from=max(to)+1L, to=min(from)-1L), by=.(g)][, 
    .(from=c(1L, from), to=c(to, maxn))]

希望这对于具有100mio整数的实际数据集来说足够快。

© www.soinside.com 2019 - 2024. All rights reserved.