Come si analizza un paragrafo di testo in frasi? (perferibilmente in Ruby)

Come si prende un paragrafo o una grande quantità di testo e lo si suddivide in frasi (preferibilmente usando Ruby) prendendo in considerazione casi come Mr. and Dr. e USA? (Supponendo che tu metta le frasi in una serie di array)

AGGIORNAMENTO: Una ansible soluzione che ho pensato riguarda l’uso di un tagger di parti di discorso (POST) e un classificatore per determinare la fine di una frase:

Ottenere i dati dal signor Jones sentì il caldo sole sul suo volto mentre usciva sul balcone della sua casa estiva in Italia. Era felice di essere vivo.

CLASSIFICATORE Mr./PERSON Jones / PERSON felt / O the / O warm / O sun / O on / O his / O face / O as / O he / O stepped / O out / O su / O the / O balcony / O di / O his / O summer / O home / O in / O Italy / LOCATION ./O He / O era / O happy / O to / O be / O alive / O ./O

POST Mr./NNP Jones / NNP felt / VBD the / DT warm / JJ sun / NN on / IN his / PRP $ face / NN come / IN he / PRP stepped / VBD out / RP su / IN / DT balcone / NN di / nella sua / PRP $ estate / NN casa / NN in / IN Italia./NNP Lui / PRP era / VBD felice / JJ a / TO essere / VB vivo./IN

Possiamo supporre, dal momento che l’Italia è un luogo, il periodo è la fine valida della frase? Da quando termina su “Mr.” non avrebbe altre parti del discorso, possiamo supporre che questo non sia un periodo di fine frase valido? È questa la migliore risposta alla mia domanda?

Pensieri?

Prova a guardare l’ involucro di Ruby attorno allo Stanford Parser . Ha una funzione getSentencesFromString ().

Giusto per chiarire, non c’è una soluzione semplice a questo. Questo è l’argomento della ricerca sulla PNL come una veloce ricerca su Google mostra.

Tuttavia, sembra che ci siano alcuni progetti open source che si occupano di NLP che supportano il rilevamento delle frasi, ho trovato il seguente set di strumenti basato su Java:

openNLP

Commenti aggiuntivi: Il problema di decidere dove iniziare e finire le frasi è anche chiamato disambiguazione dei confini delle frasi (SBD) nell’elaborazione del linguaggio naturale .

Dai uno sguardo allo splitter della frase Python in NLTK (Natural Language Tool Kit):

Tokenizer di frase Punkt

Si basa sul seguente articolo:

Kiss, Tibor and Strunk, Jan (2006): Rilevamento di confini di frasi multilingue senza supervisione . Linguistica computazionale 32: 485-525.

L’approccio nella carta è piuttosto interessante. Riducono il problema della suddivisione della frase al problema di determinare quanto fortemente una parola è associata alla seguente punteggiatura. Il sovraccarico di periodi dopo le abbreviazioni è responsabile della maggior parte dei periodi ambigui, quindi se è ansible identificare le abbreviazioni è ansible identificare i limiti della frase con un’alta probabilità.

Ho testato questo strumento in modo informale un po ‘e sembra dare buoni risultati per una varietà di lingue (umane).

Portarlo su Ruby non sarebbe banale, ma potrebbe darti qualche idea.

Sembra che questa gem ruby potrebbe fare il trucco.

https://github.com/zencephalon/Tactful_Tokenizer

Questo è un problema difficile se ti interessa davvero farlo bene. Troverete che i pacchetti di parser PNL probabilmente forniscono questa funzionalità. Se vuoi qualcosa di più veloce, devi finire per duplicare parte di quella funzionalità con una funzione probabilistica addestrata di una finestra di token (probabilmente vorrai contare un line feed come token, dato che potrei lasciare un punto se è la fine di un paragrafo).

Modifica: Raccomando il parser di Stanford se puoi usare Java. Non ho alcuna raccomandazione per altre lingue, ma sono molto interessato a sentire che altro è là fuori che è open source.

Sfortunatamente non sono un tipo ruby ma forse un esempio in perl ti porterà nella giusta direzione. Usando uno sguardo non corrispondente dietro per la punteggiatura finale, alcuni casi speciali in un dietro, seguito da una quantità di spazio seguita da uno sguardo in avanti per una lettera maiuscola. Sono sicuro che non è perfetto, ma spero che punti nella giusta direzione. Non sei sicuro di come sapresti se gli USA sono effettivamente alla fine della frase …

#!/usr/bin/perl $string = "Mr. Thompson is from the USA and is 75 years old. Dr. Bob is a dentist. This is a string that contains several sentances. For example this is one. Followed by another. Can it deal with a question? It sure can!"; my @sentances = split(/(?:(?<=\.|\!|\?)(? 

