2015-06-23 8 views
8

Karl Broman's post: https://kbroman.wordpress.com/2015/06/22/randomized-hobbit-2/ habe mich mit regex und ngrams nur zum Spaß spielen. Ich habe versucht, Regex zu verwenden, um 2 Gramm zu extrahieren. Ich weiß, dass es Parser gibt, um das zu tun, aber ich interessiere mich für die Regex-Logik (d. H. Es war eine Selbst-Herausforderung, die ich nicht erfüllen konnte).r ngram extraktion mit regex

Im Folgenden gebe ich ein minimales Beispiel und die gewünschte Ausgabe. Das Problem in meinem Versuch ist 2-fach:

  1. Die Gramm (Worte) gefressen und ist für den nächsten Durchgang nicht zur Verfügung. Wie kann ich sie für den zweiten Durchgang zur Verfügung stellen? (zB mag ich like für like toast verfügbar sein, nachdem es bereits zuvor in I like verbraucht worden ist)

  2. Ich konnte nicht den Raum zwischen den Worten nicht gefangen machen (dem Leerraum in meiner Ausgabe feststellen, obwohl ich verwendet (?:\\s*)). Wie kann ich nachfolgende Leerzeichen am n-ten (in diesem Fall zweiten) Wort nicht erfassen? Ich weiß, dass dies einfach gemacht werden könnte mit: "(\\b[A-Za-z']+\\s)(\\b[A-Za-z']+)" für ein 2-Gramm, aber ich möchte die Lösung auf N-Gramm erweitern. PS Ich weiß über \\w, aber ich halte Unterstriche und Zahlen nicht als Wortteile, sondern betrachten ' als Wortteil.

MWE:

library(stringi) 

x <- "I like toast and jam." 

stringi::stri_extract_all_regex(
    x, 
    pattern = "((\\b[A-Za-z']+\\b)(?:\\s*)){2}" 
) 

## [[1]] 
## [1] "I like " "toast and " 

gewünschte Ausgabe:

## [[1]] 
## [1] "I like" "like toast" "toast and" "and jam" 
+0

Vielleicht ist der beste Ansatz zu Problem # 2: '" (\\ b [A-Za-z '] + \\ s +) {1} (\\ b [A-Za-z'] +) " 'wo Sie die Regex durch die Anpassung der 1 zu' n-1 ' –

Antwort

6

Hier ist eine Möglichkeit Basis R regex. Dies kann leicht erweitert werden, um beliebige N-Gramme zu handhaben. Der Trick besteht darin, die Capture-Gruppe in einer positiven Vorgriff Behauptung zu setzen, beispielsweise (?=(my_overlapping_pattern))

x <- "I like toast and jam." 
pattern <- "(?=(\\b[A-Za-z']+\\b \\b[A-Za-z']+\\b))" 
matches<-gregexpr(pattern, x, perl=TRUE) 
# a little post-processing needed to get the capture groups with regmatches 
attr(matches[[1]], 'match.length') <- as.vector(attr(matches[[1]], 'capture.length')[,1]) 
regmatches(x, matches) 

# [[1]] 
# [1] "I like"  "like toast" "toast and" "and jam" 
+0

Ich denke, das ist für zukünftige Suchenden verwandt: http://StackOverflow.com/a/25800334/1000343 Die relevante Terminologie, die mir fehlte, ist * Overlapping Matches * Danke für die Antwort. –

2

Eigentlich gibt es eine App dafür. Das quanteda Paket (für die quantitative Analyse von Textdaten). Mein Co-Autor Paul Nulty und ich arbeiten hart daran, dies zu verbessern, aber es behandelt leicht den Anwendungsfall, den Sie beschreiben.

install.packages("quanteda") 
require(quanteda) 
x <- "I like toast and jam." 
> ngrams(x, 2) 
## [[1]] 
## [1] "i_like"  "like_toast" "toast_and" "and_jam" 
ngrams(x, n = 2, concatenator = " ", toLower = FALSE) 
## [[1]] 
## [1] "I like"  "like toast" "toast and" "and jam" 

Keine schmerzhaften Regex erforderlich!

+0

Ich gebe nach, du hast Recht, aber hoffentlich findet jemand, der nach Fragen zu Wort-Ngrammen sucht, diese Hilfe nützlich! –

+1

Danke, @ Ken-Benoit, das sieht nach einem nützlichen Paket aus und ich freue mich darauf es zu überprüfen. –

+1

Ich habe das heute bei der Arbeit benutzt. Schöne Upgrades auf das Paket. +1 –