Python 如何防止在NLTK中拆分特定的单词、短语和数字?

Python 如何防止在NLTK中拆分特定的单词、短语和数字?,python,nltk,tokenize,phrase,Python,Nltk,Tokenize,Phrase,当我标记分割特定单词、日期和数字的文本时,文本匹配有问题。在NLTK中标记单词时,如何防止诸如“在我家跑步”、“30分钟步行”或“每天4次”之类的短语分裂 它们不应导致: ['runs','in','my','family','4x','a','day'] 例如: 是的,我每天骑自行车20-30分钟,效果很好 给出: ['yes','20-30','minutes','a','day','on','my','bike',',','it','works','great'] 我想把“20-30分

当我标记分割特定单词、日期和数字的文本时,文本匹配有问题。在NLTK中标记单词时,如何防止诸如“在我家跑步”、“30分钟步行”或“每天4次”之类的短语分裂

它们不应导致:

['runs','in','my','family','4x','a','day']
例如:

是的,我每天骑自行车20-30分钟,效果很好

给出:

['yes','20-30','minutes','a','day','on','my','bike',',','it','works','great']

我想把“20-30分钟”当作一个词来对待。如何获得此行为>?

据我所知,您将很难在标记化的同时保留不同长度的n-gram,但您可以找到这些n-gram,如图所示。然后,您可以用一些连接字符(如破折号)替换语料库中您想要作为n-gram的项目

这是一个示例解决方案,但可能有很多方法可以实现重要提示:我提供了一种查找文本中常见的ngrams的方法(您可能需要1个以上的变量,因此我在其中放置了一个变量,以便您可以决定要收集多少个ngrams。您可能需要为每种类型指定不同的数字,但我现在只给出了1个变量)。这可能会遗漏您认为重要的ngrams。为此,您可以将想要查找的内容添加到
user\u grams
。这些将被添加到搜索中

import nltk 

#an example corpus
corpus='''A big tantrum runs in my family 4x a day, every week. 
A big tantrum is lame. A big tantrum causes strife. It runs in my family 
because of our complicated history. Every week is a lot though. Every week
I dread the tantrum. Every week...Here is another ngram I like a lot'''.lower()

#tokenize the corpus
corpus_tokens = nltk.word_tokenize(corpus)

#create ngrams from n=2 to 5
bigrams = list(nltk.ngrams(corpus_tokens,2))
trigrams = list(nltk.ngrams(corpus_tokens,3))
fourgrams = list(nltk.ngrams(corpus_tokens,4))
fivegrams = list(nltk.ngrams(corpus_tokens,5))
本节将查找高达5克的常见Ngram

#if you change this to zero you will only get the user chosen ngrams
n_most_common=1 #how many of the most common n-grams do you want.

fdist_bigrams = nltk.FreqDist(bigrams).most_common(n_most_common) #n most common bigrams
fdist_trigrams = nltk.FreqDist(trigrams).most_common(n_most_common) #n most common trigrams
fdist_fourgrams = nltk.FreqDist(fourgrams).most_common(n_most_common) #n most common four grams
fdist_fivegrams = nltk.FreqDist(fivegrams).most_common(n_most_common) #n most common five grams

#concat the ngrams together
fdist_bigrams=[x[0][0]+' '+x[0][1] for x in fdist_bigrams]
fdist_trigrams=[x[0][0]+' '+x[0][1]+' '+x[0][2] for x in fdist_trigrams]
fdist_fourgrams=[x[0][0]+' '+x[0][1]+' '+x[0][2]+' '+x[0][3] for x in fdist_fourgrams]
fdist_fivegrams=[x[0][0]+' '+x[0][1]+' '+x[0][2]+' '+x[0][3]+' '+x[0][4]  for x in fdist_fivegrams]

#next 4 lines create a single list with important ngrams
n_grams=fdist_bigrams
n_grams.extend(fdist_trigrams)
n_grams.extend(fdist_fourgrams)
n_grams.extend(fdist_fivegrams)
本节允许您将自己的NGRAM添加到列表中

#Another option here would be to make your own list of the ones you want
#in this example I add some user ngrams to the ones found above
user_grams=['ngram1 I like', 'ngram 2', 'another ngram I like a lot']
user_grams=[x.lower() for x in user_grams]    

