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