我有一个类似的过程:
建立一个单词标记模式-天真地用单词边界包围2个以上字母数字。
标记文档,然后使用nltk标记这些标记。
在sklearn的内置英语停用词中添加一些“ custom”停用词。 (此处,在可复制的示例中仅使用一个公司名称。)
利用上述方法获得词频,从gram到4-gram。
问题在于(大概是因为首先出现了令牌化这一事实?)多词停用词(短语)没有被丢弃。
完整示例:
import re
import nltk
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS as ESW, CountVectorizer
# Make sure we have the corpora used by nltk's lemmatizer
try:
nltk.data.find('corpora/wordnet')
except:
nltk.download('wordnet')
# "Naive" token similar to that used by sklearn
TOKEN = re.compile(r'\b\w{2,}\b')
# Tokenize, then lemmatize these tokens
# Modified from:
# http://scikit-learn.org/stable/modules/feature_extraction.html#customizing-the-vectorizer-classes
class LemmaTokenizer(object):
def __init__(self):
self.wnl = WordNetLemmatizer()
def __call__(self, doc):
return (self.wnl.lemmatize(t) for t in TOKEN.findall(doc))
# Add 1 more phrase to sklearn's stop word list
sw = ESW.union(frozenset(['sinclair broadcast group']))
vect = CountVectorizer(stop_words=sw, ngram_range=(1, 4),
tokenizer=LemmaTokenizer())
# These are nonsense babbling
docs = ["""And you ask Why You Are Sinclair Broadcast Group is Asking It""",
"""Why are you asking what Sinclair Broadcast Group and you"""]
tf = vect.fit_transform(docs)
重申一下:单词停用词已被正确删除,但该短语仍然存在:
vect.get_feature_names()
# ['ask',
# 'ask sinclair',
# 'ask sinclair broadcast',
# 'ask sinclair broadcast group',
# 'asking',
# 'asking sinclair',
# 'asking sinclair broadcast',
# 'asking sinclair broadcast group',
# 'broadcast',
# 'broadcast group',
# 'broadcast group asking',
# 'group',
# 'group asking',
# 'sinclair',
# 'sinclair broadcast',
# 'sinclair broadcast group',
# 'sinclair broadcast group asking']
我该如何纠正?
最佳答案
从the documentation of CountVectorizer
:
stop_words:字符串{'english'},列表或“无”(默认)
如果为“英语”,则使用内置的英语停用词列表。
如果是列表,则假定该列表包含停用词,所有停用词将从结果标记中删除。仅在分析器=='word'时适用。
如果为None,则不使用停用词。可以将max_df设置为[0.7,1.0)范围内的值,以基于术语的内部语料库文档频率自动检测和过滤停用词。
然后进一步查找参数token_pattern
:
token_pattern:字符串
表示什么构成“令牌”的正则表达式,仅在分析器=='word'时使用。默认的regexp select标记包含2个或更多字母数字字符(标点符号被完全忽略,始终视为标记分隔符)。
因此,只有在analyzer(token)
的结果等于'sinclair broadcast group'
的情况下,它才会删除停用词。但是默认的analyzer
是'word'
,这意味着停用词检测仅适用于单个单词,因为令牌是由默认的token_pattern
定义的,如上所述。
标记不是n-gram(而是n-gram由标记组成,并且在构造n-gram之前,停用词的删除似乎发生在标记级别)。
快速检查一下,您可以将您的自定义停用词更改为实验的'sinclair'
,以查看将其视为独立词时可以正确删除该词。
换句话说,您需要将自己的可调用对象传递为analyzer
,以使其也将分析器逻辑应用于n-gram,您必须手动对其进行检查。但是默认行为是假设停用词检测不能应用于n-gram,而只能应用于单个词。
以下是针对您的案例的自定义分析器功能的示例。这是based on this answer ...请注意,我没有对其进行测试,因此可能存在错误。
def trigram_match(i, trigram, words):
if i < len(words) - 2 and words[i:i + 3] == trigram:
return True
if (i > 0 and i < len(words) - 1) and words[i - 1:i + 2] == trigram:
return True
if i > 1 and words[i - 2:i + 1] == trigram:
return True
return False
def custom_analyzer(text):
bad_trigram = ['sinclair', 'broadcasting', 'group']
words = [str.lower(w) for w in re.findall(r'\w{2,}', text)]
for i, w in enumerate(words):
if w in sw or trigram_match(i, bad_trigram, words):
continue
yield w
关于python - sklearn/nltk中的“词组”停用词被忽略,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49014129/