如何将一个向量分成规则的,连续的序列组?
我有一个向量,例如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
并使用diff
, ifelse
和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
您可以轻松定义切点:
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