我该如何在多个独立的文本中找到最常见的单词?

4

这是一个相对简单的问题,但我似乎无法解决它。 我有一个字符串,格式如下:

["category1",("data","data","data")]
["category2", ("data","data","data")]

我称不同类别的文章为“帖子”,我想从数据部分获取最常见的单词。所以我尝试了以下操作:

from nltk.tokenize import wordpunct_tokenize
from collections import defaultdict
freq_dict = defaultdict(int)

for cat, text2 in posts:
   tokens = wordpunct_tokenize(text2)
   for token in tokens:
       if token in freq_dict:
           freq_dict[token] += 1
       else:
           freq_dict[token] = 1
   top = sorted(freq_dict, key=freq_dict.get, reverse=True)
   top = top[:50]
   print top

然而,这将给我每个帖子中的顶部单词。

我需要一个通用的顶部单词列表。
但是,如果我在循环之外去掉“print top”,它只会给我最后一篇文章的结果。
有人有主意吗?


你想要统计所有元组中每个唯一单词的出现次数吗? - Janus Troelsen
wordpunct_tokenize是做什么的?如果我们能够执行您发布的代码,那么帮助您会更容易。它是否总是需要三元组,还是可以使用任意长度? - Janus Troelsen
wordpunct来自nltk包,用于对字符串进行分词。从nltk.tokenize导入wordpunct_tokeniz,并在问题中进行了更改。不,我只想要所有帖子中最常见的单词。 - Shifu
5
你可能想要查看Counter - soulcheck
似乎是使用defaultdict的一个用例。就像这个答案中所述。啊,@Nikolaas,请不要忽略提供所有信息,以便我们可以适当地给出建议,而不是留下半知不解的评论。 - kojiro
3
请使用更好的标题。你的问题不是“for loop,相当简单”,而是“如何在多个分开的文本中找到最常见的单词?” - Janus Troelsen
4个回答

3
这是一个范围问题。而且,您不需要初始化defaultdict的元素,因此这简化了您的代码:
请尝试像这样:
posts = [["category1",("data1 data2 data3")],["category2", ("data1 data3 data5")]]

from nltk.tokenize import wordpunct_tokenize
from collections import defaultdict
freq_dict = defaultdict(int)

for cat, text2 in posts:
   tokens = wordpunct_tokenize(text2)
   for token in tokens:
      freq_dict[token] += 1

top = sorted(freq_dict, key=freq_dict.get, reverse=True)
top = top[:50]
print top

这是预期的输出结果。
['data1', 'data3', 'data5', 'data2']

as a result.

If you really have something like

posts = [["category1",("data1","data2","data3")],["category2", ("data1","data3","data5")]]

作为输入,您不需要使用 wordpunct_tokenize(),因为输入数据已经被分词。接下来的代码将起作用:
posts = [["category1",("data1","data2","data3")],["category2", ("data1","data3","data5")]]

from collections import defaultdict
freq_dict = defaultdict(int)

for cat, tokens in posts:
   for token in tokens:
      freq_dict[token] += 1

top = sorted(freq_dict, key=freq_dict.get, reverse=True)
top = top[:50]
print top

它还输出了预期的结果:

['data1', 'data3', 'data5', 'data2']

实际上,我已经初始化了freq_dict,只是没有在我的帖子中写出来,我现在会进行编辑。 - Shifu
freq_dict 是一个默认字典,而且无论如何 freq_dict 不能是一个列表,因为 token 不是一个整数而是一个 Token 对象。因此,它不能是一个列表索引! - pradyunsg
@Nikolaas,我从列表中删除了有问题的行。 - likeitlikeit
@Nikolaas添加了更多关于可能适用的不同输入格式的信息。如果您确实拥有问题中所述的输入格式,请查看第二个列表,因为您根本不需要使用wordpunct_tokenize()。祝玩得愉快... - likeitlikeit

3

为什么不直接使用Counter

In [30]: from collections import Counter

In [31]: data=["category1",("data","data","data")]

In [32]: Counter(data[1])
Out[32]: Counter({'data': 3})

In [33]: Counter(data[1]).most_common()
Out[33]: [('data', 3)]

仍然没有展示如何在(分词后)最佳地链接文本并获取特定数量的最常见单词。请检查我的答案。 - Janus Troelsen
2
@Nikolaas:当然,我们已经这样做了。但是,如果你可以使用标准库中的计数器,那么编写自己的计数器就是不必要的复杂操作。最好的代码是没有代码 - Janus Troelsen

2
from itertools import chain
from collections import Counter
from nltk.tokenize import wordpunct_tokenize
texts=["a quick brown car", "a fast yellow rose", "a quick night rider", "a yellow officer"]
print Counter(chain.from_iterable(wordpunct_tokenize(x) for x in texts)).most_common(3)

输出:

[('a', 4), ('yellow', 2), ('quick', 2)]

正如您在Counter.most_common文档中所看到的,返回的列表是已排序的。

要在您的代码中使用,您可以执行以下操作:

texts = (x[1] for x in posts)

或者你可以这样做。
... wordpunct_tokenize(x[1]) for x in texts ...

如果您的帖子实际上是这样的:
posts=[("category1",["a quick brown car", "a fast yellow rose"]), ("category2",["a quick night rider", "a yellow officer"])]

您可以删除分类:
texts = list(chain.from_iterable(x[1] for x in posts))

您可以在此答案的片段中使用它。

(texts将是['一个快速的棕色汽车','一个快速的夜骑手','一个快速的黄玫瑰','一个黄色的警官'])


使用生成器表达式比列表推导更好。 - soulcheck
@soulcheck: 为什么?反正所有的都会被读取。我认为这样可以获得更好的空间局部性和更好的性能表现。 - Janus Troelsen
我想帖子可能相当大。创建一个集合只是为了迭代一次然后将其丢弃也没有意义。 - soulcheck
啊,你是指帖子,我以为你是指标记化的数据。 - Janus Troelsen
@codesparkle:主要是因为它更长,而且我认为“chain”很容易理解,但是“chain.from_iterable”就不那么直观了。无论如何,我已经改了。 :) - Janus Troelsen
显示剩余4条评论

1

只需更改您的代码以允许处理帖子,然后获取顶部单词:

from nltk.tokenize import wordpunct_tokenize
from collections import defaultdict

freq_dict = defaultdict(int)

for cat, text2 in posts:
   tokens = wordpunct_tokenize(text2)
   for token in tokens:
       freq_dict[token] += 1
# get top after all posts have been processed.
top = sorted(freq_dict, key=freq_dict.get, reverse=True)
top = top[:50]
print top

1
@Nikolaas:你为什么这样想? - Janus Troelsen
@Nikolaas,你能发布一下posts的值吗?如果是这样的话,我们看到输入会对验证输出有所帮助... - pradyunsg
@Janus 我用一些虚假数据试过了,它只返回最后一行。像我上面发布的那样,帖子看起来像["category"["data","data",...]]。我正在处理实际的博客文章,所以提供实际的字符串需要几页纸。 - Shifu
学弟,看来我判断得太快了,没有好好接手你的代码,我以为未缩进的freq_dict[token] += 1是一个错误。但实际上它是正确的。 现在它运行得非常好。 对此我深表歉意,谢谢! - Shifu
@Nikolaas:它创建正确的频率计数有什么问题吗?这不是你想要的吗? - Janus Troelsen
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接