高效 Python 词对共现计数?
Efficient Python for word pair co-occurrence counting?
我想要一种高效的 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')
和 ('banana', 'apple')
应该被认为是相同的,计数应该是
(('apple', 'banana'), 2)
(('banana', 'banana'), 1)
我找不到不需要我访问单词列表中的每个项目的 Pythonic 方法,这对于较大的文本来说效率很低。
我也很高兴将常见的 scipy、numpy 和 pandas 用作库。
有几个内置的解决方案。
将 word_pair_list
映射到 frozenset
:
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计数器
第三种选择是制作自己的计数器class,它会为您完成所有这些工作。
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__
, ...
您可以使用官方文档 (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})
希望对您有所帮助!
我想要一种高效的 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')
和 ('banana', 'apple')
应该被认为是相同的,计数应该是
(('apple', 'banana'), 2)
(('banana', 'banana'), 1)
我找不到不需要我访问单词列表中的每个项目的 Pythonic 方法,这对于较大的文本来说效率很低。
我也很高兴将常见的 scipy、numpy 和 pandas 用作库。
有几个内置的解决方案。
将 word_pair_list
映射到 frozenset
:
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计数器
第三种选择是制作自己的计数器class,它会为您完成所有这些工作。
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__
, ...
您可以使用官方文档 (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})
希望对您有所帮助!