web-dev-qa-db-fra.com

Python diviser le texte en phrases

J'ai un fichier texte. J'ai besoin d'une liste de phrases.

Comment cela peut-il être mis en œuvre? Il y a beaucoup de subtilités, telles que l'utilisation des points dans les abréviations.

Mon ancien regexp fonctionne mal.

re.compile('(\. |^|!|\?)([A-Z][^;↑\.<>@\^&/\[\]]*(\.|!|\?) )',re.M)
83
Artyom

La boîte à outils en langage naturel ( nltk.org ) a ce dont vous avez besoin. Cet envoi de groupe indique que cela le fait:

import nltk.data

tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
fp = open("test.txt")
data = fp.read()
print '\n-----\n'.join(tokenizer.tokenize(data))

(Je ne l'ai pas essayé!)

130
Ned Batchelder

Cette fonction permet de scinder le texte entier de Huckleberry Finn en phrases d’environ 0,1 seconde et de gérer nombre des cas Edge les plus douloureux qui rendent l’analyse de phrase non triviale, par exemple. "M. John Johnson Jr. est né aux États-Unis mais a obtenu son doctorat en Israël avant de rejoindre Nike Inc. en tant qu'ingénieur. Il a également travaillé chez craigslist.org en tant qu'analyste commercial."

# -*- coding: utf-8 -*-
import re
alphabets= "([A-Za-z])"
prefixes = "(Mr|St|Mrs|Ms|Dr)[.]"
suffixes = "(Inc|Ltd|Jr|Sr|Co)"
starters = "(Mr|Mrs|Ms|Dr|He\s|She\s|It\s|They\s|Their\s|Our\s|We\s|But\s|However\s|That\s|This\s|Wherever)"
acronyms = "([A-Z][.][A-Z][.](?:[A-Z][.])?)"
websites = "[.](com|net|org|io|gov)"

def split_into_sentences(text):
    text = " " + text + "  "
    text = text.replace("\n"," ")
    text = re.sub(prefixes,"\\1<prd>",text)
    text = re.sub(websites,"<prd>\\1",text)
    if "Ph.D" in text: text = text.replace("Ph.D.","Ph<prd>D<prd>")
    text = re.sub("\s" + alphabets + "[.] "," \\1<prd> ",text)
    text = re.sub(acronyms+" "+starters,"\\1<stop> \\2",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>\\3<prd>",text)
    text = re.sub(alphabets + "[.]" + alphabets + "[.]","\\1<prd>\\2<prd>",text)
    text = re.sub(" "+suffixes+"[.] "+starters," \\1<stop> \\2",text)
    text = re.sub(" "+suffixes+"[.]"," \\1<prd>",text)
    text = re.sub(" " + alphabets + "[.]"," \\1<prd>",text)
    if "”" in text: text = text.replace(".”","”.")
    if "\"" in text: text = text.replace(".\"","\".")
    if "!" in text: text = text.replace("!\"","\"!")
    if "?" in text: text = text.replace("?\"","\"?")
    text = text.replace(".",".<stop>")
    text = text.replace("?","?<stop>")
    text = text.replace("!","!<stop>")
    text = text.replace("<prd>",".")
    sentences = text.split("<stop>")
    sentences = sentences[:-1]
    sentences = [s.strip() for s in sentences]
    return sentences
80
D Greenberg

Au lieu d'utiliser regex pour diviser le texte en phrases, vous pouvez également utiliser la bibliothèque nltk.

>>> from nltk import tokenize
>>> p = "Good morning Dr. Adams. The patient is waiting for you in room number 3."

>>> tokenize.sent_tokenize(p)
['Good morning Dr. Adams.', 'The patient is waiting for you in room number 3.']

ref: https://stackoverflow.com/a/9474645/2877052

27
Hassan Raza

Voici une approche intermédiaire qui ne repose sur aucune bibliothèque externe. J'utilise la compréhension de liste pour exclure les chevauchements entre abréviations et terminateurs, ainsi que pour exclure les chevauchements entre les variations de terminaisons, par exemple: '.' contre. '."'

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)

J'ai utilisé la fonction find_all de Karl à partir de cette entrée: Trouvez toutes les occurrences d'une sous-chaîne en Python

9
TennisVisuals

Vous pouvez essayer d'utiliser Spacy au lieu de regex. Je l'utilise et ça fait le travail.

import spacy
nlp = spacy.load('en')

text = '''Your text here'''
tokens = nlp(text)

for sent in tokens.sents:
    print(sent.string.strip())
6
Elf

Pour les cas simples (où les phrases se terminent normalement), cela devrait fonctionner:

import re
text = ''.join(open('somefile.txt').readlines())
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)

