텍스트를 문장으로 나누려면 어떻게해야합니까?


108

텍스트 파일이 있습니다. 문장 목록이 필요합니다.

어떻게 구현할 수 있습니까? 약어에 점이 사용되는 등 미묘한 부분이 많이 있습니다.

내 이전 정규식이 잘못 작동합니다.

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

18
"문장"을 정의하십시오.
martineau

어느 기간 또는 줄 바꿈이 어디든지 내가 이렇게 할,하지만 난 분할 할
yishairasowsky

답변:


152

Natural Language Toolkit ( nltk.org )에는 필요한 것이 있습니다. 이 그룹 게시물 은 다음을 수행함을 나타냅니다.

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))

(나는 그것을 시도하지 않았습니다!)



4
@Artyom : 여기에 대한 온라인 설명서에 대한 직접 링크가 있습니다 nltk .tokenize.punkt.PunktSentenceTokenizer.
martineau 2011 년

10
당신은 실행해야 할 수도 있습니다 nltk.download()먼저 다운로드 모델을 ->punkt
마틴 토마스

2
끝 따옴표가있는 경우에는 실패합니다. "this"와 같이 끝나는 문장이 있다면.
Fosa

1
좋아요, 당신은 나를 설득했습니다. 그러나 나는 방금 테스트했으며 실패하지 않은 것 같습니다. 내 입력은 'This fails on cases with ending quotation marks. If we have a sentence that ends like "this." This is another sentence.'내 출력은 ['This fails on cases with ending quotation marks.', 'If we have a sentence that ends like "this."', 'This is another sentence.']나에게 맞는 것 같습니다.
szedjani

101

이 기능은 약 0.1 초 만에 Huckleberry Finn의 전체 텍스트를 문장으로 분할 할 수 있으며 " Mr. John Johnson Jr.는 미국에서 태어 났지만 박사 학위를 취득했습니다. D.는 엔지니어로 Nike Inc.에 합류하기 전에 이스라엘에서 근무했으며 craigslist.org에서 비즈니스 분석가로 일했습니다. "

# -*- 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

19
이것은 멋진 솔루션입니다. 그러나 정규식 선언에 Digit = "([0-9])"와 text = re.sub (digits + "[.]"+ numbers, "\\ 1 <prd> \ \ 2 ", text). 이제 5.5와 같은 소수로 줄을 분할하지 않습니다. 이 답변에 감사드립니다.
Ameya Kulkarni

1
전체 Huckleberry Fin을 어떻게 구문 분석 했습니까? 텍스트 형식은 어디에 있습니까?
PascalVKooten

6
훌륭한 솔루션입니다. 함수에서 if "eg"in text : text = text.replace ( "eg", "e <prd> g <prd>") if "ie"in text : text = text.replace ( "ie" , "i <prd> e <prd>") 내 문제가 완전히 해결되었습니다.
Sisay Chala

3
매우 유용한 의견으로 훌륭한 솔루션! 그냥 좀 더 강력한 불구로 만들려면 : prefixes = "(Mr|St|Mrs|Ms|Dr|Prof|Capt|Cpt|Lt|Mt)[.]", websites = "[.](com|net|org|io|gov|me|edu)",와if "..." in text: text = text.replace("...","<prd><prd><prd>")
Dascienz

1
이와 같은 문장을 한 문장으로 볼 수 있도록이 기능을 만들 수 있습니다. 아이가 어머니에게 "아기들은 어디에서 왔습니까?"라고 물으면 어떤 대답을해야할까요?
twhale

50

정규식을 사용하여 텍스트를 문장으로 분할하는 대신 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.']

참조 : https://stackoverflow.com/a/9474645/2877052


허용되는 답변보다 위대하고 간단하며 재사용 가능한 예입니다.
Jay D.

점 뒤의 공백을 제거하면 tokenize.sent_tokenize ()는 작동하지 않지만 tokenizer.tokenize ()는 작동합니다! 흠 ...
레오 니드 Ganeline

1
for sentence in tokenize.sent_tokenize(text): print(sentence)
빅토리아 스튜어트

11

정규식 대신 Spacy 를 사용해 볼 수 있습니다 . 나는 그것을 사용하고 그것은 일을한다.

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

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

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

1
공간은 엄청나 다. 하지만 문장으로 분리해야한다면 데이터 파이프를 다루는 경우 텍스트를 공백으로 전달하는 데 시간이 너무 오래 걸립니다
Berlines

@Berlines 동의하지만 spaCy만큼 깨끗한 작업을 수행하는 다른 라이브러리를 찾을 수 없습니다. 하지만 제안 사항이 있으면 시도해 볼 수 있습니다.
Elf

또한 AWS Lambda 서버리스 사용자의 경우 spacy의 지원 데이터 파일이 100MB (영어 크기가 400MB 이상)이므로 이와 같은 것을 즉시 사용할 수 없습니다. 매우 슬프게도 (여기 Spacy의 열렬한 팬)
Julian H

