2015-10-13 36 views
9

Sto cercando di trovare le parole che si verificano in più documenti allo stesso tempo.Trattare le parole separate dallo spazio nello stesso modo

Facciamo un esempio.

doc1: "this is a document about milkyway" 
doc2: "milky way is huge" 

Come si può vedere nel precedente 2 documenti, parola "Milkyway" avviene in entrambi i documenti, ma nel secondo termine documento "Milkyway" è separato da uno spazio e in primo doc non lo è.

sto facendo quanto segue per ottenere la matrice termine documento in R.

library(tm) 
tmp.text <- data.frame(rbind(doc1, doc2)) 
tmp.corpus <- Corpus(DataframeSource(tmp.text)) 
tmpDTM <- TermDocumentMatrix(tmp.corpus, control = list(tolower = T, removeNumbers = T, removePunctuation = TRUE,stopwords = TRUE,wordLengths = c(2, Inf))) 
tmp.df <- as.data.frame(as.matrix(tmpDTM)) 
tmp.df 

     1 2 
document 1 0 
huge  0 1 
milky 0 1 
milkyway 1 0 
way  0 1 

Term milkyway è presente solo nella prima doc come per la matrice di cui sopra.

Voglio essere in grado di ottenere 1 in entrambi i documenti per termine "milkyway" nella matrice sopra. Questo è solo un esempio. Ho bisogno di farlo per molti documenti. In definitiva voglio essere in grado di trattare tali parole ("milkyway" & "via lattea") in modo simile.

EDIT 1:

Non posso forzare la matrice termine documento per ottenere calcolato in modo tale che per qualsiasi parola che sta cercando di guardare per esso non deve solo cercare quella parola come un parola separata nella stringa ma anche all'interno delle stringhe? Ad esempio, un termine è milky e c'è un documento this is milkyway, quindi qui attualmente milky non si verifica in questo documento ma se l'algoritmo cerca la parola in questione all'interno di stringhe troverà anche la parola milky all'interno della stringa milkyway, in questo modo parole milky e way verranno conteggiati in entrambi i miei documenti (esempio precedente).

EDIT 2:

In definitiva voglio essere in grado di calcolare la somiglianza indice del coseno tra i documenti.

+0

Forse rimuovere gli spazi quindi utilizzare regex? – zx8754

+0

Hai solo bisogno di farlo per "via lattea" o altri? Preferisci che siano entrambi "milkyway"? –

+0

@ sebastian-c ho bisogno di farlo per più parole. preferisco entrambi diventare "milkyway" in qualche modo. Potrebbero esserci casi come "tutti i giorni" e "tutti i giorni". In questo caso preferirei che fossero "tutti i giorni". – user3664020

risposta

0

È possibile utilizzare regex per corrispondere per ogni possibile suddivisione delle parole, inserendo "\\ s?" tra ogni personaggio nelle tue parole di ricerca. Se vuoi solo divisioni specifiche, basta inserirle in quei luoghi. Il codice seguente genera un modello regex per i termini di ricerca, inserendo "\\ s?" tra ogni personaggio. grep restituisce l'indice per il punto in cui il modello corrisponde, ma può essere scambiato con altre funzioni di regex.

docs <- c("this is a document about milkyway", "milky way is huge") 
search_terms <- c("milkyway", "document") 
pattern_fix <- sapply(strsplit(search_terms, split = NULL), paste0, collapse = "\\s?") 
sapply(pattern_fix, grep, docs) 

$`m\\s?i\\s?l\\s?k\\s?y\\s?w\\s?a\\s?y` 
[1] 1 2 

$`d\\s?o\\s?c\\s?u\\s?m\\s?e\\s?n\\s?t` 
[1] 1 

Edit:

Per verificare tutte le parole, si potrebbe utilizzare i nomi dei tmp.df nello script come SEARCH_TERMS nella mia soluzione.

doc1 <- "this is a document about milkyway" 
doc2 <- "milky way is huge" 

library(tm) 
tmp.text<-data.frame(rbind(doc1,doc2)) 
tmp.corpus<-Corpus(DataframeSource(tmp.text)) 
tmpDTM<-TermDocumentMatrix(tmp.corpus, control= list(tolower = T, removeNumbers = T, removePunctuation = TRUE,stopwords = TRUE,wordLengths = c(2, Inf))) 
tmp.df<-as.data.frame(as.matrix(tmpDTM)) 
tmp.df 

search_terms <- row.names(tmp.df) 
pattern_fix <- sapply(strsplit(search_terms, split = NULL), paste0, collapse = "\\s?") 
names(pattern_fix) <- search_terms 
word_count <- sapply(pattern_fix, grep, tmp.text[[1]]) 
h_table <- sapply(word_count, function(x) table(factor(x, levels = 1:nrow(tmp.text)))) #horizontal table 
v_table <- t(h_table) #vertical table (like tmp.df) 
v_table 

     1 2 
document 1 0 
huge  0 1 
milky 1 1 
milkyway 1 1 
way  1 1 
+0

grazie per fare uno sforzo. Ma la tua soluzione mi obbliga a menzionare esplicitamente i termini che voglio abbinare a quelli che non conosco in anticipo. Vedi il mio EDIT 1 e EDIT 2 se questo ti aiuta a trovare una soluzione migliore. – user3664020

+0

Vedere la mia modifica. Potrebbe esserci un modo migliore, ma questo funziona almeno per questo breve esempio. – JohannesNE

1

Avrete bisogno di convertire i documenti in un sacchetto di primitivo-parola rappresentanza di fronte. Quando una parola primitiva viene abbinata a un insieme di parole. La parola primitiva può anche essere nel corpus.

