Bir metni cümlelere nasıl bölebilirim?


109

Bir metin dosyam var. Bir cümle listesi almam gerekiyor.

Bu nasıl uygulanabilir? Kısaltmalarda kullanılan nokta gibi pek çok incelik vardır.

Eski normal ifadem kötü çalışıyor:

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

18
"Cümle" yi tanımlayın.
martineau

Bunu yapmak istiyorum, ancak bir nokta veya yeni bir satır olan her yerde bölmek istiyorum
yishairasowsky

Yanıtlar:


152

Natural Language Toolkit ( nltk.org ) ihtiyacınız olan şeylere sahiptir. Bu grup gönderimi bunu yaptığını gösterir:

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

(Ben denemedim!)



4
@Artyom: İşte için çevrimiçi belgelere doğrudan bağlantı nltk .tokenize.punkt.PunktSentenceTokenizer.
martineau

10
Önce çalıştırmanız nltk.download()ve modelleri indirmeniz gerekebilir ->punkt
Martin Thoma

2
Bu, tırnak işaretlerinin bittiği durumlarda başarısız olur. "Bu" gibi biten bir cümleniz varsa.
Fosa

1
Tamam, beni ikna ettin. Ama daha yeni test ettim ve başarısız görünmüyor. Benim girdidir 'This fails on cases with ending quotation marks. If we have a sentence that ends like "this." This is another sentence.'ve benim çıkış olduğunu ['This fails on cases with ending quotation marks.', 'If we have a sentence that ends like "this."', 'This is another sentence.']benim için doğru görünüyor.
szedjani

103

Bu işlev, Huckleberry Finn'in tüm metnini yaklaşık 0,1 saniyede cümlelere ayırabilir ve cümle ayrıştırmayı önemsiz hale getiren daha acı verici uç vakaların çoğunu ele alır, örneğin " Bay John Johnson Jr. ABD'de doğdu, ancak doktorasını kazandı. Bir mühendis olarak Nike Inc.'e katılmadan önce İsrail'de D.. Ayrıca craigslist.org'da iş analisti olarak çalıştı. "

# -*- 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
Bu harika bir çözüm. Bununla birlikte, normal ifadelerin bildirimine digits = "([0-9])" ve text = re.sub (digits + "[.]" + Digits, "\\ 1 <prd> \ \ 2 ", metin) işlevde. Şimdi satırı 5.5 gibi ondalık sayılara bölmez. Bu cevap için teşekkür ederim.
Ameya Kulkarni

1
Huckleberry Fin'in tamamını nasıl ayrıştırdınız? Metin biçiminde nerede?
PascalVKooten

6
Harika bir çözüm. İşlevde, text: text = text.replace ("eg", "e <prd> g <prd>") if "ie" ise text: text = text.replace ("ie" , "i <prd> e <prd>") ve sorunumu tamamen çözdü.
Sisay Chala 01

3
Çok faydalı yorumlarla harika bir çözüm! Sadece biraz daha sağlam olsa yapmak: prefixes = "(Mr|St|Mrs|Ms|Dr|Prof|Capt|Cpt|Lt|Mt)[.]", websites = "[.](com|net|org|io|gov|me|edu)", veif "..." in text: text = text.replace("...","<prd><prd><prd>")
Dascienz

1
Böyle cümleleri tek bir cümle olarak görmek için bu işlev yapılabilir mi: Bir çocuk annesine "Bebekler nereden geliyor?" Diye sorduğunda ona ne cevap vermeli?
twhale

50

Metni cümlelere ayırmak için normal ifade kullanmak yerine, nltk kitaplığını da kullanabilirsiniz.

>>> 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.']

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


Kabul edilen cevaptan daha harika, daha basit ve daha yeniden kullanılabilir bir örnek.
Jay D.

Bir noktadan sonra bir boşluk kaldırırsanız, tokenize.sent_tokenize () çalışmaz, ancak tokenizer.tokenize () çalışır! Hmm ...
Leonid Ganeline

1
for sentence in tokenize.sent_tokenize(text): print(sentence)
Victoria Stuart

11

Normal ifade yerine Spacy kullanmayı deneyebilirsiniz . Ben kullanıyorum ve işi yapıyor.

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

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

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

1
Uzay mega harika. ancak cümleleri ayırmanız gerekiyorsa, metni uzaya
aktarmak

@Berlines Kabul ediyorum, ancak işi spaCy kadar temiz yapan başka bir kitaplık bulamadım. Ama herhangi bir önerin varsa deneyebilirim.
Elf

