Python对称词矩阵使用nltk

6
我正在尝试从文本文档中创建对称的单词矩阵。例如:文本="Barbara is good. Barbara is friends with Benny. Benny is bad." 我使用nltk对文本进行了分词。现在我想要计算其他单词在同一句子中出现的次数。从上面的文本中,我想要创建下面的矩阵:
        Barbara good    friends Benny   bad
Barbara 2   1   1   1   0
good    1   1   0   0   0
friends 1   0   1   1   0
Benny   1   0   1   2   1
bad     0   0   1   1   1

注意,对角线是单词的频率。由于Barbara在句子中出现的次数与Barbara的数量相同,因此希望不会过多计算,但如果代码变得过于复杂,也不是什么大问题。

什么是问题? - Brian Cain
如何从文本创建一个矩阵? - mumpy
2个回答

8

首先我们对文本进行分词,遍历每个句子,遍历每个句子中所有单词的两两组合,并将计数存储在一个嵌套的dict中:

from nltk.tokenize import word_tokenize, sent_tokenize
from collections import defaultdict
import numpy as np
text = "Barbara is good. Barbara is friends with Benny. Benny is bad."

sparse_matrix = defaultdict(lambda: defaultdict(lambda: 0))

for sent in sent_tokenize(text):
    words = word_tokenize(sent)
    for word1 in words:
        for word2 in words:
            sparse_matrix[word1][word2]+=1

print sparse_matrix
>> defaultdict(<function <lambda> at 0x7f46bc3587d0>, {
'good': defaultdict(<function <lambda> at 0x3504320>, 
    {'is': 1, 'good': 1, 'Barbara': 1, '.': 1}), 
'friends': defaultdict(<function <lambda> at 0x3504410>, 
    {'friends': 1, 'is': 1, 'Benny': 1, '.': 1, 'Barbara': 1, 'with': 1}), etc..

这实际上类似于矩阵,我们可以索引 sparse_matrix['good']['Barbara'] 并获得数字 1,并索引 sparse_matrix['bad']['Barbara'] 并获得数字 0,但我们实际上没有存储任何从未共现的单词计数,0 只是在您请求时由 defaultdict 生成的。这在处理此类问题时可以节省大量内存。如果我们需要某种线性代数或其他计算原因的密集矩阵,可以像这样获取它:
lexicon_size=len(sparse_matrix)
def mod_hash(x, m):
    return hash(x) % m
dense_matrix = np.zeros((lexicon_size, lexicon_size))

for k in sparse_matrix.iterkeys():
    for k2 in sparse_matrix[k].iterkeys():
        dense_matrix[mod_hash(k, lexicon_size)][mod_hash(k2, lexicon_size)] = \
            sparse_matrix[k][k2]

print dense_matrix
>>
[[ 0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  1.  1.  1.  1.  0.  1.]
 [ 0.  0.  1.  1.  1.  0.  0.  1.]
 [ 0.  0.  1.  1.  1.  1.  0.  1.]
 [ 0.  0.  1.  0.  1.  2.  0.  2.]
 [ 0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  1.  1.  1.  2.  0.  3.]]

我建议查看http://docs.scipy.org/doc/scipy/reference/sparse.html,了解其他处理矩阵稀疏性的方法。

非常感谢你!我也很感激你提供的关于稀疏矩阵的链接。干杯! - mumpy

3
我会先设置类似以下的内容。可能需要添加某种令牌化;但对于您的示例,不需要。
text = """Barbara is good. Barbara is friends with Benny. Benny is bad."""
allwords = text.replace('.','').split(' ')
word_to_index = {}
index_to_word = {}
index = 0
for word in allwords:
    if word not in word_to_index:
         word_to_index[word] = index
         index_to_word[index] = word
         index += 1
word_count = index

>>> index_to_word
{0: 'Barbara',
 1: 'is',
 2: 'good',
 3: 'friends',
 4: 'with',
 5: 'Benny',
 6: 'bad'}

>>> word_to_index
{'Barbara': 0,
 'Benny': 5,
 'bad': 6,
 'friends': 3,
 'good': 2,
 'is': 1,
 'with': 4}

然后声明一个适当大小的矩阵(word_count x word_count);可以使用numpy来实现,例如:

import numpy
matrix = numpy.zeros((word_count, word_count))

或者只是一个嵌套列表:
matrix = [None,]*word_count
for i in range(word_count):
    matrix[i] = [0,]*word_count

请注意,这是一个棘手的问题,像matrix = [[0]*word_count]*word_count这样的代码是不行的,因为它会生成一个包含7个指向同一内部数组的引用列表(例如,如果你尝试该代码,然后执行matrix[0][1] = 1,你会发现matrix[1][1]matrix[2][1]等也会变成1)。
接下来,你只需要遍历你的句子即可。
sentences = text.split('.')
for sent in sentences:
   for word1 in sent.split(' '):
       if word1 not in word_to_index:
           continue
       for word2 in sent.split(' '):
           if word2 not in word_to_index:
               continue
           matrix[word_to_index[word1]][word_to_index[word2]] += 1

然后你得到:
>>> matrix

[[2, 2, 1, 1, 1, 1, 0],
 [2, 3, 1, 1, 1, 2, 1],
 [1, 1, 1, 0, 0, 0, 0],
 [1, 1, 0, 1, 1, 1, 0],
 [1, 1, 0, 1, 1, 1, 0],
 [1, 2, 0, 1, 1, 2, 1],
 [0, 1, 0, 0, 0, 1, 1]]

或者,如果您想知道“Benny”和“bad”这两个单词的频率,可以使用matrix[word_to_index['Benny']][word_to_index['bad']]查询。


我希望我能选择两个答案 - 你们两个的回答对我的分析都非常有帮助。干杯! - mumpy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接