【问题标题】:How to count the documents which contain a specific word?如何计算包含特定单词的文档?
【发布时间】:2014-05-12 05:38:30
【问题描述】:

假设我有这样的字典:

docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}

而我想做的就是计算“文档频率”:假设每个字典项都是一个文档,并且我有一个特定的单词,那么有多少文档包含该单词?

我看到很多帖子告诉我如何计算频率,但是如果"a" 在文档"alpha" 中出现两次,我只需要计数为1。所以"a" 的“频率”应该是1,"c" 应该是 2。

我知道我可以迭代整个文档字典,并在文档中查找单词时添加计数器。或者我可以先让每个文档中的单词唯一,然后将所有文档合并起来统计单词。

但我认为有更好的方法,更有效的方法。有什么想法吗?

顺便说一句,有什么办法可以保持字典的结构吗?在这个例子中,我想得到{"alpha": {'c': 2, 'b': 2, 'a': 1}, "bravo": {'c': 2, 'b': 2, 'd': 1}的结果

更新

如果这里我只有一个列表(类似[["a", "b", "c", "a", "b"], ["b", "c", "d", "c", "d"]]),我如何获得类似[[1, 2, 2, 0], [0, 2, 2, 1]] 的结果列表。

我不知道。重点是扩展每个列表并确保术语的顺序。想法?

【问题讨论】:

  • 我不明白你的目标。您想计算一个字符/单词在文档中出现的次数吗?但你不想实际算吗?为什么"a" 的计数为1"c" 的计数为2?这对我来说没有意义。
  • @Cyber​​ 我想计算有多少文档包含该词。在我的例子中,只有"alpha" 包含"a",所以它应该是1(尽管在"alpha" 中有两次),而"c""alpha""bravo" 中都有,所以它是2(虽然有总共 3 个"c"s)。

标签: python dictionary counter frequency tf-idf


【解决方案1】:

这不是特别的,很普通的方式。

from collections import defaultdict

docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
result = defaultdict(set)

for k, vlist in docDict.items():
    for v in vlist:
        result[v].add(k)

#Now the result looks like this.
#{'a': set(['alpha']), 'c': set(['alpha', 'bravo']), 'b': set(['alpha', 'bravo']), 'd': set(['bravo'])})

print dict(zip(result.keys(), map(lambda x:len(x), result.values())))
#{'a': 1, 'c': 2, 'b': 2, 'd': 1}

更新

另一种方式......只是数数。并改为使用迭代器。所以它比上面的代码更快。

from collections import defaultdict
def func3(docDict):
    result = defaultdict(int)
    for vlist in docDict.itervalues():
        for i in set(vlist):
            result[i] += 1
    return dict(result)

【讨论】:

  • 我不得不说这真的很神奇。处理3000多行70000字只用了2秒左右!至于保持字典的结构,我只是创建一个新字典并迭代原始字典以映射来自该计数器的结果。仍然非常快。
  • @Melkor:我不知道set 函数和for-loop 这么快。谢谢你告诉我。无论如何,我上传更快的一个。:)
【解决方案2】:

我会使用 collections.Counterset 的第二种方式。

>>> from collections import Counter
>>> sum((Counter(set(x)) for x in docDict.itervalues()), Counter())
Counter({'c': 2, 'b': 2, 'a': 1, 'd': 1})

更新 1:

>>> c = sum((Counter(set(x)) for x in docDict.itervalues()), Counter())
>>> {k: {k1:c[k1] for k1 in set(v)} for k, v in docDict.iteritems()}
{'alpha': {'a': 1, 'c': 2, 'b': 2}, 'bravo': {'c': 2, 'b': 2, 'd': 1}}

更新 2:

如果性能是一个问题,那么不要将Countersum 一起使用,这是另一种方法。请注意,与@user2931409 答案不同,我不会将一组单词保存在内存中只是为了获得它们的长度,所以这比他们的答案更内存效率,但比他们的答案稍慢。

result = Counter()
for v in docDict.itervalues():
    result.update(set(v))
return result

时序对比:

def func1():
    #http://stackoverflow.com/a/22787509/846892
    result = defaultdict(set)
    for k, vlist in docDict.items():
        for v in vlist:
            result[v].add(k)
    return dict(zip(result.keys(), map(lambda x:len(x), result.values())))

def func2():

    result = Counter()
    for v in docDict.itervalues():
        result.update(set(v))
    return result

In [94]: docDict = {''.join(random.choice(lis) for _ in xrange(8)): random.sample(lis, 25)
    ...:   for _ in xrange(70000)}

In [95]: %timeit func1(docDict)
1 loops, best of 3: 380 ms per loop

In [96]: %timeit func2(docDict)
1 loops, best of 3: 591 ms per loop

In [97]: docDict = {''.join(random.choice(lis) for _ in xrange(8)): random.sample(lis, 25)
    ...:   for _ in xrange(10**5)}

In [98]: %timeit func1(docDict)
1 loops, best of 3: 529 ms per loop

In [99]: %timeit func2(docDict)
1 loops, best of 3: 848 ms per loop

In [101]: func1(docDict) == func2(docDict)
Out[101]: True

【讨论】:

  • 谢谢!我从没想过它会如此紧凑和优雅!
  • 但是...有什么办法可以保持字典的结构吗?喜欢...{"alpha": {'c': 2, 'b': 2, 'a': 1}, "bravo": {'c': 2, 'b': 2, 'd': 1}}
  • @Melkor 检查更新的答案,你需要一个额外的循环。
  • 您好,我已经测试过了,但是运行速度很慢。当我处理超过 7 万字时,我花了将近 300 秒。不过@user2931409 说的方式真的很快。
  • @Melkor 我有另一个答案。
【解决方案3】:

您可以使用 set 来统一单个文档中的字符。然后只需Counter()他们。

from collections import Counter

docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}

result = reduce(lambda x, y: x + Counter(set(y)), docDict.itervalues(), Counter([]))

【讨论】:

    【解决方案4】:
    docDict = {"alpha": ["a", "b", "c", "a", "b"], "bravo": ["b", "c", "d", "c", "d"]}
    revDict = {v : sum(1 for l in docDict.values() if v in l)  
            for v in set(x for y in docDict.values() for x in y) }
    print revDict
    

    给予:

    {'a': 1, 'c': 2, 'b': 2, 'd': 1}
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 2011-08-29
      • 1970-01-01
      • 2017-03-19
      相关资源
      最近更新 更多