Ayrıca, dışarıdaki AWS Lambda Sunucusuz kullanıcıları için, spacy'nin destek veri dosyaları çok sayıda 100MB'dir (İngilizce büyüktür> 400MB), bu tür şeyleri kutunun dışında kullanamazsınız, ne yazık ki (Spacy'nin büyük hayranı burada)
Julian H

9

Burada herhangi bir dış kütüphaneye dayanmayan yolun ortası yaklaşımı var. Kısaltmalar ve sonlandırıcılar arasındaki çakışmaları ve sonlandırmalardaki varyasyonlar arasındaki çakışmaları hariç tutmak için liste anlamayı kullanıyorum, örneğin: '.' 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)

Bu girişten Karl'ın find_all işlevini kullandım: Python'da bir alt dizenin tüm oluşumlarını bulun


1
Mükemmel yaklaşım! Diğerleri yakalamıyor ...ve ?!.
Shane Smiskol

6

Basit durumlarda (cümlelerin normal olarak sonlandırıldığı durumlarda), bu işe yaramalıdır:

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

Regex olan *\. +sol ve 1 veya daha fazla sağa 0 veya daha fazla boşluklarla çevrili bir dönemi eşleşen, (re.split dönem gibi bir şey cümlede bir değişiklik olarak sayılmasına önlemek için).

Açıkçası, en sağlam çözüm değil, ancak çoğu durumda iyi olacak. Bunun kapsamayacağı tek durum kısaltmalardır (belki cümleler listesini gözden geçirin ve her dizenin sentencesbüyük harfle başlayıp başlamadığını kontrol edin ?)


29
İngilizce'de bir cümlenin nokta ile bitmediği bir durum düşünemiyor musunuz? Hayal edin! Buna cevabım, "tekrar düşünün" olacaktır. (Orada ne yaptığımı gördün mü?)
Ned Batchelder

@Ned wow, bu kadar aptal olduğuma inanamıyorum. Sarhoş falan olmalıyım.
Rafe Kettler

Win 7 x86'da Python 2.7.2 kullanıyorum ve yukarıdaki koddaki normal ifade bana şu hatayı veriyor: SyntaxError: EOL while scanning string literalkapanış parantezini (sonra text) işaret ediyor . Ayrıca, metninizde referans verdiğiniz normal ifade kod örneğinizde mevcut değildir.
Sabuncu

1
r' *[\.\?!][\'"\)\]]* +'
Normal

Pek çok soruna neden olabilir ve bir cümleyi daha küçük parçalara bölebilir. "Bu dondurma için 3.5 dolar ödedim" durumunu düşünün, parçalar "3 dolar ödedim" ve "bu dondurma için 5". varsayılan nltk cümlesini kullanın. tokenizer daha güvenlidir!
Reihan_amn

6

NLTK'da cümle belirtme işlevini de kullanabilirsiniz:

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,

Selam! Bu işlevi kullanarak Rusça (ve diğer bazı diller) için yeni bir belirteç oluşturabilirsiniz:

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

ve sonra şu şekilde arayın:

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

İyi şanslar Marilena.


0

Hiç şüphe yok ki, NLTK amaca en uygun olanıdır. Ancak NLTK ile başlamak oldukça acı verici (Ama bir kez kurduğunuzda - sadece ödülleri alırsınız)

İşte, http://pythonicprose.blogspot.com/2009/09/python-split-paragraph-into-sentences.html adresinde bulunan basit yeniden tabanlı kod.

# 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
Evet ama bu çok kolay başarısız oluyor: "Bay Smith bunun bir cümle olduğunu biliyor."
thomas

0

Altyazı dosyalarını okuyup cümlelere bölmem gerekiyordu. Ön işlemden sonra (.srt dosyalarındaki zaman bilgisinin kaldırılması vb. Gibi), fullFile değişkeni altyazı dosyasının tam metnini içeriyordu. Aşağıdaki kaba yol onları düzgünce cümlelere ayırır. Muhtemelen, cümlelerin her zaman (doğru) boşlukla bittiği için şanslıydım. Önce bunu deneyin ve herhangi bir istisnası varsa, daha fazla kontrol ve bakiye ekleyin.

# 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! iyi. Şimdi anlıyorum ki içeriğim İspanyolca olduğu için, "Bay Smith" ile uğraşmak gibi sorunlar yaşamadım. Yine de, birisi hızlı ve kirli bir ayrıştırıcı isterse ...


0

umarım bu sana latin, çince, arapça metinlerde yardımcı olur

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

Benzer bir görev üzerinde çalışıyordu ve birkaç bağlantıyı izleyerek ve nltk için birkaç alıştırma üzerinde çalışarak bu sorguya rastladım, aşağıdaki kod benim için sihir gibi çalıştı.

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

çıktı:

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

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.