Identificación de patrones en dos cadenas en R

Quiero evaluar si ColA contiene una nueva cadena que ColB. Sin embargo, no estoy interesado en ciertos tipos de cadena, por ejemplo, aceite. Me gustaría tener una variable indicadora como sigue:

  ColA                           ColB                       Ind
--------------------------      ------------------------   -----
 coconut+grape+pine              grape+coconut              TRUE
 orange+apple+grape+pine         grape+coconut              TRUE
 grape+pine                      grape+oil                  TRUE
 oil+grape                       grape+apple                FALSE
 grape                           grape+oil                  FALSE
 grape+pine                      grape+orange+pine          FALSE

¿Alguna sugerencia usando R?

¡Muchas gracias!

Pregunta hecha hace 3 años, 4 meses, 28 días - Por codecraftsman32c9


4 Respuestas:

  • Ya que tenemos que dividir las cuerdas, empezaremos con strsplit,

    strsplit(dat$ColA, '+', fixed = TRUE)
    # [[1]]
    # [1] "coconut" "grape"   "pine"   
    # [[2]]
    # [1] "orange" "apple"  "grape"  "pine"  
    # [[3]]
    # [1] "grape" "pine" 
    # [[4]]
    # [1] "oil"   "grape"
    # [[5]]
    # [1] "grape"
    # [[6]]
    # [1] "grape" "pine" 
    

    Desde aquí, queremos determinar qué hay en ColA que no está ColB. Voy a usar Map para correr setdiff en cada conjunto (ColA's [[1]] con ColB's [[1]], etc.

    Map(setdiff, strsplit(dat$ColA, '+', fixed = TRUE), strsplit(dat$ColB, '+', fixed = TRUE))
    # [[1]]
    # [1] "pine"
    # [[2]]
    # [1] "orange" "apple"  "pine"  
    # [[3]]
    # [1] "pine"
    # [[4]]
    # [1] "oil"
    # [[5]]
    # character(0)
    # [[6]]
    # character(0)
    

    Para determinar cuál tiene "nuevas palabras", podemos comprobar por la longitud no cero usando lengths(.) > 0:

    lengths(Map(setdiff, strsplit(dat$ColA, '+', fixed = TRUE), strsplit(dat$ColB, '+', fixed = TRUE))) > 0
    # [1]  TRUE  TRUE  TRUE  TRUE FALSE FALSE
    

    Pero ya que no te importa oilTenemos que eliminar eso también.

    lapply(Map(setdiff, strsplit(dat$ColA, '+', fixed = TRUE), strsplit(dat$ColB, '+', fixed = TRUE)), setdiff, "oil")
    # [[1]]
    # [1] "pine"
    # [[2]]
    # [1] "orange" "apple"  "pine"  
    # [[3]]
    # [1] "pine"
    # [[4]]
    # character(0)
    # [[5]]
    # character(0)
    # [[6]]
    # character(0)
    lengths(lapply(Map(setdiff, strsplit(dat$ColA, '+', fixed = TRUE), strsplit(dat$ColB, '+', fixed = TRUE)),
                   setdiff, "oil")) > 0
    # [1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
    

    @akrun sugirió una variante tidiversa:

    library(dplyr)
    library(purrr)   # map2_lgl
    library(stringr) # str_extract_all
    dat %>%
      mutate(
        new = map2_lgl(
          str_extract_all(ColB, "\\w+"), str_extract_all(ColA, "\\w+"),
          ~ !all(setdiff(.y, "oil") %in% .x)
        )
      )
    #                      ColA              ColB   Ind   new
    # 1      coconut+grape+pine     grape+coconut  TRUE  TRUE
    # 2 orange+apple+grape+pine     grape+coconut  TRUE  TRUE
    # 3              grape+pine         grape+oil  TRUE  TRUE
    # 4               oil+grape       grape+apple FALSE FALSE
    # 5                   grape         grape+oil FALSE FALSE
    # 6              grape+pine grape+orange+pine FALSE FALSE
    

    Datos

    dat <- structure(list(ColA = c("coconut+grape+pine", "orange+apple+grape+pine", "grape+pine", "oil+grape", "grape", "grape+pine"), ColB = c("grape+coconut", "grape+coconut", "grape+oil", "grape+apple", "grape+oil", "grape+orange+pine"), Ind = c(TRUE, TRUE, TRUE, FALSE, FALSE, FALSE)), class = "data.frame", row.names = c(NA, -6L))
    

    Respondida el Dec 18, 2020 a las 16:57 - por coderchampion

    Votos positivos: 0 | Votos negativos: 0

  • Aquí hay una solución similar a r2evans's que llama strsplit sólo una vez con la ayuda de do.call.

    rid <- function(x) x[!x %in% z]  ## helper FUN to get rid of the oil
    
    z <- "oil"
    L <- sapply(unname(dat), strsplit, "\\+")
    dat$ind <- sapply(1:nrow(L), function(x) length(do.call(setdiff, rev(Map(rid, L[x,]))))) > 0
    dat
    #                  V1                      V2   ind
    # 1     grape+coconut      coconut+grape+pine  TRUE
    # 2     grape+coconut orange+apple+grape+pine  TRUE
    # 3         grape+oil              grape+pine  TRUE
    # 4       grape+apple               oil+grape FALSE
    # 5         grape+oil                   grape FALSE
    # 6 grape+orange+pine              grape+pine FALSE
    

    Datos:

    dat <- structure(list(V1 = c("grape+coconut", "grape+coconut", "grape+oil", 
    "grape+apple", "grape+oil", "grape+orange+pine"), V2 = c("coconut+grape+pine", 
    "orange+apple+grape+pine", "grape+pine", "oil+grape", "grape", 
    "grape+pine")), row.names = c(NA, -6L), class = "data.frame")
    

    Respondida el Dec 18, 2020 a las 17:04 - por bytebard66b1

    Votos positivos: 0 | Votos negativos: 0

  • Para lograr esto en R, puedes utilizar la función grepl() para buscar patrones en las cadenas de texto. Para este caso en particular, puedes usar expresiones regulares para buscar si la cadena de ColA contiene una nueva cadena presente en ColB, pero excluyendo ciertos patrones como "aceite".

    Aquí te dejo un código para lograr lo que necesitas:

    R
    

    Crear un dataframe con los datos proporcionados

    df <- data.frame(ColA = c("coconut+grape+pine", "orange+apple+grape+pine", "grape+pine", "oil+grape", "grape", "grape+pine"), ColB = c("grape+coconut", "grape+coconut", "grape+oil", "grape+apple", "grape+oil", "grape+orange+pine"))

    Función para evaluar si la cadena de ColA contiene una nueva cadena que ColB, excluyendo "aceite"

    evaluar_cadenas <- function(cadena_A, cadena_B) { patron_excluir <- "aceite" ifelse(grepl(patron_excluir, cadena_A), FALSE, ifelse(grepl(patron_excluir, cadena_B), FALSE, any(!grepl(paste0(cadena_B, collapse = "|"), cadena_A)))) }

    Aplicar la función a cada fila del dataframe

    df$Ind <- mapply(evaluar_cadenas, df$ColA, df$ColB)

    Mostrar el resultado

    print(df)

    Este código creará una nueva columna "Ind" en el dataframe que contendrá un valor TRUE si ColA contiene una nueva cadena que ColB (excluyendo "aceite") y FALSE en caso contrario.

    Espero que esta solución sea de ayuda. ¡Déjame saber si tienes alguna otra pregunta o si necesitas más ayuda!

    Respondida el Dec 19, 2020 a las 16:56 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • 
    library(stringr)
    
    

    Crear una función personalizada para identificar patrones

    match_pattern <- function(str1, str2, exclude_pattern) {

    Excluir patrones no deseados

    exclude_pattern <- paste0(".", exclude_pattern, ".")

    Verificar si el patrón está presente en str1, excluyendo los patrones no deseados

    str_detect(str1, str_replace_all(str2, exclude_pattern, "")) }

    Crear una variable indicadora

    Ind <- sapply(ColA, match_pattern, ColB, "oil")

    Respondida el Dec 19, 2020 a las 17:05 - por Gemini

    Votos positivos: 0 | Votos negativos: 0