una separar por extraer contar comparar caracteres caracter cadenas cadena r combinations

separar - extraer caracteres de una cadena en r



Crear todas las combinaciones de sustituciĆ³n de letras en cadena (7)

Tengo una cadena "ECET" y me gustaría crear todas las cadenas posibles donde sustituyo una o más letras (todas menos la primera) con "X".

Entonces en este caso mi resultado sería:

> result [1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"

¿Alguna idea sobre cómo abordar el problema?

Esto no es solo crear las posibles combinaciones / permutaciones de "X" sino también cómo combinarlas con la cadena existente.


Algo así como para agregar otra opción usando lógica binaria:

Asumiendo que su cadena siempre tiene 4 caracteres de largo:

input<-"ECET" invec <- strsplit(input,'''')[[1]] sapply(1:7, function(x) { z <- invec z[rev(as.logical(intToBits(x))[1:4])] <- "X" paste0(z,collapse = '''') }) [1] "ECEX" "ECXT" "ECXX" "EXET" "EXEX" "EXXT" "EXXX"

Si la cadena tiene que ser más larga, puede calcular los valores con una potencia de 2, algo como esto debería hacer:

input<-"ECETC" pow <- nchar(input) invec <- strsplit(input,'''')[[1]] sapply(1:(2^(pow-1) - 1), function(x) { z <- invec z[rev(as.logical(intToBits(x))[1:(pow)])] <- "X" paste0(z,collapse = '''') }) [1] "ECETX" "ECEXC" "ECEXX" "ECXTC" "ECXTX" "ECXXC" "ECXXX" "EXETC" "EXETX" "EXEXC" "EXEXX" "EXXTC" "EXXTX" "EXXXC" [15] "EXXXX"

La idea es conocer el número de posibles alteraciones, es un binario de 3 posiciones, por lo que 2 ^ 3 menos 1, ya que no queremos mantener la cadena sin reemplazo: 7

intToBits devuelve el valor binario del entero, para 5:

> intToBits(5) [1] 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

R usa 32 bits por defecto, pero solo queremos un vector lógico que se corresponda con nuestra longitud de cadena, por lo que solo conservamos el valor nominal de la cadena original. Luego convertimos a lógico e invertimos estos 4 valores booleanos, ya que nunca dispararemos el último bit (8 para 4 caracteres) nunca será cierto:

> intToBits(5) [1] 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > tmp<-as.logical(intToBits(5)[1:4]) > tmp [1] TRUE FALSE TRUE FALSE > rev(tmp) [1] FALSE TRUE FALSE TRUE

Para evitar sobrescribir nuestro vector original, lo copiamos en z y luego reemplazamos la posición en z usando este vector lógico.

Para una salida agradable, devolvemos el paste0 con el colapso como nada para recrear una sola cadena y recuperar un vector de caracteres.


Aquí hay una solución base R, pero me parece complicada, con 3 bucles anidados.

replaceChar <- function(x, char = "X"){ n <- nchar(x) res <- NULL for(i in seq_len(n)){ cmb <- combn(n, i) r <- apply(cmb, 2, function(cc){ y <- x for(k in cc) substr(y, k, k) <- char y }) res <- c(res, r) } res } x <- "ECET" replaceChar(x) replaceChar(x, "Y") replaceChar(paste0(x, x))


Aquí hay una solución recursiva:

f <- function(x,pos=2){ if(pos <= nchar(x)) c(f(x,pos+1), f(`substr<-`(x, pos, pos, "X"),pos+1)) else x } f(x)[-1] # [1] "ECEX" "ECXT" "ECXX" "EXET" "EXEX" "EXXT" "EXXX"

O usando expand.grid :

do.call(paste0, expand.grid(c(substr(x,1,1),lapply(strsplit(x,"")[[1]][-1], c, "X"))))[-1] # [1] "EXET" "ECXT" "EXXT" "ECEX" "EXEX" "ECXX" "EXXX"

O usando combn / Reduce / substr<- :

combs <- unlist(lapply(seq(nchar(x)-1),combn, x =seq(nchar(x))[-1],simplify = F),F) sapply(combs, Reduce, f= function(x,y) `substr<-`(x,y,y,"X"), init = x) # [1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"

Segunda solución explicada

pairs0 <- lapply(strsplit(x,"")[[1]][-1], c, "X") # pairs of original letter + "X" pairs1 <- c(substr(x,1,1), pairs0) # including 1st letter (without "X") do.call(paste0, expand.grid(pairs1))[-1] # expand into data.frame and paste


Otra versión con combn, utilizando purrr:

s <- "ECET" f <- function(x,y) {substr(x,y,y) <- "X"; x} g <- function(x) purrr::reduce(x,f,.init=s) unlist(purrr::map(1:(nchar(s)-1), function(x) combn(2:nchar(s),x,g))) #[1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"

o sin purrr:

s <- "ECET" f <- function(x,y) {substr(x,y,y) <- "X"; x} g <- function(x) Reduce(f,x,s) unlist(lapply(1:(nchar(s)-1),function(x) combn(2:nchar(s),x,g)))


Un método vectorizado con indexación booleana:

permX <- function(text, replChar=''X'') { library(gtools) library(stringr) # get TRUE/FALSE permutations for nchar(text) idx <- permutations(2, nchar(text),c(T,F), repeats.allowed = T) # we don''t want the first character to be replaced idx <- idx[1:(nrow(idx)/2),] # split string into single chars chars <- str_split(text,'''') # build data.frame with nrows(df) == nrows(idx) df = t(data.frame(rep(chars, nrow(idx)))) # do replacing df[idx] <- replChar row.names(df) <- c() return(df) } permX(''ECET'') [,1] [,2] [,3] [,4] [1,] "E" "C" "E" "T" [2,] "E" "C" "E" "X" [3,] "E" "C" "X" "T" [4,] "E" "C" "X" "X" [5,] "E" "X" "E" "T" [6,] "E" "X" "E" "X" [7,] "E" "X" "X" "T" [8,] "E" "X" "X" "X"


Una solución más simple

# expand.grid to get all combinations of the input vectors, result in a matrix m <- expand.grid( c(''E''), c(''C'',''X''), c(''E'',''X''), c(''T'',''X'') ) # then, optionally, apply to paste the columns together apply(m, 1, paste0, collapse='''')[-1] [1] "EXET" "ECXT" "EXXT" "ECEX" "EXEX" "ECXX" "EXXX"


Usando el argumento FUN de combn :

a <- "ECET" fun <- function(n, string) { combn(nchar(string), n, function(x) { s <- strsplit(string, '''')[[1]] s[x] <- ''X'' paste(s, collapse = '''') } ) } lapply(seq_len(nchar(a)), fun, string = a)

[[1]] [1] "XCET" "EXET" "ECXT" "ECEX" [[2]] [1] "XXET" "XCXT" "XCEX" "EXXT" "EXEX" "ECXX" [[3]] [1] "XXXT" "XXEX" "XCXX" "EXXX" [[4]] [1] "XXXX"

unlist para obtener un solo vector. Probablemente hay soluciones más rápidas disponibles.

Para dejar tu primer personaje sin cambios:

paste0( substring(a, 1, 1), unlist(lapply(seq_len(nchar(a) - 1), fun, string = substring(a, 2))) )

[1] "EXET" "ECXT" "ECEX" "EXXT" "EXEX" "ECXX" "EXXX"