Python-RegEx para dividir texto en oraciones(oración-tokenización)
nlp tokenize (8)
Enfoque ingenuo para oraciones inglesas adecuadas que no comiencen con non-alfas y que no contengan partes del discurso citadas:
import re
text = """/
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn''t. In any case, this isn''t true... Well, with a probability of .9 it isn''t.
"""
EndPunctuation = re.compile(r''([/./?/!]/s+)'')
NonEndings = re.compile(r''(?:Mrs?|Jr|i/.e)/./s*$'')
parts = EndPunctuation.split(text)
sentence = []
for part in parts:
if len(part) and len(sentence) and EndPunctuation.match(sentence[-1]) and not NonEndings.search(''''.join(sentence)):
print(''''.join(sentence))
sentence = []
if len(part):
sentence.append(part)
if len(sentence):
print(''''.join(sentence))
La división positiva falsa se puede reducir extendiendo NonEndings un poco. Otros casos requerirán código adicional. Manejar errores tipográficos de una manera sensata será difícil con este enfoque.
Nunca alcanzarás la perfección con este enfoque. Pero dependiendo de la tarea, podría funcionar "suficiente" ...
Quiero hacer una lista de oraciones de una cadena y luego imprimirlas. No quiero usar NLTK para hacer esto. Por lo tanto, necesita dividir un período al final de la oración y no en decimales o abreviaturas o título de un nombre o si la oración tiene un .com. Este es un intento de expresión regular que no funciona.
import re
text = """/
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn''t. In any case, this isn''t true... Well, with a probability of .9 it isn''t.
"""
sentences = re.split(r'' *[/./?!][/'"/)/]]* *'', text)
for stuff in sentences:
print(stuff)
Ejemplo de salida de lo que debería verse
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it.
Did he mind?
Adam Jones Jr. thinks he didn''t.
In any case, this isn''t true...
Well, with a probability of .9 it isn''t.
Intenta dividir la entrada según los espacios en lugar de un punto o ?
, si te gusta esto, entonces el punto o ?
no se imprimirá en el resultado final.
>>> import re
>>> s = """Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn''t. In any case, this isn''t true... Well, with a probability of .9 it isn''t."""
>>> m = re.split(r''(?<=[^A-Z].[.?]) +(?=[A-Z])'', s)
>>> for i in m:
... print i
...
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it.
Did he mind?
Adam Jones Jr. thinks he didn''t.
In any case, this isn''t true...
Well, with a probability of .9 it isn''t.
Prueba esto:
(?<!/b(?:[A-Z][a-z]|/d|[i.e]))/.(?!/b(?:com|/d+)/b)
Si quieres dividir oraciones en 3 períodos (no estoy seguro si esto es lo que quieres) puedes usar esta expresión regular:
import re text = """/ Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn''t. In any case, this isn''t true... Well, with a probability of .9 it isn''t. """ sentences = re.split(r''/.{3}'', text) for stuff in sentences: print(stuff)
(?<!/w/./w.)(?<![A-Z][a-z]/.)(?<=/.|/?)/s
Prueba esto. divida su cadena en esta. También puede verificar la demostración.
sent = re.split(''(?<!/w/./w.)(?<![A-Z][a-z]/.)(?<=/.|/?)(/s|[A-Z].*)'',text)
for s in sent:
print s
Aquí la expresión regular utilizada es: (?<!/w/./w.)(?<![AZ][az]/.)(?<=/.|/?)(/s|[AZ].*)
Primer bloque: (?<!/w/./w.)
: Este patrón busca en un bucle de retroalimentación negativa (?<!)
Para todas las palabras (/w)
seguido de la parada completa (/.)
, Seguida de otras palabras (/.)
Segundo bloque: (?<![AZ][az]/.)
: Este patrón busca en un bucle de retroalimentación negativa cualquier cosa que comience con alfabetos en mayúsculas ([AZ])
, seguido de letras minúsculas ([az])
hasta un punto (/.)
se encuentra.
Tercer bloque: (?<=/.|/?)
: Este patrón busca en un bucle de retroalimentación de punto (/.)
O interrogación (/?)
Cuarto bloque: (/s|[AZ].*)
: Este patrón busca el punto O el signo de interrogación del tercer bloque. Busca espacios en blanco (/s)
O cualquier secuencia de caracteres que comience con un alfabeto en mayúscula ([AZ].*)
. Este bloque es importante para dividir si la entrada es tan
Hola mundo. Hola, estoy aquí hoy.
es decir, si hay espacio o no hay espacio después del punto.
Escribí esto teniendo en cuenta los comentarios de smci anteriores. Es un enfoque intermedio que no requiere bibliotecas externas y no utiliza expresiones regulares. Le permite proporcionar una lista de abreviaturas y cuentas para oraciones terminadas por terminadores en envoltorios, como un punto y una cita: [. ",? '',.)].
abbreviations = {''dr.'': ''doctor'', ''mr.'': ''mister'', ''bro.'': ''brother'', ''bro'': ''brother'', ''mrs.'': ''mistress'', ''ms.'': ''miss'', ''jr.'': ''junior'', ''sr.'': ''senior'', ''i.e.'': ''for example'', ''e.g.'': ''for example'', ''vs.'': ''versus''}
terminators = [''.'', ''!'', ''?'']
wrappers = [''"'', "''", '')'', '']'', ''}'']
def find_sentences(paragraph):
end = True
sentences = []
while end > -1:
end = find_sentence_end(paragraph)
if end > -1:
sentences.append(paragraph[end:].strip())
paragraph = paragraph[:end]
sentences.append(paragraph)
sentences.reverse()
return sentences
def find_sentence_end(paragraph):
[possible_endings, contraction_locations] = [[], []]
contractions = abbreviations.keys()
sentence_terminators = terminators + [terminator + wrapper for wrapper in wrappers for terminator in terminators]
for sentence_terminator in sentence_terminators:
t_indices = list(find_all(paragraph, sentence_terminator))
possible_endings.extend(([] if not len(t_indices) else [[i, len(sentence_terminator)] for i in t_indices]))
for contraction in contractions:
c_indices = list(find_all(paragraph, contraction))
contraction_locations.extend(([] if not len(c_indices) else [i + len(contraction) for i in c_indices]))
possible_endings = [pe for pe in possible_endings if pe[0] + pe[1] not in contraction_locations]
if len(paragraph) in [pe[0] + pe[1] for pe in possible_endings]:
max_end_start = max([pe[0] for pe in possible_endings])
possible_endings = [pe for pe in possible_endings if pe[0] != max_end_start]
possible_endings = [pe[0] + pe[1] for pe in possible_endings if sum(pe) > len(paragraph) or (sum(pe) < len(paragraph) and paragraph[sum(pe)] == '' '')]
end = (-1 if not len(possible_endings) else max(possible_endings))
return end
def find_all(a_str, sub):
start = 0
while True:
start = a_str.find(sub, start)
if start == -1:
return
yield start
start += len(sub)
Utilicé la función find_all de Karl desde esta entrada: encuentre todas las apariciones de una subcadena en Python
Ok, los tokenizadores de oraciones son algo que analicé con un pequeño detalle, usando expresiones regulares, nltk, CoreNLP. Usted termina escribiendo el suyo y depende de la aplicación. Esto es engañoso y valioso, y la gente no solo regala su código tokenizador. (En última instancia, la tokenización no es un procedimiento determinista, es probabilística, y también depende en gran medida de su corpus o dominio, por ejemplo, publicaciones en redes sociales vs revisiones de Yelp vs ...)
En general, no puede confiar en una única expresión regular infalible de Great White , debe escribir una función que use varias expresiones regulares (tanto positivas como negativas); también un diccionario de abreviaturas, y un análisis de lenguaje básico que sabe que, por ejemplo, "I", "EE. UU.", "FCC", "TARP" están en mayúscula en inglés.
Para ilustrar cuán fácilmente esto puede complicarse seriamente, intentemos escribirle esa especificación funcional para un tokenizador determinista solo para decidir si el período único o múltiple (''.'' / ''...'') indica el final de la oración , o algo más:
function isEndOfSentence(leftContext, rightContext)
- Devuelve False para decimales dentro de los números o la moneda, por ejemplo 1.23, $ 1.23, "Eso es solo $ .02" Considere también referencias de sección como 1.2.3 o formatos de fecha europeos como 09.07.2014
- Devuelve False (y no tokenize en letras individuales) para abreviaturas conocidas, por ejemplo, "Las acciones de EE. UU. Están cayendo"; esto requiere un diccionario de abreviaciones conocidas. Cualquier cosa fuera de ese diccionario te equivocarás.
- Las elipsis ''...'' al final de las oraciones son terminales, pero en el medio de las oraciones no lo son. Esto no es tan fácil como podría pensar: necesita mirar el contexto de la izquierda y el contexto correcto, específicamente el RHS está en mayúscula y considerar de nuevo las palabras en mayúscula como "I" y las abreviaturas. Aquí hay un ejemplo que demuestra la ambigüedad que: Ella me pidió que me quedara ... Me fui una hora más tarde. (¿Fueron una o dos frases? Imposible de determinar)
- También es posible que desee escribir algunos patrones para detectar y rechazar usos diversos de la puntuación que no incluyen la oración: emoticones, arte ASCII, elipsis espaciadas. . . y otras cosas esp. Gorjeo. (Hacer que la adaptación sea aún más difícil). ¿Cómo podemos saber si @midnight es un usuario de Twitter, el programa de Comedy Central o simplemente la puntuación no deseada / basura / error tipográfico? En serio no trivial.
- Después de manejar todos esos casos negativos, puede decir arbitrariamente que cualquier período aislado seguido de espacios en blanco es probable que sea el final de una oración. (En definitiva, si realmente quiere comprar precisión adicional, termina escribiendo su propia fraseología probabilística-tokenizer que usa ponderaciones y entrenándola en un corpus específico (por ejemplo, textos legales, medios de difusión, , Twitter, comentarios de foros, etc.) ) Luego debe revisar manualmente los ejemplos y los errores de entrenamiento. Ver el libro de Manning y Jurafsky o el curso de Coursera [a]. En definitiva, obtienes la corrección que estás dispuesto a pagar.
- Todo lo anterior es claramente específico de las abreviaturas en inglés, los formatos de número / hora / fecha de EE. UU. Si desea que sea independiente del país y del idioma, esa es una propuesta más grande, necesitará corpus, personas nativas para etiquetar y QA todo, etc.
- Todo lo anterior sigue siendo solo ASCII. Permita que la entrada sea Unicode, y las cosas se vuelven aún más difíciles (y el conjunto de entrenamiento necesariamente debe ser mucho más grande o mucho más escaso)
En el caso simple (determinista), la function isEndOfSentence(leftContext, rightContext)
devuelve booleano, pero en el sentido más general, es probabilístico: devuelve un flotante (nivel de confianza de que ese particular ''.'' Es un final de oración).
Referencias: [a] Video de Coursera: "Procesamiento de texto básico 2-5 - Segmentación de frases - Stanford NLP - Profesor Dan Jurafsky y Chris Manning"