【问题标题】:How do I calculate a word-word co-occurrence matrix with sklearn?如何用 sklearn 计算词-词共现矩阵?
【发布时间】:2016-06-04 10:03:52
【问题描述】:

我正在寻找 sklearn 中的一个模块,它可以让您导出词-词共现矩阵。

我可以获得文档术语矩阵,但不知道如何获取共现的词词矩阵。

【问题讨论】:

  • 您能否添加一些数据以及您解决问题的尝试?

标签: python matrix scikit-learn


【解决方案1】:

所有提供的答案都没有考虑到窗口移动的概念。所以,我做了自己的函数,通过应用定义大小的移动窗口来找到共现矩阵。

这个函数接受一个句子列表和一个window_size数字;它返回一个代表共现矩阵的pandas.DataFrame对象:

from collections import defaultdict

def co_occurrence(sentences, window_size):
    d = defaultdict(int)
    vocab = set()
    for text in sentences:
        # preprocessing (use tokenizer instead)
        text = text.lower().split()
        # iterate over sentences
        for i in range(len(text)):
            token = text[i]
            vocab.add(token)  # add to vocab
            next_token = text[i+1 : i+1+window_size]
            for t in next_token:
                key = tuple( sorted([t, token]) )
                d[key] += 1
    
    # formulate the dictionary into dataframe
    vocab = sorted(vocab) # sort vocab
    df = pd.DataFrame(data=np.zeros((len(vocab), len(vocab)), dtype=np.int16),
                      index=vocab,
                      columns=vocab)
    for key, value in d.items():
        df.at[key[0], key[1]] = value
        df.at[key[1], key[0]] = value
    return df

让我们试试下面两个简单的句子:

>>> text = ["I go to school every day by bus .",
            "i go to theatre every night by bus"]
>>> 
>>> df = co_occurrence(text, 2)
>>> df
         .  bus  by  day  every  go  i  night  school  theatre  to
.        0    1   1    0      0   0  0      0       0        0   0
bus      1    0   2    1      0   0  0      1       0        0   0
by       1    2   0    1      2   0  0      1       0        0   0
day      0    1   1    0      1   0  0      0       1        0   0
every    0    0   2    1      0   0  0      1       1        1   2
go       0    0   0    0      0   0  2      0       1        1   2
i        0    0   0    0      0   2  0      0       0        0   2
night    0    1   1    0      1   0  0      0       0        1   0
school   0    0   0    1      1   1  0      0       0        0   1
theatre  0    0   0    0      1   1  0      1       0        0   1
to       0    0   0    0      2   2  2      0       1        1   0

[11 rows x 11 columns]

现在,我们有了同现矩阵。

【讨论】:

  • 什么是defaultdict(int)
  • 它是一个字典,其值具有默认数据类型。您可以像这样导入它:from collections import defaultdict。所以,defaultdict(int) 是一个带有int 值的字典。这与普通字典之间的唯一区别是defaultdict 在找不到密钥时不会引发KeyError。这就是我使用它的原因。
  • every day by busevery night by bus同时出现了every和bus,那为什么共现矩阵对于every和bus都是0呢?
  • @GuruVishnuVardhanReddy,这里我使用大小为2 的移动窗口,这意味着对于索引i 处的每个单词,我将只考虑索引i-1、@987654335 处的单词@、i+1i+2。这就是为什么 (to, every) 的值是 2 而 (to, bus) 的值是 0。希望这会有所帮助。
【解决方案2】:

使用 numpy,因为语料库将是列表的列表(每个列表都是一个标记化的文档):

corpus = [['<START>', 'All', 'that', 'glitters', "isn't", 'gold', '<END>'], 
          ['<START>', "All's", 'well', 'that', 'ends', 'well', '<END>']]

和一个词->行/列映射

def compute_co_occurrence_matrix(corpus, window_size):

    words = sorted(list(set([word for words_list in corpus for word in words_list])))
    num_words = len(words)

    M = np.zeros((num_words, num_words))
    word2Ind = dict(zip(words, range(num_words)))

    for doc in corpus:

        cur_idx = 0
        doc_len = len(doc)

        while cur_idx < doc_len:

            left = max(cur_idx-window_size, 0)
            right = min(cur_idx+window_size+1, doc_len)
            words_to_add = doc[left:cur_idx] + doc[cur_idx+1:right]
            focus_word = doc[cur_idx]

            for word in words_to_add:
                outside_idx = word2Ind[word]
                M[outside_idx, word2Ind[focus_word]] += 1

            cur_idx += 1

    return M, word2Ind

