0

我关于如何从字符串构建字典的问题比从字符串创建字典更倾向于语言/NLP

给定一个字符串句子列表,有没有一种更简单的方法来构建一个唯一的字典,然后对字符串句子进行向量化?我知道有外部库可以这样做,gensim但我想避免它们。我一直这样做:

from itertools import chain

def getKey(dic, value):
  return [k for k,v in sorted(dic.items()) if v == value]

# Vectorize will return a list of tuples and each tuple is made up of 
# (<position of word in dictionar>,<number of times it occurs in sentence>)
def vectorize(sentence, dictionary): # is there simpler way to do this?
  vector = []
  for word in sentence.split():
    word_count = sentence.lower().split().count(word)
    dic_pos = getKey(dictionary, word)[0]
    vector.append((dic_pos,word_count))
  return vector

s1 = "this is is a foo"
s2 = "this is a a bar"
s3 = "that 's a foobar"

uniq = list(set(chain(" ".join([s1,s2,s3]).split()))) # is there simpler way for this?
dictionary = {}
for i in range(len(uniq)): # can this be done with dict(list_comprehension)?
  dictionary[i] = uniq[i]

v1 = vectorize(s1, dictionary)
v2 = vectorize(s2, dictionary)
v3 = vectorize(s3, dictionary)

print v1
print v2
print v3
4

4 回答 4

3

这里:

from itertools import chain, count

s1 = "this is is a foo"
s2 = "this is a a bar"
s3 = "that 's a foobar"

# convert each sentence into a list of words, because the lists
# will be used twice, to build the dictionary and to vectorize
w1, w2, w3 = all_ws = [s.split() for s in [s1, s2, s3]]

# chain the lists and turn into a set, and then a list, of unique words
index_to_word = list(set(chain(*all_ws)))

# build the inverse mapping of index_to_word, by pairing it with a counter
word_to_index = dict(zip(index_to_word, count()))

# create the vectors of word indices and of word count for each sentence
v1 = [(word_to_index[word], w1.count(word)) for word in w1]
v2 = [(word_to_index[word], w2.count(word)) for word in w2]
v3 = [(word_to_index[word], w3.count(word)) for word in w3]

print v1
print v2
print v3

要记住的事情:

  • 字典只能从键到值;如果你需要做相反的事情,创建(并保持更新)两个字典,一个是另一个的逆映射,就像我上面做的那样;
  • 如果您需要一个键是连续整数的字典,只需使用一个列表(感谢 Jeff);
  • 永远不要两次计算同一件事!(参见句子的 split() 版本)如果您以后需要它,请将其保存在变量中;
  • 尽可能使用列表推导,以提高性能、简洁性和可读性。
于 2013-03-14T00:19:41.497 回答
1

如果您想计算一个单词在句子中出现的次数,请使用collections.Counter

您的代码存在问题:

uniq = list(set(chain(" ".join([s1,s2,s3]).split()))) # is there simpler way for this?
dictionary = {}
for i in range(len(uniq)): # can this be done with dict(list_comprehension)?
  dictionary[i] = uniq[i]

以上部分所做的只是创建一个由任意数字索引的字典(来自迭代set没有索引概念的 a)。然后使用上面的字典访问

def getKey(dic, value):
  return [k for k,v in sorted(dic.items()) if v == value]

此功能也完全忽略了 dict 的精神:您通过键而不是值进行查找。

另外,这个想法vectorize也不清楚。你想通过这个功能实现什么?你要求一个更简单的版本vectorize,但没有告诉我们它的作用。

于 2013-03-13T23:58:01.507 回答
1

您的代码中有多个问题,所以让我们一一回答。


uniq = list(set(chain(" ".join([s1,s2,s3]).split()))) # is there simpler way for this?

一方面,它可能在概念上更简单(尽管同样冗长)split()独立的字符串,而不是将它们连接在一起然后拆分结果。

uniq = list(set(chain(*map(str.split, (s1, s2, s3))))

除此之外:看起来你总是使用单词列表,而不是实际的句子,所以你在多个地方分裂。为什么不一次把它们全部分开,在顶部呢?

同时,与其明确地传递s1,s2s3, 为什么不把它们放在一个集合中呢?您也可以将结果粘贴到集合中。

所以:

sentences = (s1, s2, s3)
wordlists = [sentence.split() for sentence in sentences]

uniq = list(set(chain.from_iterable(wordlists)))

# ...

vectors = [vectorize(sentence, dictionary) for sentence in sentences]
for vector in vectors:
    print vector

dictionary = {}
for i in range(len(uniq)): # can this be done with dict(list_comprehension)?
  dictionary[i] = uniq[i]

你可以像dict()在列表推导上那样做——但更简单的是,使用字典推导。而且,当您使用它时,请使用enumerate而不是for i in range(len(uniq))位。

dictionary = {idx: word for (idx, word) in enumerate(uniq)}

这取代了上面的整个# ...部分。


同时,如果您想要反向字典查找,这不是这样做的方法:

def getKey(dic, value):
    return [k for k,v in sorted(dic.items()) if v == value]

相反,创建一个逆字典,将值映射到键列表。

def invert_dict(dic):
    d = defaultdict(list)
    for k, v in dic.items():
        d[v].append(k)
    return d

然后,而不是您的getKey函数,只需在倒置的字典中进行正常查找。

如果您需要交替修改和查找,您可能需要某种双向字典,它可以管理自己的逆字典。在 ActiveState 上有很多这样的东西的食谱,在 PyPI 上可能有一些模块,但自己构建并不难。无论如何,你在这里似乎不需要它。


最后,还有你的vectorize功能。

首先要做的是取一个词表而不是一个句子来拆分,如上所述。

并且没有理由在lower;之后重新拆分句子。只需在单词列表上使用地图或生成器表达式。

lower实际上,当您的字典是根据原始案例版本构建的时,我不确定您为什么要在这里做。我猜这是一个错误,您lower在构建字典时也想做。这就是在一个容易找到的地方预先制作单词列表的好处之一:你只需要改变那一行:

wordlists = [sentence.lower().split() for sentence in sentences]

现在你已经有点简单了:

def vectorize(wordlist, dictionary):
    vector = []
    for word in wordlist:
        word_count = wordlist.count(word)
        dic_pos = getKey(dictionary, word)[0]
        vector.append((dic_pos,word_count))
    return vector

同时,您可能会认识到这vector = []… for word in wordlist… vector.append正是列表推导的用途。但是如何将三行代码变成一个列表推导式呢?简单:将其重构为一个函数。所以:

def vectorize(wordlist, dictionary):
    def vectorize_word(word):
        word_count = wordlist.count(word)
        dic_pos = getKey(dictionary, word)[0]
        return (dic_pos,word_count)
    return [vectorize_word(word) for word in wordlist]
于 2013-03-14T00:07:45.330 回答
0

好吧,看起来你想要:

  • 返回每个标记的位置值的字典。
  • 在一组中找到令牌的次数的计数。

你可以:

import bisect

uniq.sort() #Sort it since order didn't seem to matter

def getPosition(value):
    position = bisect.bisect_left(uniq, value) #Do a log(n) query
    if uniq[position] != value:
        raise IndexError

要在 O(n) 时间内搜索,您可以改为创建您的设置并使用顺序键迭代地插入每个值。这在内存上的效率要低得多,但它通过哈希提供了 O(n) 搜索......并且 Tobia 在我编写时发布了一个很好的代码示例,所以请参阅那个答案。

于 2013-03-14T00:24:56.440 回答