5

我想要一种有效的 Pythonic 方法来计算文本中的相邻单词对。高效,因为它需要很好地处理更大的数据集。

计数的方式也很重要。

考虑这个简化的例子:

words_list = "apple banana banana apple".split()

我可以使用以下方法创建相邻对:

word_pair_list = zip(words_list[:-1], words_list[1:])

然后我可以使用 Python 来计算它们

word_pair_ctr = collections.Counter(word_pair_list)

这给了我

(('apple', 'banana'), 1)
(('banana', 'banana'), 1)
(('banana', 'apple'), 1)

请注意'apple''apple'不是相邻对。

但我希望成对的顺序不计算在内。这意味着('apple', 'banana')and('banana', 'apple')应该被认为是相同的,并且计数应该是

(('apple', 'banana'), 2)
(('banana', 'banana'), 1)

我找不到一种不需要我访问单词列表中每个项目的 Pythonic 方式,这对于较大的文本来说效率很低。

我也很乐意使用常见的 scipy、numpy 和 pandas 作为库。

4

2 回答 2

2

有几种内置的解决方案。

映射word_pair_listfrozenset

word_pair_ctr = collections.Counter(map(frozenset, word_pair_list))

结果:

Counter({frozenset({'apple', 'banana'}): 2, frozenset({'banana'}): 1})

第二个集合可能看起来很奇怪,但这只是因为集合只包含一个相同的元素。检索仍然有效,即word_pair_ctr[frozenset(["banana", "banana"])]等于 1。

您需要使用frozenset 而不是普通集,因为普通集不可散列,因此不能是字典(或计数器)中的键。

在插入计数器之前对对进行排序:

word_pair_ctr = collections.Counter(map(lambda x: tuple(sorted(x)), word_pair_list))

结果如下所示:

Counter({('apple', 'banana'): 2, ('banana', 'banana'): 1})

虽然这可能看起来更好,但您必须确保以相同的方式访问计数,即word_pair_ctr[tuple(sorted([word1, word2]))],这可能比以前的解决方案更令人费解。

子类计数器

第三种选择是创建自己的计数器类来为您完成所有这些工作。

class BiDirectionalCounter(collections.Counter):
    def __init__(self, iterable):
        super().__init__(map(lambda x: tuple(sorted(x)), iterable))
    def __getitem__(self, items):
        return super().__getitem__(tuple(sorted(items)))

这似乎有效:

>>> BidirectionalCounter(word_pair_list)
BidirectionalCounter({('apple', 'banana'): 2, ('banana', 'banana'): 1})

但要真正工作,您需要实现所有相关的dunder方法,即__setitem__, __add__, __iadd__, ...

于 2019-01-22T13:19:19.700 回答
2

您可以使用官方文档(https://docs.python.org/3.8/library/itertools.html)中的pairwise函数的修改版本,以便逐对阅读您的列表,同时重新排序成员每对:

l = "apple banana banana apple".split()
def pairwise(iterable):
    """s -> (s0,s1), (s1,s2), (s2, s3), ..."""
    a, b = itertools.tee(iterable)
    next(b, None)
    return ((a, b) if a < b else (b, a) for a, b in zip(a, b))
>>> list(pairwise(l))
<class 'list'>: ['apple', 'banana', 'banana', 'apple']
>>> collections.Counter(pairwise(l))
Counter({('apple', 'banana'): 2, ('banana', 'banana'): 1})

希望这可以帮助!

于 2019-01-22T13:45:48.003 回答