18

次のようなベクトルがありc(1, 3, 4, 5, 9, 10, 17, 29, 30)、規則的な連続シーケンスを形成する「隣接する」要素をグループ化したいと思います。つまり、1ずつ増加し、次のような不規則なベクトルになります。

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ではない(中括弧にもかかわらず)
b)グローバル変数は純粋な悪
であるc)結果を達成するためのひどく非効率的な方法であることがわかりました

、したがって、より良い解決策は大歓迎です。

4

5 に答える 5

21

いくつかの 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
于 2011-03-07T16:40:58.787 に答える
12

daroczig は次のように書いていますdiff

1 つの方法を次に示します。

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 

結果が整数になるようにcing するだけで、かなり高速になります。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 
于 2011-03-07T16:40:03.563 に答える
7

ジョシュアとアーロンはぴったりだった。ただし、正しい型、整数、および論理を注意深く使用することで、コードを 2 倍以上高速化することができます。

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 
于 2011-04-08T00:04:48.543 に答える
4

を作成し、、、およびdata.frameを使用して要素をグループに割り当ててから、 :を使用して集計できます。diffifelsecumsumtapply

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
于 2011-03-07T16:47:32.770 に答える
4

カットポイントは簡単に定義できます。

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。頑張ってください!

于 2011-03-07T16:34:12.437 に答える