2013-05-03 1 views
15

Я задаюсь вопросом о простой задаче разделения вектора на два в определенный индекс:R разделить числовой вектор на позиции

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 

Мой вопрос: Там должны быть некоторые существующие функции для этого, но я могу» Найти? Возможно, есть split? Моя наивная реализация также не работает, если pos=0 или pos>length(a).

ответ

23

Улучшение будет:

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

который теперь может взять вектор позиций:

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

И он ведет себя правильно (субъективно), если pos <= 0 или pos >= length(x) в том смысле, что он возвращает весь исходный вектор в одном элементе списка. Если вы хотите, чтобы вместо этого вышла ошибка, используйте stopifnot в верхней части функции.

+0

Спасибо, это отлично работает для меня! Я по-прежнему удивлен, что функция «splitAt» реализована в базе R ... – user1981275

+0

Эта функция очень медленная с очень большим 'x', возможно, из-за' seq_along (x) ', который создает очень длинный вектор, а затем '% in%', который должен соответствовать этому очень длинному вектору. – Calimo

+0

@Calimo: нет, если вы просматриваете его, вы увидите, что большую часть времени тратится на медленный 'split'. Вы можете избежать этого, но вы потеряете много с точки зрения удобочитаемости и компактности кода. – flodel

4

Я пытался использовать flodel's answer, но это было слишком медленно в моем случае с очень большим x (и функция должна вызываться повторно). Поэтому я создал следующую функцию, которая намного быстрее, но также очень уродлива и не ведет себя правильно. В частности, он ничего не проверяет и вернет багги результаты, по крайней мере, для pos >= length(x) или pos <= 0 (вы можете добавить эти проверки самостоятельно, если вы не уверены в своих источниках и не слишком обеспокоены скоростью), и, возможно, и в некоторых других случаях , так что будь осторожен.

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

Однако splitAt2 работает примерно в 20 раз быстрее, с й длиной 10 :

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

Спасибо! Также с простым примером сверху, 'splitAt2' работает лучше. – user1981275

+2

+1 - довольно красивая переписка может быть: 'function (x, pos) {pos <- c (1L, pos, length (x) + 1L); Карта (функция (x, i, j) x [i: j], list (x), head (pos, -1L), tail (pos, -1L) - 1L)} '. Это также кажется немного быстрее по мере увеличения количества расколов, а не уверен, почему. – flodel

+0

@ user1981275 определить «лучше». Если лучше = быстрее, я согласен, но в качестве универсальной функции важна надежность, и в этом случае версия flodel лучше. – Calimo

1

Другой альтернативой, которая может быть быстрее и/или более удобными для чтения/элегантного, чем flodel's solution:

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