У меня есть:
vec1 <- c(0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1)
vec2 <- c(1, 1)
Я ожидаю:
magicFUN(x = vec1, y = vec2)
[1] 4 7 8
Это означает, что мне нужно положение полного вектора внутри другого вектора. match и is.element бесполезны, потому что они возвращают позицию каждого элемента vec2, и мне нужно, чтобы magicFUN соответствовал полному vec2 в vec1.
@Calum Да, я бы предпочел числовой вывод.
@ WenchengLau-Medrano Хороший вопрос. У тебя так много оставленных вопросов. Вы можете принять ответы на эти вопросы, чтобы они были полезны будущим пользователям. Спасибо большое.
связанные с stackoverflow.com/questions/48660606/…





Вот один способ, но он не будет хорошо масштабироваться, если длина vec2 увеличится:
which(head(vec1, -1) == vec2[1] & tail(vec1, -1) == vec2[2])
# [1] 4 7 8
Редактировать: Более общее решение.
magicFUN <- function(vec1, vec2){
l1 <- length(vec1)
l2 <- length(vec2)
which(colSums(sapply(1:(l1-l2), function(i) vec1[i:(i+l2-1)]) == vec2) == l2)
}
magicFUN(vec1, vec2)
# [1] 4 7 8
Спасибо, но, конечно, пример, который я привел, предназначен только для быстрого объяснения моей проблемы. В моих реальных данных vec1 - это вектор длины 10k, а vec2 - вектор переменной длины (2-10).
Общее решение:
magicFUN <- function(vec1, vec2) {
if (length(vec2) > length(vec1)) stop("vec 2 should be shorter")
len <- length(vec1) - length(vec2) + 1
out <- vector(mode = "logical", length=len)
for(i in 1:len) {
out[i] <- identical(vec2, vec1[i:(i+length(vec2)-1)])
}
return(which(out))
}
vec1 <- c(0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1)
vec2 <- c(1, 1)
magicFUN(vec1, vec2)
[1] 4 7 8
Цикл for будет самым быстрым решением (помимо использования Rcpp). См. Тесты ниже:
magicFUN <- function(vec1, vec2) {
if (length(vec2) > length(vec1)) stop("vec 2 should be shorter")
len <- length(vec1) - length(vec2) + 1
out <- vector(mode = "logical", length=len)
for(i in 1:len) {
out[i] <- identical(vec2, vec1[i:(i+length(vec2)-1)])
}
return(which(out))
}
magicFUN2 <- function(vec1, vec2){
l1 <- length(vec1)
l2 <- length(vec2)
which(colSums(sapply(1:(l1-l2), function(i) vec1[i:(i+l2-1)]) == vec2) == l2)
}
magicFUN3 <- function(vec1, vec2){
which(c(zoo::rollapply(vec1, width=length(vec2),
function(x)all(x==vec2), align = "left"),rep(FALSE,length(vec2)-1))==TRUE)
}
library(microbenchmark)
microbenchmark(magicFUN(vec1, vec2), magicFUN2(vec1, vec2), magicFUN3(vec1, vec2))
Unit: milliseconds
expr min lq mean median uq max neval cld
magicFUN(vec1, vec2) 6.083572 6.575844 7.292443 6.878016 7.421208 13.35746 100 a
magicFUN2(vec1, vec2) 8.289640 8.976736 11.007967 9.338644 9.951492 139.68886 100 a
magicFUN3(vec1, vec2) 39.131268 42.369479 46.303722 44.203563 45.053252 172.46151 100 b
Очевидно, что ответ на основе for-loop будет быстрее :-)
Вы жмете на векторах длины 3/10? Думаю, это будет быстро, даже если запустить в Excel
Векторов длины 10k / 5.
Можно использовать пакет zoo как:
library(zoo)
which(c(rollapply(vec1, width=2, function(x)all(x==vec2), align = "left"),0)==TRUE)
[1] 4 7 8
Отредактировано: На основе обратной связи от @G.Grothendieck:
Вышеупомянутое решение можно хорошо масштабировать с помощью length(vec2). Давайте создадим magicFUN как:
magicFUN <- function(vec1, vec2){
which(rollapply(vec1, length(vec2), identical, vec2, align = "left"))
}
magicFUN(vec1, vec2)
#[1] 4 7 8
Обратите внимание, что мы можем написать: which(rollapply(vec1, length(vec2), identical, vec2, align = "left")).
@ G.Grothendieck Очень верное замечание. На самом деле, я изменил свой код, чтобы использовать эту альтернативу, пытаясь повысить производительность, но это не сильно улучшилось. Следовательно, я не думал об обновлении. Хорошо, что вы сегодня отметили. Я обновлю свой ответ сейчас. Спасибо.
Общее решение, где a - более длинный вектор, а b - более короткий.
magicFun <- function(a,b){
la <- length(a)
lb <- length(b)
out <- vector(mode = "numeric", length = la-1)
for(i in 1:(la-lb)) {
out[i] <- ifelse(all(a[i:(i+lb-1)] == b),i,0)
}
out <- out[out != 0]
return(out)
}
magicFun(vec1,vec2)
[1] 4 7 8
Вот еще один метод с использованием grep:
vec1 <- c(0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1)
vec2 <- c(1, 1)
magicFUN = function(x, y){
y_len = length(y)
temp_x = do.call(paste, lapply((y_len-1):0, function(lags){
lag(x, lags)[-(0:(y_len-1))]
}))
temp_y = paste(y, collapse = ' ')
return(grep(temp_y, temp_x, fixed = TRUE))
}
magicFUN(vec1, vec2)
# [1] 4 7 8
Вот попытка векторизовать это с помощью пакета data.table. Хотя, если vec2 будет очень длинным, у него потенциально могут быть проблемы с памятью.
library(data.table)
l2 <- length(vec2)
setDT(shift(vec1, 0 : (l2 - 1), type = "lead")
)[, which(rowSums(.SD == vec2[col(.SD)]) == l2)]
## [1] 4 7 8
в базе R:
which(sapply(seq_along(vec1),function(x) identical(vec1[x:(x+1)],vec2)))
# [1] 4 7 8
Должен поддерживать списки и любые типы данных.
вам нужно делать это только с числами? Насколько важна скорость?