Eu tenho uma coluna chamada “MATCH” em um dataframe e uma lista de padrões chamados “PATTERN”.

df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc") 

Quero usar o grepl para comparar a coluna MATCH com PATTERN, se verdadeiro, aplicarei minhas funções. O resultado desejado seria “ABC” corresponde a “ABC” e “ABC abc”. Este é o código que usei:

df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ... 

Recebo o erro:

"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used" 

I entenda que não posso usar grepl para uma lista de vetores. Existe alguma maneira de resolver isso?

Comentários

  • Posso usar o filtro (grepl (paste (df1.MATCH, collapse = " | "), df2.PATTERN)) e isso funciona para este exemplo. No entanto, meu dataframe real tem cerca de 1 milhão de linhas e recebo um erro ao usar este código.
  • Na primeira linha do seu exemplo de código, deveria dizer df1.MATCH <- c("ABC", "abc" ,"ABC") em vez de última string sendo "BCD"?

Resposta

TL; DR: grepl espera que seu primeiro argumento seja uma string (comprimento 1), não um vetor. Você pode resolver isso com combinações de sapply e lapply (veja abaixo), mas é melhor usar uma única expressão regular que captu res o que você deseja corresponder em df1.MATCH e não usar df2.PATTERN de forma alguma. Esta segunda opção é muito mais rápida (se menos inteligente) para um grande conjunto de dados. Para este tipo de trabalho, vale a pena aprender a usar as expressões regulares em todo o seu potencial.

df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE)) 

Explicação

A documentação de grepl mostra o seguinte uso:

grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE) 

O argumento pattern é o primeiro, e este argumento deve ser uma string (um elemento). Você está fornecendo df1.MATCH a este argumento, que é um vetor.

Poderíamos usar sapply para aplicar grepl para cada elemento de df1.MATCH.

sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE 

No entanto, observe a saída! Você provavelmente não queria uma matriz. O que acontece quando executamos seu grepl um apenas o primeiro elemento de df1.MATCH?

grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE 

Obtemos um vetor porque grepl está verificando ABC em relação a cada elemento de df2.PATTERN. Para obter um vetor lógico útil para filtragem, você precisa retornar um vetor lógico do mesmo comprimento que df1.MATCH. Vejo duas maneiras de fazer isso.

Método 1: Use any

Como você deseja saber quais elementos em df1.MATCH correspondem a quaisquer elementos em df2.PATTERN, você pode usar any, que retorna TRUE se qualquer elemento em seus argumentos for TRUE. Precisamos de uma sintaxe um pouco diferente para fazer isso funcionar. Precisamos envolver grepl em lapply para fazer uma lista de três vetores (um para cada elemento em df1.MATCH1) que alimenta sapply empacotado any. Se usarmos apenas sapply, any retornará apenas um valor, pois temos uma entrada de matriz.

any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE 

Método 2: Escreva uma expressão regular melhor.

Você deseja comparar o conteúdo de df1.MATCH com valores possíveis que se parecem com abc, ABC , ABC ABC ou ABC abc, etc. Você pode englobar tudo isso em uma única string regex. A string que você deseja é

"^((ABC)( )*)+$" ^ # Nothing else before this (ABC) # Must contain ABC together as a group ( )* # followed by any number of spaces (including 0) ((ABC)( )*)+ # Look for the ABC (space) pattern repeated one or more times $ # And nothing else after it 

Em seguida, use grepl com ignore.case = TRUE:

grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE 

Comparativo

Em um grande conjunto de dados, um desses terá um desempenho mais rápido. Vamos descobrir. Seus resultados de benchmark irão variar de acordo com os recursos de sua máquina.

df1.MATCH <- sample(c("ABC", "abc" ,"BCD"), size = 100000, replace = TRUE) df1 <- data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc") library(rbenchmark) benchmark("any lapply" = { df1 %>% filter(sapply(lapply(df1.MATCH, grepl, x=df2.PATTERN), any) ) }, "better regex" = { df1 %>% filter(grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE)) } ) test replications elapsed relative user.self sys.self user.child sys.child 1 any lapply 100 149.13 70.678 147.67 0.39 NA NA 2 better regex 100 2.11 1.000 2.10 0.02 NA NA 

Parece que o método regex aprimorado é significativamente mais rápido. Isso porque ele está realizando apenas uma operação por linha (grepl) antes da filtragem. O outro método está realizando quatro operações por linha: lapply está executando grepl três vezes (uma para cada elemento de df2.PATTERN e sapply então executa any para cada elemento da lista (cada linha).

Comentários

  • Usar str_detect de package::stringr executa de forma semelhante à abordagem grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *