2013-05-03 14 views
15

Zastanawiam o prostym zadaniem dzielenie wektora na dwie części w określonym indeksie:R podzielić wektor numerycznej w pozycji

splitAt <- function(x, pos){ 
    list(x[1:pos-1], x[pos:length(x)]) 
} 

a <- c(1, 2, 2, 3) 

> splitAt(a, 4) 
[[1]] 
[1] 1 2 2 

[[2]] 
[1] 3 

Moje pytanie: Tam musi być jakaś istniejąca funkcja dla tego, ale mogę” t go znaleźć? Czy możliwe jest, że split? Moja naiwna implementacja również nie działa, jeśli pos=0 lub pos>length(a).

Odpowiedz

23

Poprawa będzie:

splitAt <- function(x, pos) unname(split(x, cumsum(seq_along(x) %in% pos))) 

który może teraz podjąć wektor pozycji:

splitAt(a, c(2, 4)) 
# [[1]] 
# [1] 1 
# 
# [[2]] 
# [1] 2 2 
# 
# [[3]] 
# [1] 3 

I to nie zachowywać prawidłowo (subiektywna) jeżeli pos <= 0 lub pos >= length(x) w tym sensie, że zwraca cały oryginalny wektor w pojedynczym elemencie listy. Jeśli zamiast tego chcesz, aby błąd się pojawił, użyj funkcji stopifnot u góry funkcji.

+0

Dzięki, to działa dobrze dla mnie! Nadal jestem zaskoczony, że nie ma funkcji "splitAt" zaimplementowanej w base R ... – user1981275

+0

Ta funkcja jest bardzo powolna z bardzo dużym 'x', prawdopodobnie ze względu na' seq_along (x) ', który tworzy bardzo długi wektor, a następnie "% w%", który musi pasować do tego bardzo długiego wektora. – Calimo

+0

@ Calimo: nie, jeśli to zrobisz, zobaczysz, że większość czasu spędza się wewnątrz powolnego "podziału". Z pewnością można tego uniknąć, ale stracisz dużo pod względem czytelności i zwartości kodu. – flodel

4

Próbowałem użyć flodel's answer, ale w moim przypadku było zbyt wolno z bardzo dużym x (a funkcja musi być wielokrotnie wywoływana). Stworzyłem więc następującą funkcję, która jest znacznie szybsza, ale również bardzo brzydka i nie zachowuje się właściwie. W szczególności nie sprawdza niczego i zwróci wyniki błędne co najmniej dla pos >= length(x) lub pos <= 0 (możesz dodać te sprawdzenia samodzielnie, jeśli nie masz pewności co do swoich danych wejściowych i nie przejmujesz się zbytnio szybkością), a może również inne przypadki , więc uważaj.

splitAt2 <- function(x, pos) { 
    out <- list() 
    pos2 <- c(1, pos, length(x)+1) 
    for (i in seq_along(pos2[-1])) { 
     out[[i]] <- x[pos2[i]:(pos2[i+1]-1)] 
    } 
    return(out) 
} 

Jednakże splitAt2 przebiega około 20 razy szybciej z X o długości 10 :

library(microbenchmark) 
W <- rnorm(1e6) 
splits <- cumsum(rep(1e5, 9)) 
tm <- microbenchmark(
        splitAt(W, splits), 
        splitAt2(W, splits), 
        times=10) 
tm 
+0

Dzięki! Również z prostym przykładem z powyższego, 'splitAt2' działa lepiej. – user1981275

+2

+1 - nieco dość przeróbka może brzmieć: 'function (x, pos) {pos <- c (1L, pos, długość (x) + 1L); Mapa (funkcja (x, i, j) x [i: j], lista (x), głowa (pos, -1L), ogon (pos, -1L) - 1L)} '. Wydaje się również nieco szybszy, ponieważ liczba podziałów rośnie, nie wiem dlaczego. – flodel

+0

@ user1981275 Zdefiniuj "lepiej". Jeśli lepiej = szybciej zgadzam się, ale jako funkcja ogólnego przeznaczenia niezawodność jest kluczowa, w takim przypadku wersja flodel jest lepsza. – Calimo

1

Inna alternatywa może być szybszy i/lub bardziej czytelny/eleganckim niż flodel's solution:

splitAt <- function(x, pos) { 
    unname(split(x, findInterval(x, pos))) 
} 
Powiązane problemy