2015-07-18 7 views
5

Ho scoperto l'eccellente pacchetto "stringdist" e ora voglio usarlo per calcolare le distanze tra le stringhe. In particolare, ho un set di parole e voglio stampare le partite ravvicinate, dove "near match" è attraverso un algoritmo come la distanza di Levenshtein.R: produrre un elenco di corrispondenze ravvicinate con stringdist e stringdistmatrix

Ho un codice di lavoro estremamente lento in uno script di shell, e sono stato in grado di caricare in stringdist e produrre una matrice con le metriche. Ora voglio ridurre quella matrice in una matrice più piccola che ha solo le corrispondenze ravvicinate, ad es. dove la metrica è diversa da zero ma inferiore a qualche soglia.

kp <- c('leaflet','leafletr','lego','levenshtein-distance','logo') 
kpm <- stringdistmatrix(kp,useNames="strings",method="lv") 
> kpm 
        leaflet leafletr lego levenshtein-distance 
leafletr     1         
lego      5  6       
levenshtein-distance  16  16 18      
logo      6  7 1     19 
m = as.matrix(kpm) 
close = apply(m, 1, function(x) x>0 & x<5) 
> close 
        leaflet leafletr lego levenshtein-distance logo 
leaflet    FALSE  TRUE FALSE    FALSE FALSE 
leafletr    TRUE FALSE FALSE    FALSE FALSE 
lego     FALSE FALSE FALSE    FALSE TRUE 
levenshtein-distance FALSE FALSE FALSE    FALSE FALSE 
logo     FALSE FALSE TRUE    FALSE FALSE 

OK, ora ho un (grande) dist, come faccio a ridurre di nuovo a una lista in cui l'uscita sarebbe qualcosa di simile

leafletr,leaflet,1 
logo,lego,1 

per i casi solo se la metrica è non- zero e meno di n = 5? Ho trovato "apply()" che mi permette di fare il test, ora ho bisogno di capire come usarlo.

Il problema non è specifico per stringdist e stringdistmatrix ed è molto elementare R, ma comunque sono bloccato. Sospetto che la risposta riguardi il sottoinsieme(), ma non so come trasformare un "dist" in qualcos'altro.

+0

Sarebbe utile se potessi mostrarci 'kpm' o" la tua grande matrice "in modo che sappiamo con cosa stai lavorando. In alternativa, è possibile rendere riproducibile il problema, fornendo alcuni dati fittizi o dati reali 'dput (head (read.table (" ... "), 20))' e includendolo nella domanda. –

+0

Grazie Brandon, lo farò, ridurrò a una matrice 5x5 e includerò tutto il codice. Lavorava con un originale di 100 dimensioni. – vielmetti

risposta

3

si può fare questo:

library(reshape2) 
d <- unique(melt(m)) 
out <- subset(d, value > 0 & value < 5) 

Qui, melt porta m in forma lunga (2 colonne con i nomi di stringa e una colonna con il valore). Tuttavia, poiché abbiamo fuso una matrice simmetrica, usiamo unique per la deduplicazione.

Un altro modo è quello di utilizzare dplyr (dal momento che tutti i bambini freschi utilizzano dplyr con tubi ora):

library(dlpyr) 
library(reshape2) 
library(magrittr) 

out <- melt(m) %>% distinct() %>% filter(value > 0 & value < 5) 

Questa seconda opzione è probabilmente più veloce, ma non ho davvero contati.

+1

Oh, e se si scioglie (m, as.is = TRUE) le etichette di m non vengono convertite in fattori. –

+0

Era sciolto() che non avevo nella mia cassetta degli attrezzi, grazie, questo è buono. – vielmetti

2

impostare i propri dati:

library('stringdist') 
library('dplyr') 
kp <- c('leaflet','leafletr','lego','levenshtein-distance','logo') 
kpm <- stringdistmatrix(kp,useNames="strings",method="lv") 

Ecco dove possiamo cambiare kpm in un dataframe:

kpm <- data.frame(as.matrix(kpm)) 

Questo è un modo per ottenere un dataframe che ha un '1' per segnare dove le parole sono abbastanza vicini:

idx <- apply(kpm, 2, function(x) x >0 & x<5) 
idx <- apply(idx, 1:2, function(x) if(isTRUE(x)) x<-1 else x<-NA) 
#> idx 
#      leaflet leafletr lego levenshtein.distance logo 
# leaflet     NA  1 NA     NA NA 
# leafletr     1  NA NA     NA NA 
# lego      NA  NA NA     NA 1 
# levenshtein-distance  NA  NA NA     NA NA 
# logo      NA  NA 1     NA NA 

Per rendere le cose facili, sciogliere il dataframe, filtrare e ottenere liberarsi dell'ultima colonna:

final <- melt(idx) %>% 
     filter(value==1) %>% 
     select(Var1, Var2) 

Non dimenticare di trasformare tutto torna in personaggi, non fattori! (E 'come un disco rotto in R a volte ...)

final[] <- lapply(final, as.character) 
#> final 
#  Var1  Var2 
# leafletr leaflet 
# leaflet leafletr 
#  logo  lego 
#  lego  logo 

Ora ci liberiamo dei duplicati:

final <- final[!duplicated(data.frame(list(do.call(pmin,final),do.call(pmax,final)))),] 

Tack su alcuni nomi buoni e vi sono buone per andare.

names(final) <- c('string 1', 'string 2') 
#> final 
# string 1 string 2 
# leafletr leaflet 
#  logo  lego 

(Anche se è richiesto un elenco, questo è un dataframe. Da qui è abbastanza facile da convertire in quello che vuoi a seconda delle necessità, ad esempio, scrivere su un csv, ecc ecc)