Per esempio:

milkyway -> {milky, milky way, milkyway} 
economy -> {economics, economy} 
sport -> {soccer, football, basket ball, basket, NFL, NBA} 

È possibile costruire tale dizionario prima di calcolare la distanza del coseno sia con un dizionario sinonimi e una distanza di modifica come Levenstein che completerà sinonimi.

La chiave informatica è più coinvolgente.

0

Ecco una soluzione che non richiede elenchi di parole preimpostate, ma esegue la separazione mediante tokenizzazione del testo come bigram in cui non vi è alcun carattere separatore tra le parole adiacenti, quindi cercando le corrispondenze nella tokenizzazione unigram. Questi vengono quindi salvati e successivamente sostituiti nei testi con le versioni separate.

Ciò significa che non sono richiesti elenchi predefiniti, ma solo quelli non analizzati che hanno versioni analizzate equivalenti nel testo. Si noti che questo potrebbe generare falsi positivi come "rimproverato" e "valutato" che potrebbero non essere occorrenze della stessa coppia, ma piuttosto un unigramma valido come nel primo termine, distinto dall'equivalente bigramma concatenato nel secondo termine. (Nessuna soluzione perfetta a questo particolare problema esiste.)

Questa soluzione richiede il pacchetto quanteda per analisi del testo, e il pacchetto stringi per la sostituzione regex Vectorised.

# original example 
myTexts <- c(doc1 = "this is a document about milkyway", doc2 = "milky way is huge") 

require(quanteda) 

unparseMatches <- function(texts) { 
    # tokenize all texts 
    toks <- quanteda::tokenize(toLower(texts), simplify = TRUE) 
    # tokenize bigrams 
    toks2 <- quanteda::ngrams(toks, 2, concatenator = " ") 
    # find out which compressed pairs exist already compressed in original tokens 
    compoundTokens <- toks2[which(gsub(" ", "", toks2) %in% toks)] 
    # vectorized replacement and return 
    result <- stringi::stri_replace_all_fixed(texts, gsub(" ", "", compoundTokens), compoundTokens, vectorize_all = FALSE) 
    # because stringi strips names 
    names(result) <- names(texts) 
    result 
} 

unparseMatches(myTexts) 
##         doc1         doc2 
## "this is a document about milky way"     "milky way is huge" 
quanteda::dfm(unparseMatches(myTexts), verbose = FALSE) 
## Document-feature matrix of: 2 documents, 8 features. 
## 2 x 8 sparse Matrix of class "dfmSparse" 
##  features 
## docs this is a document about milky way huge 
## doc1 1 1 1  1  1  1 1 0 
## doc1 0 1 0  0  0  1 1 1 


# another test, with two sets of phrases that need to be unparsed 
testText2 <- c(doc3 = "This is a super duper data set about the milky way.", 
       doc4 = "And here is another superduper dataset about the milkyway.") 
unparseMatches(testText2) 
##               doc3               doc4 
##   "This is a super duper data set about the milky way." "And here is another super duper data set about the milky way." 
(myDfm <- dfm(unparseMatches(testText2), verbose = FALSE)) 
## Document-feature matrix of: 2 documents, 14 features. 
## 2 x 14 sparse Matrix of class "dfmSparse" 
##  features 
## docs this is a super duper data set about the milky way and here another 
## doc3 1 1 1  1  1 1 1  1 1  1 1 0 0  0 
## doc4 0 1 0  1  1 1 1  1 1  1 1 1 1  1 

quanteda può anche fare calcoli di similarità come la distanza del coseno:

quanteda::similarity(myDfm, "doc3", margin = "documents", method = "cosine") 
##  doc4 <NA> 
## 0.7833  NA 

Non sono sicuro di quello che la NA è - sembra essere bug nel uscita quando c'è solo un documento da confrontare con un set di due documenti. (Riparerò così presto, ma il risultato è ancora corretta.)

0

Come Ken già detto: (. Nessuna soluzione perfetta a questo particolare problema esiste)

Per tutti So che questo è assolutamente giusto e supportato da molti libri di testo e riviste sul text mining, di solito entro i primi paragrafi.

Nella mia ricerca mi affido a set di dati già preparati come the „Deutscher Wortschatz“ project. Là hanno già fatto il duro lavoro e presentano elenchi di sinonimi di alta qualità, contrari, termini polisemici, ecc. Questo progetto i.a. fornisce un accesso all'interfaccia tramite sapone. Un database per la lingua inglese è Wordnet, per es ..

Se non si desidera utilizzare un set precalcolata o non può permetterselo vi consiglio di andare con l'approccio di Amirouche e primitivo-parola rappresentazioni. Costruirli a parole è noioso e laborioso, ma l'approccio più praticabile.

Ogni altro metodo che mi viene in mente è decisamente più complesso. Basta vedere le altre risposte o l'approccio all'avanguardia tratto da "Text Mining, Wissensrohstoff Text" di G. Heyer, U. Quasthoff e T.Wittig, come il clustering su forme di parole tramite (1) identificazione di caratteristiche caratteristiche (index-terms), (2) Creazione di Termine-Frase-Matrix e scelta di una ponderazione per il calcolo di un term-term-matrix, (3) scegliendo una similarità misuralo e eseguendolo sul tuo term-term-matrix e infine (4) seleziona ed esegui un algoritmo di clustering.

Vorrei suggerire di contrassegnare il post di amirouche come risposta corretta perché questo è finora il modo migliore e più praticabile di fare le cose (lo so).