valores - buscar y reemplazar secuencia numérica en r
modificar una columna en r (3)
Aquí hay una solución sin lazo que usa rle()
y inverse.rle()
.
data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)
local({
r <- rle(data)
x <- r$values
x0 <- which(x==0) # index positions of zeroes
xt <- x[x0-1]==x[x0+1] # zeroes surrounded by same value
r$values[x0[xt]] <- x[x0[xt]-1] # substitute with surrounding value
inverse.rle(r)
})
[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2
PD. Utilizo local()
como un mecanismo simple para no saturar el espacio de trabajo con montones de nuevos objetos temporales. Podrías crear una function
lugar de usar local
, solo encuentro que actualmente uso mucho local
para este tipo de tarea.
PPS. Deberá modificar este código para excluir ceros iniciales o finales en sus datos originales.
Tengo un marco de datos con una secuencia de números similar a la siguiente:
data <- c(1,1,1,0,0,1,1,2,2,2,0,0,0,2,1,1,0,1,0,2)
Lo que necesito es algo para ubicar todas las instancias de 1, 2 o 3 repeticiones de 0 donde los números de procedimiento y siguientes son idénticos, es decir, ambos 1 o ambos 2 (por ejemplo 1,0,1 o 2,0,0,2 pero NO 2,0,1).
Entonces necesito completar los ceros solo con el valor circundante.
Logré localizar y contar ceros consecutivos
consec <- (!data) * unlist(lapply(rle(data)$lengths, seq_len))
entonces he encontrado la fila donde comienzan estos ceros consecutivos:
consec <- as.matrix(consec)
first_na <- which(consec==1,arr.ind=TRUE)
Pero estoy perplejo con el proceso de reemplazo
¡Realmente apreciaría tu ayuda con esto!
Carl
Dado que parece haber mucho interés en la respuesta a esta pregunta, pensé que escribiría un método alternativo de expresiones regulares para la posteridad.
Usando la función ''gregexpr'', puede buscar patrones y usar las correspondencias de ubicación resultantes y las longitudes de coincidencia para indicar qué valores cambiar en el vector original. La ventaja de utilizar expresiones regulares es que podemos ser explícitos sobre exactamente qué patrones queremos que coincidan, y como resultado, no tendremos ningún caso de exclusión de qué preocuparse.
Nota: El siguiente ejemplo funciona como está escrito, porque asumimos valores de un solo dígito. Podríamos adaptarlo fácilmente para otros patrones, pero podemos tomar un pequeño atajo con caracteres únicos. Si quisiéramos hacer esto con posibles valores de varios dígitos, nos gustaría agregar un carácter de separación como parte de la primera función de concatenación (''pegar'').
El código
str.values <- paste(data, collapse="") # String representation of vector
str.matches <- gregexpr("1[0]{1,3}1", str.values) # Pattern 101/1001/10001
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1 # Replace zeros with ones
str.matches <- gregexpr("2[0]{1,3}2", str.values) # Pattern 202/2002/20002
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2 # Replace zeros with twos
Paso 1 : crea una sola cadena de todos los valores de datos.
str.values <- paste(data, collapse="")
# "11100112220002110102"
Esto colapsará los datos en una cadena larga, por lo que podemos usar una expresión regular en ella.
Paso 2 : aplique una expresión regular para encontrar las ubicaciones y longitudes de cualquier coincidencia dentro de la cadena.
str.matches <- gregexpr("1[0]{1,3}1", str.values)
# [[1]]
# [1] 3 16
# attr(,"match.length")
# [1] 4 3
# attr(,"useBytes")
# [1] TRUE
En este caso, estamos usando una expresión regular para buscar el primer patrón, de uno a tres ceros ( [0]{2,}
) con unos a cada lado ( 1[0]{1,3}1
). Tendremos que coincidir con el patrón completo, para evitar tener que verificar si hay coincidencias o dos en los extremos. Restaremos esos extremos en el siguiente paso.
Paso 3 : Escribe unos en todas las ubicaciones coincidentes en el vector original.
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 1
# 1 1 1 1 1 1 1 2 2 2 0 0 0 2 1 1 1 1 0 2
Estamos haciendo unos pocos pasos aquí todos a la vez. Primero, estamos creando una lista de secuencias de números a partir de los números que coinciden en la expresión regular. En este caso, hay dos coincidencias, que comienzan en los índices 3 y 16 y tienen 4 y 3 elementos de longitud, respectivamente. Esto significa que nuestros ceros están ubicados en los índices (3 + 1) :( 3-2 + 4), o 4: 5 y en (16 + 1) :( 16-2 + 3), o 17:17. Concatenamos (''pegan'') estas secuencias usando nuevamente la opción ''colapsar'', en caso de que haya múltiples coincidencias. Luego, usamos una segunda concatenación para poner las secuencias dentro de una función de combinación ( c()
). Usando las funciones ''eval'' y ''analizar'', convertimos este texto en código y lo pasamos como valores de índice a la matriz [data]. Escribimos todos en esos lugares.
Paso x : repite para cada patrón. En este caso, debemos hacer una segunda búsqueda y encontrar de uno a tres ceros con dos en cada lado y luego ejecutar la misma declaración que en el Paso 3, pero asignando dos, en lugar de unos.
str.matches <- gregexpr("2[0]{1,3}2", str.values)
# [[1]]
# [1] 10
# attr(,"match.length")
# [1] 5
# attr(,"useBytes")
# [1] TRUE
data[eval(parse(text=paste("c(",paste(str.matches[[1]] + 1, str.matches[[1]] - 2 + attr(str.matches[[1]], "match.length"), sep=":", collapse=","), ")")))] <- 2
# 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2
Actualización : me di cuenta de que el problema original decía que coincidía con uno a tres ceros en una fila, en lugar de los "dos o más" que escribí en el código original. He actualizado las expresiones regulares y la explicación, aunque el código sigue siendo el mismo.
Puede haber una solución sin un bucle for
, pero puede intentar esto:
tmp <- rle(data)
val <- tmp$values
for (i in 2:(length(val)-1)) {
if (val[i]==0 & val[i-1]==val[i+1]) val[i] <- val[i-1]
}
tmp$values <- val
inverse.rle(tmp)
Lo que da :
[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 1 1 1 1 0 2