5

我想在大量文本中生成一个最不常用词的有序列表,其中最不常用的词首先出现,以及一个指示它在文本中出现次数的值。

我从一些在线期刊文章中抓取文本,然后简单地分配和拆分;

article_one = """ large body of text """.split() 
=> ("large","body", "of", "text")

似乎正则表达式适合接下来的步骤,但是对于编程来说,我并不精通 - 如果最好的答案包括正则表达式,有人可以指出我除了 pydoc 之外的一个好的正则表达式教程吗?

4

5 回答 5

4

带有 defaultdict 的更短/更简单的版本怎么样,Counter 很好,但需要 Python 2.7,这适用于 2.5 及更高版本:)

import collections

counter = collections.defaultdict(int)
article_one = """ large body of text """

for word in article_one.split():
    counter[word] += 1

print sorted(counter.iteritems(), key=lambda x: x[::-1])
于 2013-01-31T01:42:21.190 回答
3

查找列表中最不常见的元素。根据Collections 模块中的 Counter 类

c.most_common()[:-n-1:-1]       # n least common elements

所以列表中最不常见元素的代码是

from collections import Counter
Counter( mylist ).most_common()[:-2:-1]

两个最不常见的元素是

from collections import Counter
Counter( mylist ).most_common()[:-3:-1]

于 2014-05-08T15:07:23.407 回答
1

这使用了稍微不同的方法,但它似乎适合您的需求。使用此答案中的代码。

#!/usr/bin/env python
import operator
import string

article_one = """A, a b, a b c, a b c d, a b c d efg.""".split()
wordbank = {}

for word in article_one:
    # Strip word of punctuation and capitalization
    word = word.lower().strip(string.punctuation)
    if word not in wordbank:
        # Create a new dict key if necessary
        wordbank[word] = 1
    else:
        # Otherwise, increment the existing key's value
        wordbank[word] += 1

# Sort dict by value
sortedwords = sorted(wordbank.iteritems(), key=operator.itemgetter(1))

for word in sortedwords:
    print word[1], word[0]

输出:

1 efg
2 d
3 c
4 b
5 a

在 Python >= 2.4 和 Python 3+ 中工作,如果您print将底部的语句括起来并更改iteritemsitems.

于 2013-01-31T01:40:08.553 回答
0

如果您需要固定数量的最不常见单词,例如 10 个最不常见的单词,您可能需要使用计数器dict和 a的解决方案heapq,如 sotapme 的回答(与 WoLpH 的建议)或 WoLpH 的回答所建议的那样:

wordcounter = collections.Counter(article_one)
leastcommon = word counter.nsmallest(10)

但是,如果您需要数量不限的单词,例如,出现次数少于 5 次的所有单词,一次运行可能是 6 个,下一次可能是 69105,那么您最好只对列表进行排序:

wordcounter = collections.Counter(article_one)
allwords = sorted(wordcounter.items(), key=operator.itemgetter(1))
leastcommon = itertools.takewhile(lambda x: x[1] < 5, allwords)

排序比堆化花费更长的时间,但是使用 a 提取前 M 个元素list比 a快得多heap。从算法上讲,差异只是一些log N因素,所以常数在这里很重要。所以最好的办法就是测试。

在 pastebin获取我的代码,以及cat reut2* >reut2.sgmReuters-21578语料库上制作的文件(没有对其进行处理以提取文本,因此这对于严肃的工作显然不是很好,但对于基准测试应该没问题,因为没有SGML 标签将是最不常见的……):

$ python leastwords.py reut2.sgm # Apple 2.7.2 64-bit
heap: 32.5963380337
sort: 22.9287009239
$ python3 leastwords.py reut2.sgm # python.org 3.3.0 64-bit
heap: 32.47026552911848
sort: 25.855643508024514
$ pypy leastwords.py reut2.sgm # 1.9.0/2.7.2 64-bit
heap: 23.95291996
sort: 16.1843900681

我尝试了各种方法来加速它们中的每一个(包括:takewhile围绕一个 genexp 而不是yield堆版本中的循环,弹出乐观批次nsmallest并丢弃任何多余的,制作一个list并排序,装饰-排序-取消装饰而不是一个键,partial而不是lambda等),但它们都没有超过 5% 的改进(有些使事情明显变慢)。

无论如何,这些比我预期的更接近,所以我可能会选择更简单、更易读的那个。但我认为 sort 也比 heap 好,所以……</p>

再一次:如果你只需要 N 最不常见的,对于合理的 N,我愿意打赌甚至不测试堆实现会赢。

于 2013-01-31T02:52:07.463 回答
0

来自母舰的现成答案。

# From the official documentation ->>
>>> # Tally occurrences of words in a list
>>> cnt = Counter()
>>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
...     cnt[word] += 1
>>> cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
## ^^^^--- from the standard documentation.

>>> # Find the ten most common words in Hamlet
>>> import re
>>> words = re.findall('\w+', open('hamlet.txt').read().lower())
>>> Counter(words).most_common(10)
[('the', 1143), ('and', 966), ('to', 762), ('of', 669), ('i', 631),
 ('you', 554),  ('a', 546), ('my', 514), ('hamlet', 471), ('in', 451)]

>>> def least_common(adict, n=None):
.....:       if n is None:
.....:               return sorted(adict.iteritems(), key=itemgetter(1), reverse=False)
.....:       return heapq.nsmallest(n, adict.iteritems(), key=itemgetter(1))

显然适应套件:D

于 2013-01-31T01:34:37.220 回答