2013-05-03 5 views
15

Ich frage mich, über die einfache Aufgabe einen Vektors in zwei an einem bestimmten Index Aufspalten:R geteilt numerischen Vektor an Position

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 

Meine Frage: Es muss eine bestehende Funktion für das sein, aber ich kann‘ t finden Sie es? Ist vielleicht split eine Möglichkeit? Meine naive Implementierung funktioniert auch nicht, wenn pos=0 oder pos>length(a).

Antwort

23

eine Verbesserung wäre:

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

, die einen Vektor der Positionen jetzt annehmen:

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

Und es richtig (subjektiv), wenn pos <= 0 oder pos >= length(x) im Sinne verhält sich, dass Es gibt den gesamten ursprünglichen Vektor in einem einzelnen Listenelement zurück. Wenn Sie möchten, dass stattdessen ein Fehler auftritt, verwenden Sie stopifnot oben in der Funktion.

+0

Danke, das funktioniert gut für mich! Ich bin immer noch überrascht, dass es keine 'splitAt'-Funktion in der Basis R ... implementiert – user1981275

+0

Diese Funktion ist sehr langsam mit sehr großen' x', wahrscheinlich aufgrund der 'seq_along (x)', die einen sehr langen Vektor erstellt und dann die% in%, die mit diesem sehr langen Vektor übereinstimmen muss. – Calimo

+0

@Calimo: Nein, wenn du es profilierst, wirst du sehen, dass die meiste Zeit im langsamen Split verbracht wird. Sie können es sicherlich vermeiden, aber Sie werden viel in Bezug auf Lesbarkeit und Code-Kompaktheit verlieren. – flodel

4

Ich habe versucht, flodel's answer zu verwenden, aber es war zu langsam in meinem Fall mit einem sehr großen x (und die Funktion muss wiederholt aufgerufen werden). Also habe ich die folgende Funktion erstellt, die viel schneller, aber auch sehr hässlich ist und sich nicht richtig verhält. Insbesondere überprüft es nichts und gibt fehlerhafte Ergebnisse mindestens für pos >= length(x) oder pos <= 0 zurück (Sie können diese Überprüfungen selbst hinzufügen, wenn Sie sich über Ihre Eingaben unsicher sind und sich nicht zu sehr mit der Geschwindigkeit befassen) und vielleicht auch einige andere Fälle , also sei vorsichtig.

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

jedoch splitAt2 läuft etwa 20-mal schneller mit einem x der Länge 10 :

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

Danke! Auch mit dem einfachen Beispiel von oben schneidet 'splitAt2' besser ab. – user1981275

+2

+1 - ein etwas hübsches Neuschreiben könnte sein: 'Funktion (x, pos) {pos <- c (1L, pos, Länge (x) + 1L); Karte (Funktion (x, i, j) x [i: j], Liste (x), Kopf (pos, -1L), Schwanz (pos, -1L) - 1L)} '. Es scheint auch ein bisschen schneller als die Anzahl der Splits steigt, nicht sicher warum. – flodel

+0

@ user1981275 definieren "besser". Wenn besser = schneller stimme ich zu, aber als allgemeine Zweckfunktion ist Robustheit der Schlüssel, in welchem ​​Fall flodels Version besser ist. – Calimo

1

Eine weitere Alternative, die eine schnellere und/oder lesbarer/eleganter als flodel's solution könnte sein:

splitAt <- function(x, pos) { 
    unname(split(x, findInterval(x, pos))) 
} 
Verwandte Themen