LIKE com true or false

Pessoal,

No R eu tenho umas lista de milhares de nomes associada a um documento. E quero pegar os safados que usam o mesmo número do documento para mais de uma pessoa. Consegui fazer uma tabela com os números de documento que tem mais de uma ocorrência distinta. E filtrei por esses casos. Aí cheguei na lista de milhares de nomes. Entretanto, claramente a maioria dos casos é homônimo ou erro de digitação.

Como faço para, dessa base, pegar só os casos NOT LIKE ou seja, mostrar uma tabela só com os casos em que os nomes distintos não são nada parecidos, como duas pessoas diferentes mesmo usando o mesmo número de documento?

2 Curtidas

Você pode usar alguma métrica de “distância” entre strings, que calcula o quanto uma se diferencia da outra. Uma comumente utilizada é a distância de Levenshtein. A partir da distância você pode calcular algum percentual e definir que acima de X% é considerado outro nome.
Talvez seja interessante fazer algum tratamento nos nomes antes de jogá-los na função, como limpar acentos e outros caracteres, para uniformizar.

Obviamente, existem outras técnicas, mas acredito que essa dê conta da maior parte dos casos.

2 Curtidas

Caraca turicas num conhecia isso…

2 Curtidas

No R essa solução pode ser feita com o stringdist --> https://www.rdocumentation.org/packages/stringdist/versions/0.9.5.5/topics/stringdist

Eu geralmente inspeciono esses casos dessa forma:

# devtools::install_github("CenterForAssessment/randomNames")
library(randomNames)
library(dplyr)
library(stringdist)


# Gera mil nomes aleatórios
nomes_aleatorios <- randomNames(n = 1000, name.order = "first.last") %>% 
  # Para tirar a virgular que o randomNames coloca entre nome e sobrenome
  gsub(", ", " ", .) %>%  
  # coloca tudo em caixa baixa 
  tolower() %>% 
  # remove os acentos
  iconv(to="ASCII//TRANSLIT")


# Uma matriz de distâncias entre os nomes
dist_nomes <- stringdistmatrix(nomes_aleatorios, method = "dl") %>% 
  as.matrix()

# Coloca NA nas diagonais (já que é o nome comparado a si mesmo)
diag(dist_nomes) <- NA

# O index e o  valor do nome mais próximo de cada nome
index_mais_proximo <- apply(dist_nomes, 1, which.min)
valor_mais_proximo <- apply(dist_nomes, 1, min, na.rm = TRUE)

# Gera uma tabela com o nome mais próximo de cada um e o valor e
# coloca uma flag se a distância é abaixo de um limiar
limiar <- 5
nomes_mais_prox <- data.frame(nome_1 = nomes_aleatorios, 
                              nome_2 = nomes_aleatorios[index_mais_proximo], 
                              dist = valor_mais_proximo) %>% 
  arrange(dist) %>% 
  mutate(flag = dist < limiar)

Aqui em uma função:

 ## x: Os nomes que se quer comparar
 ## return_logical: Se TRUE retorna um vetor lógico, 
 ##    se FALSE retorna um data frame com o par 
 ##    mais próximo e o quão próximo está.
 ## rm_accent: Se deve retirar os acentos antes de comparar
 ## ignore.case: Se deve ignorar maiúsculas e minúsculas
 ## treshold: Distância máxima para se considerar o nome
 ##    parecido
 ## sd.method: método para se comparar os nomes
 ## sd.weight: pesos das penalidades (ver ?stringdist)
 not_like <- function(x,
                      return_logical = TRUE,
                      rm_accent = TRUE, 
                      ignore.case = TRUE, 
                      treshold = 1, 
                      sd.method = "dl", 
                      sd.weight = c(d = 1, i = 1, s = 1, t = 1)){
   
   x0 <- x
   if(rm_accent){
     x <- iconv(x, to = "ASCII//TRANSLIT")
   }
   
   if(ignore.case){
     x <- tolower(x)
   }
   
   mtx_dist <- stringdistmatrix(x, 
                                method = sd.method) %>% 
     as.matrix() 
   diag(mtx_dist) <- NA
   min_dist <- apply(mtx_dist, 1, min, na.rm = TRUE)
   
   if(return_logical){
     return(min_dist < treshold)  
   }
   
   min_index <- apply(mtx_dist, 1, which.min)
   
   df <- data.frame(name_1 = x0, 
                    name_2 = x0[min_index],
                    dist = min_dist,
                    not_like = min_dist < treshold) 
 
   return(df)
 }

 # Exemplo
 nomes <- randomNames(n = 1000)
 nomes_sim <- not_like(x = nomes, return_logical = FALSE)
2 Curtidas

Com o OpenRefine você consegue fazer a reconciliação, que é a técnica de se identificarem strings diferentes que se referem à mesma coisa, tais como os erros de digitação nos nomes. A distância de Levenshtein, mencionada pelo @turicas, é uma das opções que você pode escolher para agrupar as strings similares.

3 Curtidas