r

Cómo comprobar si un vector contiene n números consecutivos



(5)

Supongamos que mis números vectoriales contienen c (1,2,3,5,7,8), y deseo encontrar si contiene 3 números consecutivos, que en este caso son 1,2,3.

numbers = c(1,2,3,5,7,8) difference = diff(numbers) //The difference output would be 1,1,2,2,1

Para verificar que hay 3 enteros consecutivos en mi vector de números, he intentado lo siguiente con poca recompensa.

rep(1,2)%in%difference

El código anterior funciona en este caso, pero si mi vector de diferencia = (1,2,2,2,1), todavía devolverá VERDADERO aunque los "1" no sean consecutivos.


Puntos de referencia!

Estoy incluyendo un par de funciones mías. Siéntete libre de añadir el tuyo. Para calificar, necesita escribir una función general que indique si un vector x contiene más números consecutivos. Proporciono una función de prueba de unidad a continuación.

Los contendientes:

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

Pruebas unitarias:

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)

Puntos de referencia:

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


Después de la diff puede comprobar si any 1 s consecutivos -

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


Es bueno ver soluciones caseras aquí.

El usuario Carl seqle() Fellow publicó una función que llamó seqle() y la compartió here .

La función se ve así:

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

Vamos a verlo en acción. Primero, algunos datos:

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)

Ahora, la salida:

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 #

De particular interés para usted son las "longitudes" en el resultado.

Otro punto interesante es el argumento incr . Aquí podemos establecer el incremento en, digamos, "2" y buscar secuencias donde la diferencia entre los números sea dos. Entonces, para el primer vector, esperaríamos que se detectara la secuencia de 3, 5 y 7.

Intentemos:

> seqle(numbers1, incr = 2) $lengths [1] 1 1 3 1 $values [1] 1 2 3 8

Entonces, podemos ver que tenemos una secuencia de 1 (1), 1 (2), 3 (3, 5, 7) y 1 (8) si establecemos incr = 2 .

¿Cómo funciona con el segundo desafío de ECII? ¡Parece bien!

> numbers4 <- c(-2, -1, 0, 5, 7, 8) > seqle(numbers4) $lengths [1] 3 1 2 $values [1] -2 5 7


Simple pero funciona

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

Tenga en cuenta que esto no funcionará para intervalos negativos consecutivos c(-2,-1,0) debido a cómo funciona diff()


Usando diff y rle , algo como esto debería funcionar:

result <- rle(diff(numbers)) any(result$lengths>=2 & result$values==1) # [1] TRUE

En respuesta a los comentarios a continuación, mi respuesta anterior fue específicamente solo para pruebas de length==3 excluyendo longitudes más largas. Cambiar el == a >= arregla esto. También funciona para carreras con números negativos:

> numbers4 <- c(-2, -1, 0, 5, 7, 8) > result <- rle(diff(numbers4)) > any(result$lengths>=2 & result$values==1) [1] TRUE