Как проверить, содержит ли вектор n последовательных чисел
Предположим, что мои векторные числа содержат c (1,2,3,5,7,8), и я хочу найти, содержит ли он 3 последовательных числа, которые в этом случае равны 1,2,3.
numbers = c(1,2,3,5,7,8)
difference = diff(numbers) //The difference output would be 1,1,2,2,1
Чтобы убедиться, что в моем номере есть 3 последовательных целых числа, я пробовал следующее с небольшим вознаграждением.
rep(1,2)%in%difference
Приведенный выше код работает в этом случае, но если мой разностный вектор = (1,2,2,2,1), он все равно вернет TRUE, даже если "1" не являются последовательными.
Ответы
Ответ 1
Используя diff
и rle
, что-то вроде этого должно работать:
result <- rle(diff(numbers))
any(result$lengths>=2 & result$values==1)
# [1] TRUE
В ответ на приведенные ниже комментарии мой предыдущий ответ был специально проверен только для прогонов length==3
, исключая более длинные длины. Изменение ==
на >=
устраняет это. Он также работает для прогонов с отрицательными номерами:
> numbers4 <- c(-2, -1, 0, 5, 7, 8)
> result <- rle(diff(numbers4))
> any(result$lengths>=2 & result$values==1)
[1] TRUE
Ответ 2
Бенчмарки!
Я включаю в себя пару моих функций. Не стесняйтесь добавлять свои. Чтобы получить квалификацию, вам нужно написать общую функцию, которая сообщает, если вектор x
содержит n
или более последовательные числа. Я предоставляю unit test функцию ниже.
Участники:
flodel.filter <- function(x, n, incr = 1L) {
if (n > length(x)) return(FALSE)
x <- as.integer(x)
is.cons <- tail(x, -1L) == head(x, -1L) + incr
any(filter(is.cons, rep(1L, n-1L), sides = 1, method = "convolution") == n-1L,
na.rm = TRUE)
}
flodel.which <- function(x, n, incr = 1L) {
is.cons <- tail(x, -1L) == head(x, -1L) + incr
any(diff(c(0L, which(!is.cons), length(x))) >= n)
}
thelatemail.rle <- function(x, n, incr = 1L) {
result <- rle(diff(x))
any(result$lengths >= n-1L & result$values == incr)
}
improved.rle <- function(x, n, incr = 1L) {
result <- rle(diff(as.integer(x)) == incr)
any(result$lengths >= n-1L & result$values)
}
carl.seqle <- function(x, n, incr = 1) {
if(!is.numeric(x)) x <- as.numeric(x)
z <- length(x)
y <- x[-1L] != x[-z] + incr
i <- c(which(y | is.na(y)), z)
any(diff(c(0L, i)) >= n)
}
Единичные тесты:
check.fun <- function(fun)
stopifnot(
fun(c(1,2,3), 3),
!fun(c(1,2), 3),
!fun(c(1), 3),
!fun(c(1,1,1,1), 3),
!fun(c(1,1,2,2), 3),
fun(c(1,1,2,3), 3)
)
check.fun(flodel.filter)
check.fun(flodel.which)
check.fun(thelatemail.rle)
check.fun(improved.rle)
check.fun(carl.seqle)
Ориентиры:
x <- sample(1:10, 1000000, replace = TRUE)
library(microbenchmark)
microbenchmark(
flodel.filter(x, 6),
flodel.which(x, 6),
thelatemail.rle(x, 6),
improved.rle(x, 6),
carl.seqle(x, 6),
times = 10)
# Unit: milliseconds
# expr min lq median uq max neval
# flodel.filter(x, 6) 96.03966 102.1383 144.9404 160.9698 177.7937 10
# flodel.which(x, 6) 131.69193 137.7081 140.5211 185.3061 189.1644 10
# thelatemail.rle(x, 6) 347.79586 353.1015 361.5744 378.3878 469.5869 10
# improved.rle(x, 6) 199.35402 200.7455 205.2737 246.9670 252.4958 10
# carl.seqle(x, 6) 213.72756 240.6023 245.2652 254.1725 259.2275 10
Ответ 3
После diff
вы можете проверить any
последовательный 1
-
numbers = c(1,2,3,5,7,8)
difference = diff(numbers) == 1
## [1] TRUE TRUE FALSE FALSE TRUE
## find alteast one consecutive TRUE
any(tail(difference, -1) &
head(difference, -1))
## [1] TRUE
Ответ 4
Здесь приятно видеть домашние решения.
Пользователь Carl Witthoft опубликовал функцию, которую он назвал seqle()
и поделился ею здесь.
Функция выглядит следующим образом:
seqle <- function(x,incr=1) {
if(!is.numeric(x)) x <- as.numeric(x)
n <- length(x)
y <- x[-1L] != x[-n] + incr
i <- c(which(y|is.na(y)),n)
list(lengths = diff(c(0L,i)),
values = x[head(c(0L,i)+1L,-1L)])
}
Посмотрите на него в действии. Во-первых, некоторые данные:
numbers1 <- c(1, 2, 3, 5, 7, 8)
numbers2 <- c(-2, 2, 3, 5, 6, 7, 8)
numbers3 <- c(1, 2, 2, 2, 1, 2, 3)
Теперь вывод:
seqle(numbers1)
# $lengths
# [1] 3 1 2
#
# $values
# [1] 1 5 7
#
seqle(numbers2)
# $lengths
# [1] 1 2 4
#
# $values
# [1] -2 2 5
#
seqle(numbers3)
# $lengths
# [1] 2 1 1 3
#
# $values
# [1] 1 2 2 1
#
Особый интерес для вас представляет "длина" результата.
Другим интересным моментом является аргумент incr
. Здесь мы можем установить приращение, скажем, "2" и искать последовательности, где разница между числами равна двум. Итак, для первого вектора мы ожидаем, что будет обнаружена последовательность 3, 5 и 7.
Попробуйте:
> seqle(numbers1, incr = 2)
$lengths
[1] 1 1 3 1
$values
[1] 1 2 3 8
Итак, мы можем видеть, что мы имеем последовательность 1 (1), 1 (2), 3 (3, 5, 7) и 1 (8), если положить incr = 2
.
Как это работает с второй проблемой ECII? Кажется ОК!
> numbers4 <- c(-2, -1, 0, 5, 7, 8)
> seqle(numbers4)
$lengths
[1] 3 1 2
$values
[1] -2 5 7
Ответ 5
Простой, но работает
numbers = c(-2,2,3,4,5,10,6,7,8)
x1<-c(diff(numbers),0)
x2<-c(0,diff(numbers[-1]),0)
x3<-c(0,diff(numbers[c(-1,-2)]),0,0)
rbind(x1,x2,x3)
colSums(rbind(x1,x2,x3) )==3 #Returns TRUE or FALSE where in the vector the consecutive intervals triplet takes place
[1] FALSE TRUE TRUE FALSE FALSE FALSE TRUE FALSE FALSE
sum(colSums(rbind(x1,x2,x3) )==3) #How many triplets of consecutive intervals occur in the vector
[1] 3
which(colSums(rbind(x1,x2,x3) )==3) #Returns the location of the triplets consecutive integers
[1] 2 3 7
Обратите внимание, что это не будет работать для последовательных отрицательных интервалов c(-2,-1,0)
из-за того, как diff()
работает