如何将一个向量分成规则的,连续的序列组?

我有一个向量,例如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) } 

现在我明白了

a)R不是C(尽pipe大括号)
b)全局variables是纯粹的邪恶
c)这是实现结果的极其低效的方式

所以任何更好的解决scheme,

大量使用R习语:

 > 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 

daroczig写道:“你可以写很多基于diff整理代码”…

这里有一个方法:

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

编辑(增加计时):

汤米发现,通过小心types可以加快速度。 它得到更快的原因是整数split更快,实际上更快的因素。

这是Joshua的解决scheme 从cumsum的结果是一个数字,因为它与cumsum 1 ,所以这是最慢的。

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

只要使用1L ,结果就是一个整数,大大加快了速度。

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

这是汤米的解决scheme,供参考。 它也分裂在一个整数。

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

这是我的原始解决scheme; 它也是在一个整数分裂。

 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的版本都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 

您可以创build一个data.frame并使用diffifelsecumsum将这些元素分配给组,然后使用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 

您可以轻松定义切点:

 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 

这个algorithm不是一个很好的,但你可以写很多基于diff整洁的代码:)祝你好运!

约书亚和亚伦现身。 但是,通过仔细使用正确的types,整数和逻辑,他们的代码仍然可以快两倍以上:

 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