Forse prova a dividerlo per un periodo seguito da uno spazio seguito da una lettera maiuscola? Non sono sicuro di come trovare le lettere maiuscole, ma quello sarebbe il modello che avrei iniziato a guardare.

Modifica: ricerca di lettere maiuscole con Ruby.

Un’altra modifica:

Controlla la frase che termina la punteggiatura che segue le parole che non iniziano con le lettere maiuscole.

La risposta del dott. Manning è la più appropriata se si considera il JAVA (e anche Ruby in modo difficile;)). È qui-

C’è uno splitter di frasi: edu.stanford.nlp.process.DocumentPreprocessor. Prova il comando: java edu.stanford.nlp.process.DocumentPreprocessor /u/nlp/data/lexparser/textDocument.txt

oneTokenizedSentencePerLine.txt. (Questo viene fatto tramite un FSM (buono ma euristico), quindi è veloce, non stai eseguendo il parser probabilistico.)

Ma un piccolo suggerimento se modifichiamo il comando java edu.stanford.nlp.process.DocumentPreprocessor /u/nlp/data/lexparser/textDocument.txt> oneTokenizedSentencePerLine.txt TO java edu.stanford.nlp.process.DocumentPreprocessor -file / u /nlp/data/lexparser/textDocument.txt> oneTokenizedSentencePerLine.txt . Funzionerà bene in quanto è necessario specificare quale tipo di file viene presentato come input. Così -file per il file di testo, -html per HTML, ecc.

Non l’ho provato, ma se l’inglese è l’unica lingua che ti interessa suggerirei di dare un’occhiata a Lingua :: EN :: Leggibilità .

Lingua :: IT :: Readability è un modulo Ruby che calcola le statistiche sul testo inglese. Può fornire conteggi di parole, frasi e sillabe. Può anche calcolare diverse misure di leggibilità, come un indice di nebbia e un livello di Flesch-Kincaid. Il pacchetto include il modulo Lingua :: IT :: Frase, che suddivide il testo inglese in frasi che abbreviano le abbreviazioni e Lingua :: EN :: Sillaba, che può indovinare il numero di sillabe in una parola scritta in inglese. Se è disponibile un dizionario di pronuncia, può cercare il numero di sillabe nel dizionario per una maggiore precisione

Il bit che vuoi è in sentence.rb come segue:

 module Lingua module EN # The module Lingua::EN::Sentence takes English text, and attempts to split it # up into sentences, respecting abbreviations. module Sentence EOS = "\001" # temporary end of sentence marker Titles = [ 'jr', 'mr', 'mrs', 'ms', 'dr', 'prof', 'sr', 'sen', 'rep', 'rev', 'gov', 'atty', 'supt', 'det', 'rev', 'col','gen', 'lt', 'cmdr', 'adm', 'capt', 'sgt', 'cpl', 'maj' ] Entities = [ 'dept', 'univ', 'uni', 'assn', 'bros', 'inc', 'ltd', 'co', 'corp', 'plc' ] Months = [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec', 'sept' ] Days = [ 'mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun' ] Misc = [ 'vs', 'etc', 'no', 'esp', 'cf' ] Streets = [ 'ave', 'bld', 'blvd', 'cl', 'ct', 'cres', 'dr', 'rd', 'st' ] @@abbreviations = Titles + Entities + Months + Days + Streets + Misc # Split the passed text into individual sentences, trim these and return # as an array. A sentence is marked by one of the punctuation marks ".", "?" # or "!" followed by whitespace. Sequences of full stops (such as an # ellipsis marker "..." and stops after a known abbreviation are ignored. def Sentence.sentences(text) text = text.dup # initial split after punctuation - have to preserve trailing whitespace # for the ellipsis correction next # would be nicer to use look-behind and look-ahead assertions to skip # ellipsis marks, but Ruby doesn't support look-behind text.gsub!( /([\.?!](?:\"|\'|\)|\]|\})?)(\s+)/ ) { $1 << EOS << $2 } # correct ellipsis marks and rows of stops text.gsub!( /(\.\.\.*)#{EOS}/ ) { $1 } # correct abbreviations # TODO - precompile this regex? text.gsub!( /(#{@@abbreviations.join("|")})\.#{EOS}/i ) { $1 << '.' } # split on EOS marker, strip gets rid of trailing whitespace text.split(EOS).map { | sentence | sentence.strip } end # add a list of abbreviations to the list that's used to detect false # sentence ends. Return the current list of abbreviations in use. def Sentence.abbreviation(*abbreviations) @@abbreviations += abbreviations @@abbreviations end end end end 

D’accordo con la risposta accettata, l’utilizzo della PNP Stanford Core è un gioco da ragazzi.

Tuttavia, nel 2016 ci sono alcune incompatibilità che collegano lo Stanford Parser con le versioni successive dello stanford core nlp (ho avuto problemi con Stanford Core NLP v3.5 ).

