如何将矢量划分为常规连续序列组?

时间:2011-03-07 16:18:29

标签: r vector data-partitioning

我有一个向量,例如c(1, 3, 4, 5, 9, 10, 17, 29, 30),我希望将一个“相邻”元素组合在一起,这些元素在一个参差不齐的向量中形成一个规则的连续序列,从而产生:

L1:1
L2:3,4,5
L3:9,10
L4:17
L5:29,30

天真代码(前C程序员):

partition.neighbors <- function(v)
{
    result <<- list() #jagged array
    currentList <<- v[1] #current series

    for(i in 2:length(v))
    {
        if(v[i] - v [i-1] == 1)
        {
            currentList <<- c(currentList, v[i])
        }
        else
        {
            result <<- c(result, list(currentList))
            currentList <<- v[i] #next series
        }       
    }

    return(result)  
}

现在我明白了这一点,即R不是C(尽管有大括号)b)全局变量是纯粹的邪恶因为c)这是一种非常低效的方式实现结果

,欢迎任何更好的解决方案。

5 个答案:

答案 0 :(得分:16)

大量使用一些习语:

> split(v, cumsum(c(1, diff(v) != 1)))
$`1`
[1] 1

$`2`
[1] 3 4 5

$`3`
[1]  9 10

$`4`
[1] 17

$`5`
[1] 29 30

答案 1 :(得分:11)

daroczig写道“你可以根据diff编写更多更整洁的代码”......

这是一种方式:

split(v, cumsum(diff(c(-Inf, v)) != 1))

编辑(添加时间):

Tommy发现通过小心类型可以更快;它变得更快的原因是split在整数上更快,并且实际上在因子上更快。

这是约书亚的解决方案; cumsum的结果是一个数字,因为c1一致,所以它是最慢的。

system.time({
a <- cumsum(c(1, diff(v) != 1))
split(v, a)
})
#   user  system elapsed 
#  1.839   0.004   1.848 

只需c 1L所以结果是一个整数可以大大提高它。

system.time({
a <- cumsum(c(1L, diff(v) != 1))
split(v, a)
})
#   user  system elapsed 
#  0.744   0.000   0.746 

这是汤米的解决方案,供参考;它也在整数上分裂。

> system.time({
a <- cumsum(c(TRUE, diff(v) != 1L))
split(v, a)
})
#   user  system elapsed 
#  0.742   0.000   0.746 

这是我最初的解决方案;它也是分裂整数。

system.time({
a <- cumsum(diff(c(-Inf, v)) != 1)
split(v, a)
})
#   user  system elapsed 
#  0.750   0.000   0.754 

这是Joshua的,结果在split之前转换为整数。

system.time({
a <- cumsum(c(1, diff(v) != 1))
a <- as.integer(a)
split(v, a)
})
#   user  system elapsed 
#  0.736   0.002   0.740 

整数向量上split的所有版本大致相同;如果整数向量已经是一个因子,它可能会更快,因为从整数到因子的转换实际上需要大约一半的时间。在这里,我直接将它变成一个因素;一般不推荐这样做,因为它取决于因子类的结构。这里只是为了比较目的。

system.time({
a <- cumsum(c(1L, diff(v) != 1))
a <- structure(a, class = "factor", levels = 1L:a[length(a)])
split(v,a)
})
#   user  system elapsed 
#  0.356   0.000   0.357 

答案 2 :(得分:7)

约书亚和亚伦当场了。但是,通过仔细使用正确的类型,整数和逻辑,它们的代码仍然可以快两倍以上:

split(v, cumsum(c(TRUE, diff(v) != 1L)))

v <- rep(c(1:5, 19), len = 1e6) # Huge vector...
system.time( split(v, cumsum(c(1, diff(v) != 1))) ) # Joshua's code
# user  system elapsed 
#   2.64    0.00    2.64 

system.time( split(v, cumsum(c(TRUE, diff(v) != 1L))) ) # Modified code
# user  system elapsed 
# 1.09    0.00    1.12 

答案 3 :(得分:4)

您可以使用data.framediffifelse创建cumsum并将元素分配给群组,然后使用tapply进行汇总:

v.df <- data.frame(v = v)
v.df$group <- cumsum(ifelse(c(1, diff(v) - 1), 1, 0))
tapply(v.df$v, v.df$group, function(x) x)

$`1`
[1] 1

$`2`
[1] 3 4 5

$`3`
[1]  9 10

$`4`
[1] 17

$`5`
[1] 29 30

答案 4 :(得分:3)

您可以轻松定义切割点:

which(diff(v) != 1)

基于该尝试:

v <- c(1,3,4,5,9,10,17,29,30)
cutpoints <- c(0, which(diff(v) != 1), length(v))
ragged.vector <- vector("list", length(cutpoints)-1)
for (i in 2:length(cutpoints)) ragged.vector[[i-1]] <- v[(cutpoints[i-1]+1):cutpoints[i]]

结果是:

> ragged.vector
[[1]]
[1] 1

[[2]]
[1] 3 4 5

[[3]]
[1]  9 10

[[4]]
[1] 17

[[5]]
[1] 29 30

这个算法不是很好,但你可以根据diff编写很多更整洁的代码:)祝你好运!