Mam plik tekstowy. Potrzebuję listę zdań.
Jak można to wdrożyć? Istnieje wiele subtelności, takich jak kropka używana w skrótach.
Moje stare wyrażenie regularne działa źle:
re.compile('(\. |^|!|\?)([A-Z][^;↑\.<>@\^&/\[\]]*(\.|!|\?) )',re.M)
Mam plik tekstowy. Potrzebuję listę zdań.
Jak można to wdrożyć? Istnieje wiele subtelności, takich jak kropka używana w skrótach.
Moje stare wyrażenie regularne działa źle:
re.compile('(\. |^|!|\?)([A-Z][^;↑\.<>@\^&/\[\]]*(\.|!|\?) )',re.M)
Odpowiedzi:
Natural Language Toolkit ( nltk.org ) ma to, czego potrzebujesz. Ten post grupowy wskazuje, że to robi:
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))
(Nie próbowałem tego!)
nltk .tokenize.punkt.PunktSentenceTokenizer
.
nltk.download()
najpierw uruchomić i pobrać modele ->punkt
'This fails on cases with ending quotation marks. If we have a sentence that ends like "this." This is another sentence.'
a mój wynik ['This fails on cases with ending quotation marks.', 'If we have a sentence that ends like "this."', 'This is another sentence.']
wydaje się być poprawny dla mnie.
Ta funkcja może podzielić cały tekst Huckleberry Finn na zdania w około 0,1 sekundy i obsługuje wiele bardziej bolesnych skrajnych przypadków, które sprawiają, że analiza zdań jest nietrywialna, np. „ Pan John Johnson Jr. urodził się w USA, ale zdobył tytuł doktora. D. w Izraelu, zanim dołączył do Nike Inc. jako inżynier. Pracował również na craigslist.org jako analityk biznesowy ”.
# -*- 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
prefixes = "(Mr|St|Mrs|Ms|Dr|Prof|Capt|Cpt|Lt|Mt)[.]"
, websites = "[.](com|net|org|io|gov|me|edu)"
, iif "..." in text: text = text.replace("...","<prd><prd><prd>")
Zamiast używać wyrażenia regularnego do dzielenia tekstu na zdania, możesz również użyć biblioteki 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.']
for sentence in tokenize.sent_tokenize(text): print(sentence)
Możesz spróbować użyć Spacy zamiast regex. Używam go i spełnia swoje zadanie.
import spacy
nlp = spacy.load('en')
text = '''Your text here'''
tokens = nlp(text)
for sent in tokens.sents:
print(sent.string.strip())
Oto środek drogi, który nie polega na żadnych zewnętrznych bibliotekach. Używam funkcji rozumienia list, aby wykluczyć nakładanie się skrótów i terminatorów, a także aby wykluczyć nakładanie się między odmianami zakończeń, na przykład: „.” 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)
Użyłem funkcji find_all Karla z tego wpisu: Znajdź wszystkie wystąpienia podciągu w Pythonie
...
i ?!
.
W prostych przypadkach (gdzie zdania kończą się normalnie) powinno to działać:
import re
text = ''.join(open('somefile.txt').readlines())
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)
Wyrażenie regularne to *\. +
, które dopasowuje kropkę otoczoną 0 lub więcej spacjami po lewej stronie i 1 lub więcej po prawej stronie (aby zapobiec liczeniu czegoś takiego jak kropka w re.split jako zmiana w zdaniu).
Oczywiście nie jest to najbardziej solidne rozwiązanie, ale w większości przypadków będzie dobrze. Jedynym przypadkiem, którego to nie obejmuje, są skróty (może przejrzyj listę zdań i sprawdź, czy każdy ciąg sentences
zaczyna się od dużej litery?)
SyntaxError: EOL while scanning string literal
:, wskazując na nawias zamykający (po text
). Ponadto wyrażenie regularne, do którego odwołujesz się w tekście, nie istnieje w przykładowym kodzie.
r' *[\.\?!][\'"\)\]]* +'
Możesz również użyć funkcji tokenizacji zdań w 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)
@Artyom,
Cześć! Możesz stworzyć nowy tokenizer dla języka rosyjskiego (i kilku innych języków), korzystając z tej funkcji:
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
a potem nazwij to w ten sposób:
text = 'вы выполняете поиск, используя Google SSL;'
tokens = russianTokenizer(text)
Powodzenia, Marilena.
Bez wątpienia NLTK jest najbardziej odpowiedni do tego celu. Ale rozpoczęcie pracy z NLTK jest dość bolesne (ale po zainstalowaniu - po prostu czerpiesz korzyści)
Oto prosty kod ponownie oparty na http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-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 __name__ == '__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
Musiałem przeczytać pliki z napisami i podzielić je na zdania. Po wstępnym przetworzeniu (takim jak usunięcie informacji o czasie itp. Z plików .srt) zmienna fullFile zawierała pełny tekst pliku z napisami. Poniższy, prymitywny sposób zgrabnie podzielił je na zdania. Zapewne miałem szczęście, że zdania zawsze kończyły się (poprawnie) spacją. Spróbuj najpierw tego, a jeśli ma jakieś wyjątki, dodaj więcej kontroli i sald.
# 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;
O! dobrze. Teraz zdaję sobie sprawę, że ponieważ moja treść była hiszpańska, nie miałem problemów z radzeniem sobie z „Mr.
mam nadzieję, że to pomoże ci w tekście łacińskim, chińskim i arabskim
import re
punctuation = re.compile(r"([^\d+])(\.|!|\?|;|\n|。|!|?|;|…| |!|؟|؛)+")
lines = []
with open('myData.txt','r',encoding="utf-8") as myFile:
lines = punctuation.sub(r"\1\2<pad>", myFile.read())
lines = [line.strip() for line in lines.split("<pad>") if line.strip()]
Pracowałem nad podobnym zadaniem i natrafiłem na to zapytanie, klikając kilka linków i pracując nad kilkoma ćwiczeniami dla nltk, poniższy kod działał dla mnie jak magia.
from nltk.tokenize import sent_tokenize
text = "Hello everyone. Welcome to GeeksforGeeks. You are studying NLP article"
sent_tokenize(text)
wynik:
['Hello everyone.',
'Welcome to GeeksforGeeks.',
'You are studying NLP article']
Źródło: https://www.geeksforgeeks.org/nlp-how-tokenizing-text-sentence-words-works/