148 votes

Comment diviser un texte en phrases ?

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

Comment cela peut-il être mis en œuvre ? Il y a beaucoup de subtilités, comme l'utilisation d'un point dans les abréviations.

Mon ancienne expression régulière fonctionne mal :

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

0 votes

Je veux faire cela, mais je veux séparer chaque fois qu'il y a un point ou un saut de ligne.

178voto

Ned Batchelder Points 128913

La boîte à outils pour le langage naturel ( nltk.org ) a ce qu'il vous faut. Cet affichage de groupe indique que ça 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é !)

0 votes

Merci, j'espère que cette bibliothèque fonctionnera avec la langue russe.

3 votes

@Artyom : Cela peut probablement fonctionner avec le russe -- voir le NLTK/pyNLTK peut-il fonctionner "par langue" (c'est-à-dire autre que l'anglais), et comment ? .

4 votes

@Artyom : Voici un lien direct vers la documentation en ligne pour nltk .tokenize.punkt.PunktSentenceTokenizer .

124voto

D Greenberg Points 1223

Cette fonction peut découper l'ensemble du texte de Huckleberry Finn en phrases en environ 0,1 seconde et gère la plupart des cas limites les plus douloureux qui rendent l'analyse syntaxique des phrases 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

24 votes

C'est une solution géniale. Cependant, j'y ai ajouté deux lignes supplémentaires digits = "([0-9])" dans la déclaration des expressions régulières et text = re.sub(digits + "[.]" + chiffres, " \\1 <prd> \\2 ", texte) dans la fonction. Maintenant, la ligne n'est plus coupée aux décimales, comme 5,5. Merci pour cette réponse.

2 votes

Comment avez-vous analysé l'intégralité de Huckleberry Fin ? Où est-ce que ça se trouve en format texte ?

6 votes

Une bonne solution. Dans la fonction, j'ai ajouté if "e.g." in text : text = text.replace("e.g.", "e<prd>g<prd>") if "i.e." in text : text = text.replace("i.e.", "i<prd>e<prd>") et cela a entièrement résolu mon problème.

72voto

Hassan Raza Points 36

Au lieu d'utiliser une expression rationnelle 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.']

réf : https://stackoverflow.com/a/9474645/2877052

0 votes

Excellent exemple, plus simple et plus réutilisable que la réponse acceptée.

0 votes

Si vous supprimez un espace après un point, tokenize.sent_tokenize() ne fonctionne pas, mais tokenizer.tokenize() fonctionne ! Hmm...

1 votes

for sentence in tokenize.sent_tokenize(text): print(sentence)

13voto

Elf Points 21

Vous pouvez essayer d'utiliser Spacy au lieu de regex. Je l'utilise et il 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())

3 votes

Space est très bien. Mais si vous avez juste besoin de séparer en phrases, passer le texte à Space prendra trop de temps si vous avez affaire à un tuyau de données.

0 votes

@Berlines Je suis d'accord mais je n'ai pas pu trouver d'autre bibliothèque qui fasse le travail aussi proprement que spaCy. Mais si vous avez une suggestion, je peux essayer.

2 votes

Par ailleurs, pour les utilisateurs de AWS Lambda Serverless, les fichiers de données pris en charge par Spacy font plusieurs centaines de mégaoctets (un grand fichier anglais fait plus de 400 mégaoctets), ce qui fait qu'il est impossible d'utiliser ce type d'application dès sa sortie de l'emballage.

9voto

TennisVisuals Points 169

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

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 dans cet article : Trouver toutes les occurrences d'une sous-chaîne en Python

1 votes

Une approche parfaite ! Les autres n'attrapent pas ... et ?! .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X