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

Ответы

Ответ 1

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

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 в верхней части функции.

Ответ 2

Я попытался использовать ответ flodel, но в моем случае он был слишком медленным с очень большим 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 раз быстрее с длиной x 10 6:

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

Ответ 3

Другая альтернатива, которая может быть более быстрой и/или более читаемой/элегантной, чем решение для флопе:

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