【讨论】:

    【解决方案3】:

    我使用下面的代码来创建具有窗口大小的共现矩阵:

    #https://stackoverflow.com/questions/4843158/check-if-a-python-list-item-contains-a-string-inside-another-string
    import pandas as pd
    def co_occurance_matrix(input_text,top_words,window_size):
        co_occur = pd.DataFrame(index=top_words, columns=top_words)
    
        for row,nrow in zip(top_words,range(len(top_words))):
            for colm,ncolm in zip(top_words,range(len(top_words))):        
                count = 0
                if row == colm: 
                    co_occur.iloc[nrow,ncolm] = count
                else: 
                    for single_essay in input_text:
                        essay_split = single_essay.split(" ")
                        max_len = len(essay_split)
                        top_word_index = [index for index, split in enumerate(essay_split) if row in split]
                        for index in top_word_index:
                            if index == 0:
                                count = count + essay_split[:window_size + 1].count(colm)
                            elif index == (max_len -1): 
                                count = count + essay_split[-(window_size + 1):].count(colm)
                            else:
                                count = count + essay_split[index + 1 : (index + window_size + 1)].count(colm)
                                if index < window_size: 
                                    count = count + essay_split[: index].count(colm)
                                else:
                                    count = count + essay_split[(index - window_size): index].count(colm)
                    co_occur.iloc[nrow,ncolm] = count
    
        return co_occur
    

    然后我使用下面的代码进行测试:

    corpus = ['ABC DEF IJK PQR','PQR KLM OPQ','LMN PQR XYZ ABC DEF PQR ABC']
    words = ['ABC','PQR','DEF']
    window_size =2 
    
    result = co_occurance_matrix(corpus,words,window_size)
    result
    

    输出在这里:

    【讨论】:

      【解决方案4】:

      这是我在 scikit-learn 中使用 CountVectorizer 的示例解决方案。而参考这个post,可以简单的用矩阵乘法得到词-词共现矩阵。

      from sklearn.feature_extraction.text import CountVectorizer
      docs = ['this this this book',
              'this cat good',
              'cat good shit']
      count_model = CountVectorizer(ngram_range=(1,1)) # default unigram model
      X = count_model.fit_transform(docs)
      # X[X > 0] = 1 # run this line if you don't want extra within-text cooccurence (see below)
      Xc = (X.T * X) # this is co-occurrence matrix in sparse csr format
      Xc.setdiag(0) # sometimes you want to fill same word cooccurence to 0
      print(Xc.todense()) # print out matrix in dense format
      

      您也可以参考count_model中的词典,

      count_model.vocabulary_
      

      或者,如果您想通过对角线分量进行归一化(参考上一篇文章中的回答)。

      import scipy.sparse as sp
      Xc = (X.T * X)
      g = sp.diags(1./Xc.diagonal())
      Xc_norm = g * Xc # normalized co-occurence matrix
      

      额外要注意@Federico Caccia 的回答,如果您不希望自己的文本中出现虚假的共现,请设置大于 1 到 1 的出现,例如

      X[X > 0] = 1 # do this line first before computing cooccurrence
      Xc = (X.T * X)
      ...
      

      【讨论】:

      • 如果我们想改变窗口大小怎么办。改变 ngram 也会改变单词对
      【解决方案5】:

      @titipata 我认为您的解决方案不是一个好的指标,因为我们对真实的共现和只是虚假的事件给予相同的重视。 例如,如果我有 5 个文本,并且单词 applehouse 以这种频率出现:

      text1: apple:10, "house":1

      text2: apple:10, "house":0

      text3: apple:10, "house":0

      text4: apple:10, "house":0

      text5: apple:10, "house":0

      我们要测量的共现是 10*1+10*0+10*0+10*0+10*0=10,但是只是假的。

      而且,在这另一个重要的情况下,如下所示:

      text1:苹果:1,“香蕉”:1

      text2: 苹果:1, "香蕉":1

      text3: 苹果:1, "香蕉":1

      text4: 苹果:1, "香蕉":1

      text5: 苹果:1, "香蕉":1

      我们只会得到 1*1+1*1+1*1+1*1=5共现,而事实上共现真的很重要。

      @Guiem Bosch 在这种情况下,仅当两个单词连续时才会测量共现。

      我建议使用@titipa 解决方案来计算矩阵:

      Xc = (Y.T * Y) # this is co-occurrence matrix in sparse csr format
      

      其中,使用矩阵 Y 代替 X,在大于 0 的位置使用 ones,在其他位置使用 zeros

      使用它,在第一个示例中,我们将拥有: 共现:1*1+1*0+1*0+1*0+1*0=1 在第二个例子中: 共现:1*1+1*1+1*1+1*1+1*0=5 这才是我们真正想要的。

      【讨论】:

      • 嗨@Federico Caccia,谢谢你的收获!我已经在我的解决方案末尾注明。
      【解决方案6】:

      您可以在CountVectorizerTfidfVectorizer 中使用ngram_range 参数

      代码示例:

      bigram_vectorizer = CountVectorizer(ngram_range=(2, 2)) # by saying 2,2 you are telling you only want pairs of 2 words
      

      如果您想明确说明要计算哪些词的共现,请使用vocabulary 参数,即:vocabulary = {'awesome unicorns':0, 'batman forever':1}

      http://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.CountVectorizer.html

      具有预定义字词共现的不言自明且可立即使用的代码。在这种情况下,我们正在跟踪 awesome unicornsbatman forever 的同时出现:

      from sklearn.feature_extraction.text import CountVectorizer
      import numpy as np
      samples = ['awesome unicorns are awesome','batman forever and ever','I love batman forever']
      bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), vocabulary = {'awesome unicorns':0, 'batman forever':1}) 
      co_occurrences = bigram_vectorizer.fit_transform(samples)
      print 'Printing sparse matrix:', co_occurrences
      print 'Printing dense matrix (cols are vocabulary keys 0-> "awesome unicorns", 1-> "batman forever")', co_occurrences.todense()
      sum_occ = np.sum(co_occurrences.todense(),axis=0)
      print 'Sum of word-word occurrences:', sum_occ
      print 'Pretty printig of co_occurrences count:', zip(bigram_vectorizer.get_feature_names(),np.array(sum_occ)[0].tolist())
      

      最终输出为('awesome unicorns', 1), ('batman forever', 2),与我们samples提供的数据完全一致。

      【讨论】:

        猜你喜欢
        • 2023-03-10
        • 2017-02-01
        • 2018-12-12
        • 2017-05-30
        • 2018-09-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-30
        相关资源
        最近更新 更多