テキストを文に分割するにはどうすればよいですか?


108

テキストファイルがあります。文のリストを取得する必要があります。

これはどのように実装できますか?略語にドットが使用されているなど、多くの微妙な点があります。

私の古い正規表現はうまく機能しません:

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

18
「文」を定義します。
martineau

私はこれをしたいが、私は、期間や改行のいずれかがある場所に分割したい
yishairasowsky

回答:


152

自然言語ツールキット(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
Martin Thoma

2
これは、引用符で終わる場合に失敗します。「これ」のように終わる文がある場合。
フォサ

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

100

この機能は、ハックルベリーフィンのテキスト全体を約0.1秒で文に分割でき、たとえば「ジョンジョンソンジュニア氏は米国で生まれたが博士号を取得しました。 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
これは素晴らしいソリューションです。ただし、正規表現の宣言でdigits = "([0-9])"とそれに2行追加しました。text= re.sub(digits + "[。]" + digits、 "\\ 1 <prd> \ \ 2 "、text)関数内。現在は、5.5などの小数で行を分割しません。この回答をありがとうございます。
Ameya Kulkarni

1
ハックルベリーフィン全体をどのように解析しましたか?それはテキスト形式でどこにありますか?
PascalVKooten 2017

6
素晴らしいソリューションです。関数に、「eg」がテキスト内にある場合に追加しました:text = text.replace( "eg"、 "e <prd> g <prd>")if「ie」がテキスト内にある場合:text = text.replace( "ie" 、 "i <prd> e <prd>")と私の問題を完全に解決しました。
Sisay Chala 2017

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
この機能を使用して、次のような文を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


受け入れられた回答よりも優れた、シンプルで再利用可能な例。
ジェイD.

ドットの後のスペースを削除すると、tokenize.sent_tokenize()は機能しませんが、tokenizer.tokenize()は機能します。うーん...
Leonid 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ラムダサーバレスユーザーアウトのために、スペイシーのサポートデータファイルがあり、多くの100メガバイト(英語大>は400メガバイトである)あなたは非常に悲しいことに、(ここではスペイシーの巨大なファンを)箱のこのうちのようなものを使用することはできませんので
ジュリアン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
完璧なアプローチ!他の人がキャッチしない...?!
シェーンSmiskol 16

6

単純なケース(文が正常に終了する)の場合、これは機能するはずです。

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

正規表現はです*\. +。これは、左に0個以上のスペース、右に1個以上のスペースで囲まれたピリオドに一致します(re.splitのピリオドなどが文の変更としてカウントされないようにするため)。

明らかに、最も堅牢なソリューションではありませんが、ほとんどの場合は問題ありません。これがカバーしない唯一のケースは略語です(おそらく、文のリストを実行して、各文字列がsentences大文字で始まっていることを確認しますか?)


29
文章がピリオドで終わっていない英語の状況は考えられませんか?想像してみろ!それに対する私の返答は、「もう一度考えて」です。(私がそこで何をしたかを参照してください)
Ned Batchelder

@ネッドすごい、私がそんなに愚かだったなんて信じられない。酔っ払ってるかな。
Rafe Kettler

私は、Win 7 x86でPython 2.7.2を使用しています。上記のコードの正規表現では、次のエラーが表示されます:SyntaxError: EOL while scanning string literal、閉じ括弧(の後text)を指します。また、テキストで参照する正規表現は、コードサンプルには存在しません。
Sabuncu 2013

1
正規表現は完全に正しいわけではありません。それは正しいはずですr' *[\.\?!][\'"\)\]]* +'
fsociety

それは多くの問題を引き起こし、文をより小さなチャンクにチャンクするかもしれません。「このアイスクリームに3.5ドル払った」というケースを考えてみてください。それらのチャンクは「3ドル払った」と「このアイスクリームに5ドル」です。デフォルトのnltkセンテンスを使用してください。tokenizerの方が安全です!
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)

幸運、マリーナ。


0

NLTKがこの目的に最も適していることは間違いありません。しかし、NLTKの使用を開始するのは非常に困難です(しかし、一度インストールすると、報酬を得るだけです)。

だからここに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 

3
でも、これは簡単に失敗します。「スミスさんはこれが文であることを知っています。」
トーマス

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;

ああ!上手。私のコンテンツはスペイン語だったので、「スミスさん」などの問題はなかったことがわかりました。


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.