在文件中找到n个最常用的单词


34

我想在一个文本文件中找到10个最常见的单词。首先,应该针对按键优化解决方案(换句话说,就是我的时间)。其次,为了表现。这是我到目前为止获得前十名的东西:

cat test.txt | tr -c '[:alnum:]' '[\n*]' | uniq -c | sort -nr | head  -10
  6 k
  2 g
  2 e
  2 a
  1 r
  1 k22
  1 k
  1 f
  1 eeeeeeeeeeeeeeeeeeeee
  1 d

我可以制作一个Java,Python等程序,在字典中存储(单词,numberOfOccurences)并将其排序,也可以使用MapReduce,但我会优化按键操作。

有误报吗?有没有更好的办法?


你为什么最后要加-10?:P
ANU

Answers:


47

这几乎是查找“ N个最常见的事物”的最常见方法,除了您缺少sort,而且您有一个免费的信息cat

tr -c '[:alnum:]' '[\n*]' < test.txt | sort | uniq -c | sort -nr | head  -10

如果您不输入sort,则uniq -c 可能会收到很多错误的单例单词。 uniq只执行唯一的行,而不是整体唯一性。

编辑:我忘了一个把戏,“停用词”。如果您正在查看英文文本(很抱歉,这里是北美单语),“ of”,“ and”和“ the”之类的词几乎总是排在前两位或三位。您可能想消除它们。GNU Groff发行版中有一个名为的文件eign,其中包含相当不错的停用词列表。我的拱门发行了/usr/share/groff/current/eign,但我想我也看到/usr/share/dict/eign/usr/dict/eign旧的Unix。

您可以使用以下停用词:

tr -c '[:alnum:]' '[\n*]' < test.txt |
fgrep -v -w -f /usr/share/groff/current/eign |
sort | uniq -c | sort -nr | head  -10

我的猜测是,大多数人类语言都需要从有意义的词频计数中删除类似的“停用词”,但我不知道在哪里建议其他语言停用词列表。

编辑: fgrep应使用-w命令,该命令启用全字匹配。这样可以避免对仅包含短时停止词(例如“ a”或“ i”)的单词产生误报。


2
是否会cat增加一些显着的性能开销?我喜欢管道语法。'[\ n *]'中的*是做什么的?
Lukasz Madon

1
如果您喜欢“ cat test.txt”,则一定要使用它。我在某个地方读过一篇文章,其中丹尼斯·里奇(Dennis Ritchie)说过,“ cat something | somethingelse”语法被更广泛地使用,并且“ <something”语法是一个错误的东西,因为它是单一目的。
Bruce Ediger 2012年

如果我想在find输出中找到最常用的目录名怎么办?也就是说,将单词分割开,/而不是空白字符和类似字符。
erb

1
@erb-您可能会执行以下操作:find somewhere optoins | tr '/' '\n' | sort | uniq -c | sort -k1.1nr | head -10
Bruce Ediger

1
@erb-作为一个问题而不是在评论中提出。您将有更多空间来提出问题,以便获得所需的答案。给出示例输入和所需的输出。您可能会因提出一个好的问题而获得一些声望点,并且与我在评论中所提供的相比,我给与更好的答案也将获得积分。
Bruce Ediger


7

让我们使用AWK!

此函数以降序列出在提供的文件中出现的每个单词的频率:

function wordfrequency() {
  awk '
     BEGIN { FS="[^a-zA-Z]+" } {
         for (i=1; i<=NF; i++) {
             word = tolower($i)
             words[word]++
         }
     }
     END {
         for (w in words)
              printf("%3d %s\n", words[w], w)
     } ' | sort -rn
}

您可以像这样在文件上调用它:

$ cat your_file.txt | wordfrequency

对于前10个字:

$ cat your_file.txt | wordfrequency | head -10

资料来源:AWK-ward Ruby


4

让我们使用Haskell!

这正在变成一场语言大战,不是吗?

import Data.List
import Data.Ord

main = interact $ (=<<) (\x -> show (length x) ++ " - " ++ head x ++ "\n")
                . sortBy (flip $ comparing length)
                . group . sort
                . words

用法:

cat input | wordfreq

或者:

cat input | wordfreq | head -10

忽略大小写的修改版本:pastebin.com/57T5B6BY
Axel Latvala

工作比经典慢得多sort | uniq -c | sort -nr
Andriy Makukha

@AndriyMakukha的瓶颈是字符串是Haskell中字符的链接列表。我们可以通过切换到TextByteString代替来获得类似C的速度,就像将其导入合格并在限定符前面加上函数一样简单。
BlackCap

pastebin.com/QtJjQwT9版本明显更快,为提高可读性而写
BlackCap

3

像这样的东西应该可以使用通常可用的python来工作:

cat slowest-names.log | python -c 'import collections, sys; print collections.Counter(sys.stdin);'

假设每行一个字。如果还有更多,拆分也应该很容易。


python3和更好的输出cat README.md | python -c 'import collections, sys, pprint; pprint.pprint(collections.Counter(sys.stdin));'
Lukasz Madon

1

这是一个经典的问题,在1986年引起了共鸣,当时Donald Knuth在一个长达8页的程序中通过哈希尝试实现了一种快速解决方案,以说明他的识字编程技术,而Unix管道的教父Doug McIlroy则回应了单线,虽然没有那么快,但是完成了工作:

tr -cs A-Za-z '\n' | tr A-Z a-z | sort | uniq -c | sort -rn | sed 10q

当然,McIlroy的解决方案具有时间复杂度O(N log N),其中N是单词总数。有更快的解决方案。例如:

是一个C ++实现,通常具有上限时间复杂度O((N + k)log k)-接近线性。

下面是使用哈希字典和堆的快速Python实现,其时间复杂度为O(N + k log Q),其中Q是许多唯一的单词:

import collections, re, sys

filename = sys.argv[1]
k = int(sys.argv[2]) if len(sys.argv)>2 else 10

text = open(filename).read()
counts = collections.Counter(re.findall('[a-z]+', text.lower()))
for i, w in counts.most_common(k):
    print(i, w)

CPU时间比较(以秒为单位):

                                     bible32       bible256
C++ (prefix tree + heap)             5.659         44.730  
Python (Counter)                     10.314        100.487
Sheharyar (AWK + sort)               30.864        251.301
McIlroy (tr + sort + uniq)           60.531        690.906

笔记:

  • bible32是圣经与自身的串联32次(135 MB),分别与bible256 – 256次(1.1 GB)连接。
  • Python脚本的非线性减慢完全是由于它完全在内存中处理文件这一事实造成的,因此大文件的开销越来越大。
  • 如果有一个Unix工具可以构造一个堆并从堆顶部选择n个元素,则AWK解决方案可以实现近乎线性的时间复杂度,而目前为O(N + Q log Q)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.