我有一个类似的过程:


建立一个单词标记模式-天真地用单词边界包围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/

10-12 22:47