Ecco cosa ho fatto per analizzare il testo in frasi usando l’interfaccia di Ruby con la PNP Stanford Core :

  1. Installa la gem Stanford CoreNLP , che è ancora mantenuta e funziona, è stato difficile trovare gemme ruby NLP che funzionino ultimamente:

    gem install stanford-core-nlp

  2. Quindi seguire le istruzioni sul readme per l’utilizzo dell’ultima versione di Stanford CoreNLP :

L’utilizzo dell’ultima versione di Stanford CoreNLP (versione 3.5.0 al 31/10/2014) richiede alcuni passaggi manuali aggiuntivi:

  • Scarica Stanford CoreNLP versione 3.5.0 da http://nlp.stanford.edu/ .

  • Collocare il contenuto dell’archivio estratto all’interno della cartella / bin / della gem stanford-core-nlp (ad es. […] / gems / stanford-core-nlp-0.x / bin /) o all’interno della posizione della directory configurata impostando StanfordCoreNLP.jar_path.

  • Scarica la versione completa di Stanford Tagger 3.5.0 da http://nlp.stanford.edu/ .
  • Crea una directory denominata “tagger” all’interno della cartella / bin / della gem stanford-core-nlp (ad es. […] / gems / stanford-core-nlp-0.x / bin /) o all’interno della directory configurata da impostazione di StanfordCoreNLP.jar_path.
  • Posiziona il contenuto dell’archivio estratto nella directory dei tagger.
  • Scarica il file bridge.jar da https://github.com/louismullie/stanford-core-nlp .
  • Collocare il file bridger.jar scaricato all’interno della cartella / bin / della gem stanford-core-nlp (ad es. […] / gems / stanford-core-nlp-0.x / bin / taggers /) o all’interno della directory configurato impostando StanfordCoreNLP.jar_path.

Quindi il codice ruby per dividere il testo in frasi:

 require "stanford-core-nlp" #I downloaded the StanfordCoreNLP to a custom path: StanfordCoreNLP.jar_path = "/home/josh/stanford-corenlp-full-2014-10-31/" StanfordCoreNLP.use :english StanfordCoreNLP.model_files = {} StanfordCoreNLP.default_jars = [ 'joda-time.jar', 'xom.jar', 'stanford-corenlp-3.5.0.jar', 'stanford-corenlp-3.5.0-models.jar', 'jollyday.jar', 'bridge.jar' ] pipeline = StanfordCoreNLP.load(:tokenize, :ssplit) text = 'Mr. Josh Weir is writing some code. ' + 'I am Josh Weir Sr. my son may be Josh Weir Jr. etc. etc.' text = StanfordCoreNLP::Annotation.new(text) pipeline.annotate(text) text.get(:sentences).each{|s| puts "sentence: " + s.to_s} #output: #sentence: Mr. Josh Weir is writing some code. #sentence: I am Josh Weir Sr. my son may be Josh Weir Jr. etc. etc. 

Rompere su un periodo seguito da uno spazio e una lettera maiuscola non volerebbe per titoli come “Mr. Brown”.

I periodi rendono le cose difficili, ma un caso facile da gestire è punti esclamativi e punti interrogativi. Tuttavia, ci sono casi che potrebbero rendere questo non funziona. vale a dire il nome aziendale di Yahoo!

Beh, ovviamente paragraph.split('.') Non lo taglierà

#split prenderà #split come risposta in modo da provare a utilizzare un lookbehind a larghezza zero per cercare una parola che inizia con una lettera maiuscola. Ovviamente questo si dividerà sui nomi propri quindi dovresti ricorrere a /(Mr\.|Mrs\.|U\.S\.A ...) come questa /(Mr\.|Mrs\.|U\.S\.A ...) che sarebbe terribilmente brutta a meno che non hai costruito la regex a livello di codice .

Penso che questo non sia sempre risolvibile, ma potresti dividere in base a “.” (Un periodo seguito da e spazio vuoto) e verificare che la parola prima del punto non sia in un elenco di parole come Mr, Dr, ecc.

Ma, naturalmente, la tua lista potrebbe omettere alcune parole, e in tal caso otterrai cattivi risultati.

Non sono un ragazzo Ruby, ma un RegEx diviso

  ^(Mr|Mrs|Ms|Mme|Sta|Sr|Sra|Dr|U\.S\.A)[\.\!\?\"] [AZ] 

sarebbe la mia migliore scommessa, una volta ottenuto il paragrafo (diviso in \ r \ n). Questo presuppone che le tue frasi siano corrette.

Ovviamente questo è un RegEx abbastanza brutto. Che dire di forzare due spazi tra le frasi

GRAZIE !

Mi è piaciuta molto questa discussione, quindi mi sono interessato al parser. L’ho provato e ho annotato alcune note su come ottenere tutto ciò che funziona con Ruby e / o Rails!

Cercare di seguire un’espressione regolare era un incubo ..

i miei 2 centesimi