La regex est *\. +, qui correspond à une période entourée de 0 ou plusieurs espaces à gauche et de 1 ou plus à droite (pour éviter que quelque chose comme la période dans re.split ne soit comptée comme un changement de phrase).

De toute évidence, ce n’est pas la solution la plus robuste, mais elle ira très bien dans la plupart des cas. Le seul cas que cela ne couvre pas est celui des abréviations (peut-être parcourir la liste des phrases et vérifier que chaque chaîne de caractères dans sentences commence par une lettre majuscule?)

5
Rafe Kettler

@Artyom,

Salut! Vous pouvez créer un nouveau tokenizer pour le russe (et quelques autres langues) en utilisant cette fonction:

def russianTokenizer(text):
    result = text
    result = result.replace('.', ' . ')
    result = result.replace(' .  .  . ', ' ... ')
    result = result.replace(',', ' , ')
    result = result.replace(':', ' : ')
    result = result.replace(';', ' ; ')
    result = result.replace('!', ' ! ')
    result = result.replace('?', ' ? ')
    result = result.replace('\"', ' \" ')
    result = result.replace('\'', ' \' ')
    result = result.replace('(', ' ( ')
    result = result.replace(')', ' ) ') 
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.replace('  ', ' ')
    result = result.strip()
    result = result.split(' ')
    return result

puis appelez-le de cette façon:

text = 'вы выполняете поиск, используя Google SSL;'
tokens = russianTokenizer(text)

Bonne chance, Marilena.

1
Marilena Di Bari

Nul doute que NLTK est le plus approprié. Mais commencer avec NLTK est assez pénible (mais une fois que vous l’avez installé - vous n’en récoltez que les fruits)

Voici donc un code simple basé sur le code disponible sur http: //pythonicprose.blogspot.com/2009/09/python-split- paragraphe-into-sentences.html

# split up a paragraph into sentences
# using regular expressions


def splitParagraphIntoSentences(paragraph):
    ''' break a paragraph into sentences
        and return a list '''
    import re
    # to split by multile characters

    #   regular expressions are easiest (and fastest)
    sentenceEnders = re.compile('[.!?]')
    sentenceList = sentenceEnders.split(paragraph)
    return sentenceList


if __== '__main__':
    p = """This is a sentence.  This is an excited sentence! And do you think this is a question?"""

    sentences = splitParagraphIntoSentences(p)
    for s in sentences:
        print s.strip()

#output:
#   This is a sentence
#   This is an excited sentence

#   And do you think this is a question 
1
vaichidrewar

Vous pouvez également utiliser la fonction de tokenisation de phrase dans NLTK:

from nltk.tokenize import sent_tokenize
sentence = "As the most quoted English writer Shakespeare has more than his share of famous quotes.  Some Shakespare famous quotes are known for their beauty, some for their everyday truths and some for their wisdom. We often talk about Shakespeare’s quotes as things the wise Bard is saying to us but, we should remember that some of his wisest words are spoken by his biggest fools. For example, both ‘neither a borrower nor a lender be,’ and ‘to thine own self be true’ are from the foolish, garrulous and quite disreputable Polonius in Hamlet."

sent_tokenize(sentence)
1
woody

Je devais lire les fichiers de sous-titres et les scinder en phrases. Après le pré-traitement (comme supprimer les informations de temps, etc. dans les fichiers .srt), la variable fullFile contenait le texte intégral du fichier de sous-titre. La manière brute ci-dessous les scinde soigneusement en phrases. J'ai probablement eu de la chance que les phrases se terminent toujours (correctement) par un espace. Essayez ceci en premier et s'il y a des exceptions, ajoutez plus de freins et contrepoids.

# Very approximate way to split the text into sentences - Break after ? . and !
fullFile = re.sub("(\!|\?|\.) ","\\1<BRK>",fullFile)
sentences = fullFile.split("<BRK>");
sentFile = open("./sentences.out", "w+");
for line in sentences:
    sentFile.write (line);
    sentFile.write ("\n");
sentFile.close;

Oh! bien. Je réalise maintenant que, puisque mon contenu était en espagnol, je ne connaissais pas le problème de traiter avec "Mr. Smith", etc. Néanmoins, si quelqu'un veut un analyseur syntaxique rapide et sale ...

0
kishore