2011-03-07 12 views
11

I gibi c(1, 3, 4, 5, 9, 10, 17, 29, 30) ve grup için elde edilen düzensiz bir vektörde düzenli, birbirini takip eden bir dizi oluşturmak birlikte 'komşu' elemanları istiyoruz, bir vektör var 3,4 5
L3: 9,10
L4: 17
L5: (ex-Cı programcı) 29,30Bir vektör, düzenli, ardışık sekansların gruplarına nasıl bölümlenir?</p> <p>L1: 1 <br> L2:

Naif kodu:

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) 
} 

Şimdi

a) R sonucunu

ulaşmada bir korkunç verimsiz yoludur süslü parantez rağmen C()
b) küresel değişkenler saf kötülük
c vardır) olmadığını biliyoruz, bu yüzden daha iyi çözümleridir Hoşgeldiniz.

cevap

3

Kolayca kesme noktaları tanımlayabiliriz:

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]] 

sonuçlanır: bu denemede dayanarak

which(diff(v) != 1) 

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

[[2]] 
[1] 3 4 5 

[[3]] 
[1] 9 10 

[[4]] 
[1] 17 

[[5]] 
[1] 29 30 

Bu algoritma güzel biri değil ama diff'a göre çok daha iyi bir kod yazabilirsin :) İyi şanslar!

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

DÜZENLEME (katma zamanlamaları):

Tommy Bu pile keşfetti

9

daroczig

İşte bir yol ... "Sen diff dayalı çok neater kodu yazabiliriz" yazıyor türlere dikkat ederek daha hızlı olmak; Daha hızlı olmasının nedeni, split'un tamsayılarda daha hızlı olması ve aslında daha hızlı olan faktörler olmasıdır.

İşte Joshua'nın çözümü; cumsum sonucu, ile c 'd olduğu için sayısal bir sayıdır, bu yüzden en yavaş olanıdır. 1L ile ing

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

Sadece c böylece sonuç bir tamsayı ölçüde hızlandırır olduğunu.

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

Bu, Tommy'nin referansıdır; ayrıca bir tam sayıya bölünüyor.

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

İşte benim özgün çözümüm; aynı zamanda bir tam sayıya bölünüyor.

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

İşte split önce bir tam sayıya dönüştürülür sonuçla, Joshua var.bir tamsayıdır vektör üzerindeki split yaklaşık olarak aynı

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 

tüm versiyonları; Eğer tamsayı vektörü faktörü zaten bir faktör olsaydı daha hızlı olabilirdi, çünkü tamsayıdan faktüre dönüşüm aslında yaklaşık yarım zaman alır. İşte bunu doğrudan bir faktör haline getiriyorum; Bu genel olarak tavsiye edilmez, çünkü faktör sınıfının yapısına bağlıdır. Sadece karşılaştırma amacıyla burada yapıldı.

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 
+0

Evet, bu çok daha lezzetli bir yoldur! :) 'split' hakkında bilmiyordum, dikkatimi bu yararlı işleve işaret ettiğin için teşekkürler. – daroczig

+0

"As.integer" kullanırken, kesikli değeri döndürdüğünde dikkatli olmanız gerekir, sayısal sayı kayan nokta aritmetiği ile oluşturulduğunda istediğiniz gibi olmayabilir, örneğin, "as.integer (0.3 *) 3 + 0.1) '' 0' döndürür. – Aaron

13

bazı R deyimler ağır yararlanarak: Sen diff, ifelse ve cumsum kullanarak gruplara unsurları bir data.frame oluşturmak ve atamak

> 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 
4

ardından tapply kullanılarak toplanır:

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

Joshua ve Aaron yerinde idi. Ancak, kodları hala doğru tiplerin, tamsayıların ve mantıksalların dikkatli bir şekilde kullanılmasıyla iki katından daha hızlı yapılabilir:

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 
+0

Vay! Böyle bir fark yaratacağını tahmin edemezdim. – Aaron

+0

Tommy, Mesajınızın neden daha hızlı olduğunu ve yayınınızı düzenlediğini anladım. Uygun görgü kurallarından biri olup olmadığından emin değilim; umarım sakıncası yoktur. (Ayrıca, hakemli olarak değerlendirilmelidir, bu yüzden hemen görmüyorsanız, bu yüzden.) – Aaron

+0

Görünüşe göre düzenlemem reddedildi; Zamanlamaları cevabıma ekledim. – Aaron

İlgili konular