9

다음은 외부 라이브러리에 의존하지 않는 중간 접근 방식입니다. 목록 이해를 사용하여 약어와 종결 자 간의 겹침을 제외하고 종결에 대한 변형 간의 겹침을 제외합니다 (예 : '.'). 대 '. "'

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)

이 항목에서 Karl의 find_all 함수를 사용 했습니다. Python에서 하위 문자열의 모든 항목 찾기


1
완벽한 접근! 나머지는 ...?!.
Shane Smiskol

6

간단한 경우 (문장이 정상적으로 종료되는 경우)는 다음과 같이 작동합니다.

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

정규식은이며 *\. +, 이는 왼쪽에 0 개 이상의 공백과 오른쪽에 1 개 이상의 공백으로 둘러싸인 마침표와 일치합니다 (re.split의 마침표와 같은 것이 문장의 변경으로 계산되는 것을 방지하기 위해).

분명히 가장 강력한 솔루션은 아니지만 대부분의 경우 잘 작동합니다. 여기에서 다루지 않는 유일한 경우는 약어입니다 (아마도 문장 목록을 살펴보고의 각 문자열 sentences이 대문자 로 시작 하는지 확인 하시겠습니까?).


29
문장이 마침표로 끝나지 않는 영어 상황을 생각할 수 없습니까? 상상 해봐! 그것에 대한 나의 대답은 "다시 생각하라"입니다. (내가 거기에서 무엇을했는지
보십니까

@Ned 와우, 내가 그렇게 멍청했다는 것을 믿을 수 없습니다. 난 취 했나봐요
Rafe Kettler

Win 7 x86에서 Python 2.7.2를 사용하고 있으며 위 코드의 정규식 SyntaxError: EOL while scanning string literal은 닫는 괄호 (뒤 text)를 가리키는 오류를 제공합니다 . 또한 텍스트에서 참조하는 정규식이 코드 샘플에 존재하지 않습니다.
Sabuncu

1
정규식은 완전히 정확하지 않습니다r' *[\.\?!][\'"\)\]]* +'
fsociety

그것은 많은 문제를 일으키고 문장을 더 작은 덩어리로 묶을 수도 있습니다. "나는이 아이스크림에 대해 3.5 달러를 지불했습니다"라는 경우를 생각해보십시오. 청크는 "나는이 아이스크림에 대해 3 달러를 지불했습니다"와 "이 아이스크림에 5 달러를 지불했습니다"입니다. 기본 nltk 문장을 사용하십시오.
Reihan_amn

6

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)

2

@Artyom,

안녕하세요! 이 함수를 사용하여 러시아어 (및 일부 다른 언어) 용 새 토크 나이저를 만들 수 있습니다.

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

그런 다음 다음과 같이 호출하십시오.

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

행운을 빕니다, Marilena.


0

NLTK가 목적에 가장 적합하다는 것은 의심 할 여지가 없습니다. 그러나 NLTK를 시작하는 것은 매우 고통 스럽습니다 (하지만 일단 설치하면 보상을받을 수 있습니다).

그래서 여기에 http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-into-sentences.html 에서 사용할 수있는 간단한 re 기반 코드가 있습니다 .

# 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 

3
예,하지만 이것은 너무 쉽게 실패합니다. "스미스 씨는 이것이 문장이라는 것을 알고 있습니다."
thomas

0

저는 자막 파일을 읽어서 문장으로 나누어야했습니다. 사전 처리 (.srt 파일에서 시간 정보 제거 등) 후 fullFile 변수에 자막 파일의 전체 텍스트가 포함되었습니다. 아래의 조잡한 방법은 깔끔하게 문장으로 나눕니다. 아마도 문장이 항상 공백으로 (올바르게) 끝나는 것이 운이 좋았을 것입니다. 먼저 이것을 시도하고 예외가 있으면 견제와 균형을 더 추가하십시오.

# 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;

오! 잘. 나는 지금 내 내용이 스페인어 였기 때문에 "Mr. Smith"등을 다루는 문제가 없었 음을 깨달았습니다. 그래도 누군가 빠르고 더러운 파서를 원한다면 ...


0

라틴어, 중국어, 아랍어 텍스트에 도움이 되길 바랍니다.

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()]

0

유사한 작업을 수행하고 있었고 몇 개의 링크를 따르고 nltk에 대한 몇 가지 연습을 수행 함으로써이 쿼리를 발견했습니다. 아래 코드는 마술처럼 저에게 효과적이었습니다.

from nltk.tokenize import sent_tokenize 
  
text = "Hello everyone. Welcome to GeeksforGeeks. You are studying NLP article"
sent_tokenize(text) 

산출:

['Hello everyone.',
 'Welcome to GeeksforGeeks.',
 'You are studying NLP article']

출처 : https://www.geeksforgeeks.org/nlp-how-tokenizing-text-sentence-words-works/

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.