【问题标题】:How to remove stop phrases/stop ngrams (multi-word strings) using pandas/sklearn?如何使用 pandas/sklearn 删除停用短语/停用 ngram(多字串)?
【发布时间】:2018-01-07 15:15:04
【问题描述】:

我想防止某些短语潜入我的模型。例如,我想阻止“红玫瑰”进入我的分析。我了解如何添加 Adding words to scikit-learn's CountVectorizer's stop list 中给出的个别停用词:

from sklearn.feature_extraction import text
additional_stop_words=['red','roses']

但是,这也会导致无法检测到其他 ngram,例如“red tulips”或“blue roses”。

我正在构建一个 TfidfVectorizer 作为我的模型的一部分,我意识到我需要的处理可能必须在此阶段之后进入,但我不知道如何执行此操作。

我的最终目标是对一段文本进行主题建模。这是我正在处理的一段代码(几乎直接从 https://de.dariah.eu/tatom/topic_model_python.html#index-0 借来的):

from sklearn import decomposition

from sklearn.feature_extraction import text
additional_stop_words = ['red', 'roses']

sw = text.ENGLISH_STOP_WORDS.union(additional_stop_words)
mod_vectorizer = text.TfidfVectorizer(
    ngram_range=(2,3),
    stop_words=sw,
    norm='l2',
    min_df=5
)

dtm = mod_vectorizer.fit_transform(df[col]).toarray()
vocab = np.array(mod_vectorizer.get_feature_names())
num_topics = 5
num_top_words = 5
m_clf = decomposition.LatentDirichletAllocation(
    n_topics=num_topics,
    random_state=1
)

doctopic = m_clf.fit_transform(dtm)
topic_words = []

for topic in m_clf.components_:
    word_idx = np.argsort(topic)[::-1][0:num_top_words]
    topic_words.append([vocab[i] for i in word_idx])

doctopic = doctopic / np.sum(doctopic, axis=1, keepdims=True)
for t in range(len(topic_words)):
    print("Topic {}: {}".format(t, ','.join(topic_words[t][:5])))

编辑

示例数据框(我尝试插入尽可能多的边缘情况),df:

   Content
0  I like red roses as much as I like blue tulips.
1  It would be quite unusual to see red tulips, but not RED ROSES
2  It is almost impossible to find blue roses
3  I like most red flowers, but roses are my favorite.
4  Could you buy me some red roses?
5  John loves the color red. Roses are Mary's favorite flowers.

【问题讨论】:

    标签: python pandas scikit-learn nlp


    【解决方案1】:

    对于 Pandas,您想使用列表压缩

    .apply(lambda x: [item for item in x if item not in stop])
    

    【讨论】:

      【解决方案2】:

      您可以通过传递关键字参数tokenizer (doc-src) 来切换TfidfVectorizer 的标记器

      原来的样子是这样的:

      def build_tokenizer(self):
          """Return a function that splits a string into a sequence of tokens"""
          if self.tokenizer is not None:
              return self.tokenizer
          token_pattern = re.compile(self.token_pattern)
          return lambda doc: token_pattern.findall(doc)
      

      所以让我们创建一个函数来删除所有你不想要的单词组合。首先让我们定义你不想要的表达式:

      unwanted_expressions = [('red','roses'), ('foo', 'bar')]
      

      并且函数需要看起来像这样:

      token_pattern_str = r"(?u)\b\w\w+\b"
      def my_tokenizer(doc):
          """split a string into a sequence of tokens
          and remove some words along the way."""
      
          token_pattern = re.compile(token_pattern_str)
          tokens = token_pattern.findall(doc)
          for i in range(len(tokens)):
              for expr in unwanted_expressions:
                  found = True
                  for j, word in enumerate(expr):
                      found = found and (tokens[i+j] == word)
                  if found:
                      tokens[i:i+len(expr)] = len(expr) * [None]
          tokens = [x for x in tokens if x is not None]
          return tokens
      

      我自己没有专门尝试过这个,但我之前已经关闭了分词器。效果很好。

      祝你好运:)

      【讨论】:

      • 感谢@Philip Stark。当我调用 TfidfVectorizer 时,我基本上只是给出参数' tokenizer=my_tokenizer( df[ 'content' ] ) '?我已经编辑了我的帖子以提供带有内容列的示例 df。
      • 其实你只要给tokenizer=my_tokenizer。还不叫。它是一个函数对象。 Vectorizer 将在适当的时候调用它。请参阅我的原始代码链接以了解它的确切作用。
      • 它在测试数据帧中运行良好,但是对于不同的数据帧,我收到了“IndexError: list index out of range”错误。
      【解决方案3】:

      在将 df 传递给 mod_vectorizer 之前,您应该使用类似下一个:

      df=["I like red roses as much as I like blue tulips.",
      "It would be quite unusual to see red tulips, but not RED ROSES",
      "It is almost impossible to find blue roses",
      "I like most red flowers, but roses are my favorite.",
      "Could you buy me some red roses?",
      "John loves the color red. Roses are Mary's favorite flowers."]
      
      df=[ i.lower() for i in df]
      df=[i if 'red roses' not in i else i.replace('red roses','') for i in df]
      

      如果您检查的不仅仅是“红玫瑰”,请将上面的最后一行替换为:

      stop_phrases=['red roses']
      def filterPhrase(data,stop_phrases):
       for i in range(len(data)):
           for x in stop_phrases:
               if x in data[i]:
                   data[i]=data[i].replace(x,'')
       return data
      df=filterPhrase(df, stop_phrases)
      

      【讨论】:

        【解决方案4】:

        TfidfVectorizer 允许自定义预处理器。您可以使用它来进行任何需要的调整。

        例如,要从示例语料库中删除所有出现的连续“red”+“roses”标记(不区分大小写),请使用:

        import re
        from sklearn.feature_extraction import text
        
        cases = ["I like red roses as much as I like blue tulips.",
                 "It would be quite unusual to see red tulips, but not RED ROSES",
                 "It is almost impossible to find blue roses",
                 "I like most red flowers, but roses are my favorite.",
                 "Could you buy me some red roses?",
                 "John loves the color red. Roses are Mary's favorite flowers."]
        
        # remove_stop_phrases() is our custom preprocessing function.
        def remove_stop_phrases(doc):
            # note: this regex considers "... red. Roses..." as fair game for removal.
            #       if that's not what you want, just use ["red roses"] instead.
            stop_phrases= ["red(\s?\\.?\s?)roses"]
            for phrase in stop_phrases:
                doc = re.sub(phrase, "", doc, flags=re.IGNORECASE)
            return doc
        
        sw = text.ENGLISH_STOP_WORDS
        mod_vectorizer = text.TfidfVectorizer(
            ngram_range=(2,3),
            stop_words=sw,
            norm='l2',
            min_df=1,
            preprocessor=remove_stop_phrases  # define our custom preprocessor
        )
        
        dtm = mod_vectorizer.fit_transform(cases).toarray()
        vocab = np.array(mod_vectorizer.get_feature_names())
        

        现在vocab 已删除所有red roses 引用。

        print(sorted(vocab))
        
        ['Could buy',
         'It impossible',
         'It impossible blue',
         'It quite',
         'It quite unusual',
         'John loves',
         'John loves color',
         'Mary favorite',
         'Mary favorite flowers',
         'blue roses',
         'blue tulips',
         'color Mary',
         'color Mary favorite',
         'favorite flowers',
         'flowers roses',
         'flowers roses favorite',
         'impossible blue',
         'impossible blue roses',
         'like blue',
         'like blue tulips',
         'like like',
         'like like blue',
         'like red',
         'like red flowers',
         'loves color',
         'loves color Mary',
         'quite unusual',
         'quite unusual red',
         'red flowers',
         'red flowers roses',
         'red tulips',
         'roses favorite',
         'unusual red',
         'unusual red tulips']
        

        更新(每个评论线程):

        要将所需的停用词组和自定义停用词一起传递给包装函数,请使用:

        desired_stop_phrases = ["red(\s?\\.?\s?)roses"]
        desired_stop_words = ['Could', 'buy']
        
        def wrapper(stop_words, stop_phrases):
        
            def remove_stop_phrases(doc):
                for phrase in stop_phrases:
                    doc = re.sub(phrase, "", doc, flags=re.IGNORECASE)
                return doc
        
            sw = text.ENGLISH_STOP_WORDS.union(stop_words)
            mod_vectorizer = text.TfidfVectorizer(
                ngram_range=(2,3),
                stop_words=sw,
                norm='l2',
                min_df=1,
                preprocessor=remove_stop_phrases
            )
        
            dtm = mod_vectorizer.fit_transform(cases).toarray()
            vocab = np.array(mod_vectorizer.get_feature_names())
        
            return vocab
        
        wrapper(desired_stop_words, desired_stop_phrases)
        

        【讨论】:

        • 对于多个停用词组,代码(在 stop_phrases 列表中)有何变化?
        • 只需添加到stop_phrases 列表。该函数循环遍历每个短语并将其从语料库中删除。喜欢:["red roses", "blue tulips"]
        • 谢谢!有没有办法可以将停用词列表作为参数传递给 remove_stop_phrases() 函数?我的用例要求我在一个更大的函数中完成上述所有处理,根据需要输入停用词。
        • TfidfVectorizer 中的预处理器不接受附加参数。一种选择是将自定义停止短语列表传递给您的包装器函数,然后让remove_stop_phrases 引用包装器参数。我已在我的答案中添加了更新以进行演示。
        • 它工作得很好,除了我面临两个问题:1)它似乎忽略了通过的停用词。示例如下:
        猜你喜欢
        • 2019-08-11
        • 2018-09-19
        • 1970-01-01
        • 2019-02-19
        • 2018-08-07
        • 2016-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多