n_grams.extend(user_grams)
最后一部分执行处理,以便您可以再次标记化并将NGRAM作为标记

#initialize the corpus that will have combined ngrams
corpus_ngrams=corpus

#here we go through the ngrams we found and replace them in the corpus with
#version connected with dashes. That way we can find them when we tokenize.
for gram in n_grams:
    gram_r=gram.replace(' ','-')
    corpus_ngrams=corpus_ngrams.replace(gram, gram.replace(' ','-'))

#retokenize the new corpus so we can find the ngrams
corpus_ngrams_tokens= nltk.word_tokenize(corpus_ngrams)

print(corpus_ngrams_tokens)

Out: ['a-big-tantrum', 'runs-in-my-family', '4x', 'a', 'day', ',', 'every-week', '.', 'a-big-tantrum', 'is', 'lame', '.', 'a-big-tantrum', 'causes', 'strife', '.', 'it', 'runs-in-my-family', 'because', 'of', 'our', 'complicated', 'history', '.', 'every-week', 'is', 'a', 'lot', 'though', '.', 'every-week', 'i', 'dread', 'the', 'tantrum', '.', 'every-week', '...']

我认为这实际上是一个很好的问题。

您可以使用
MWETokenizer

from nltk import word_tokenize
from nltk.tokenize import MWETokenizer

tokenizer = MWETokenizer([('20', '-', '30', 'minutes', 'a', 'day')])
tokenizer.tokenize(word_tokenize('Yes 20-30 minutes a day on my bike, it works great!!'))
[out]:

['Yes', '20-30_minutes_a_day', 'on', 'my', 'bike', ',', 'it', 'works', 'great', '!', '!']
['Yes', '20-30 minutes a day', 'on', 'my', 'bike', ',', 'it', 'works', 'great', '!!']

一种更具原则性的方法,因为您不知道“单词标记化”将如何分割您想要保留的单词:

from nltk import word_tokenize
from nltk.tokenize import MWETokenizer

def multiword_tokenize(text, mwe):
    # Initialize the MWETokenizer
    protected_tuples = [word_tokenize(word) for word in mwe]
    protected_tuples_underscore = ['_'.join(word) for word in protected_tuples]
    tokenizer = MWETokenizer(protected_tuples)
    # Tokenize the text.
    tokenized_text = tokenizer.tokenize(word_tokenize(text))
    # Replace the underscored protected words with the original MWE
    for i, token in enumerate(tokenized_text):
        if token in protected_tuples_underscore:
            tokenized_text[i] = mwe[protected_tuples_underscore.index(token)]
    return tokenized_text

mwe = ['20-30 minutes a day', '!!']
print(multiword_tokenize('Yes 20-30 minutes a day on my bike, it works great!!', mwe))
[out]:

['Yes', '20-30_minutes_a_day', 'on', 'my', 'bike', ',', 'it', 'works', 'great', '!', '!']
['Yes', '20-30 minutes a day', 'on', 'my', 'bike', ',', 'it', 'works', 'great', '!!']

愚蠢的第一个问题!我认为用标点符号和语法稍微清理一下这个问题是值得的,因为我认为这不是一个简单的任务。我提供了一个解决方案,但我担心它在计算上会非常昂贵。让其他用户也参与进来会有很大帮助。问得好!还有一些函数是用
nltk
编写的,它的工作原理与
spacy
语言学步骤/regex模式approach.thanx稍有不同。如果我想匹配我在数据集中找到的n-gram,那么我应该制作自己的列表来匹配它,并且只保留列表中的n-gram,但这会更耗时?我在代码中包含了该选项。如果您不想找到最常见的,只需将
n\u most\u common=1
更改为
n\u most\u common=0
。不过,我希望我的解决方案是独立的、可验证的。我会把它编辑成评论。然后你可以把你想要的n-gram添加到
user\u-gram
列表中。而且,似乎不可能有那么多的ngram同时是不常见和重要的。换句话说,如果你要费心去标记这些ngram,那应该是因为它们对你很重要,但是如果它们不经常出现,那本身就不那么重要了。你应该用开始的方法得到所有重要的共同点,并且只需要添加一些你的研究所特有的,但这只是我的预感。如果你回答了这个问题,请考虑一下投票,并回答它。看见