web-dev-qa-db-ja.com

ファイル内で最も頻度の高いn個の単語を見つける

たとえば、テキストファイルで最も一般的な10個のWordを見つけたいと思います。まず、ソリューションをキーストローク用に最適化する必要があります(つまり、私の時間)。第二に、パフォーマンスのために。これが私がこれまでにトップ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 etc.プログラムで、(Word、numberOfOccurences)を辞書に格納して値をソートするか、MapReduceを使用できますが、キーストローク用に最適化します。

誤検知はありますか?もっと良い方法はありますか?

34
Lukasz Madon

sortがなく、不必要なcatがある場合を除いて、これは「Nの最も一般的なこと」を見つける最も一般的な方法です。

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

uniq -cの前にsortを入れない場合、おそらく多くの誤ったシングルトンワードを取得します。 uniqは一意の行の実行のみを行い、全体的な一意性は行いません。

編集:「ストップワード」というトリックを忘れました。英語のテキスト(申し訳ありませんが、ここでは北米の1か国語です)を表示している場合、「of」、「and」、「the」などの単語は、ほとんど常に上位2、3位を占めています。あなたはおそらくそれらを排除したいでしょう。 GNU Groffディストリビューションには、ストップワードのかなりまともなリストが含まれているeignという名前のファイルがあります。私のArchディストリビューションには/usr/share/groff/current/eignがありますが、古いUnixでは/usr/share/dict/eignまたは/usr/dict/eignも見られます。

次のようなストップワードを使用できます。

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"のように短いストップストップを含む単語の誤検出が回避されます。

50
Bruce Ediger

これはutf-8でうまく機能します:

$ sed -e 's/\s/\n/g' < test.txt | sort | uniq -c | sort -nr | head  -10
9

AWKを使ってみよう!

この関数は、提供されたファイルで出現する各Wordの頻度を降順でリストします。

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

7
Sheharyar

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
4
BlackCap

これは、1986年に共鳴した古典的な問題で、 Donald Knuthが高速なソリューションを実装した 8ページのプログラムでハッシュを試みて、彼の文芸的プログラミング技法を説明している一方で、ゴッドファーザーのDoug McIlroyがUnixパイプの場合、ワンライナーで応答しましたが、それほど速くはありませんでしたが、仕事は完了しました。

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

もちろん、McIlroyの解には時間の複雑さO(N log N)があります。ここで、Nは単語の総数です。より高速なソリューションがあります。例えば:

ここ は、時間の複雑さの上限がO((N + k)log k)のC++実装であり、通常–ほぼ線形です。

以下は、高速な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)

ここ は、Anders KaseorgによるRustの非常に高速なソリューションです。

CPU時間の比較(秒単位):

                                     bible32       bible256
Rust (prefix tree)                   0.632         5.284
C++ (prefix tree + heap)             4.838         38.587
Python (Counter)                     9.851         100.487
Sheharyar (AWK + sort)               30.071        251.301
McIlroy (tr + sort + uniq)           60.251        690.906

ノート:

  • bible32はそれ自体32回(135 MB)、bible256 – 256回(1.1 GB)連結された聖書です。
  • Pythonスクリプトの非線形の速度低下は、メモリ内でファイルを完全に処理するために発生するものであり、巨大なファイルのオーバーヘッドが大きくなっています。
  • ヒープを構築し、ヒープの最上部からn個の要素を選択できるUnixツールがあった場合、AWKソリューションは、ほぼO(N + Q log Q)であるが、ほぼ線形の時間の複雑さを実現できます。
3
Andriy Makukha

このようなものは、pythonを使用して機能するはずです。これは一般的に利用可能です:

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

これは、1行あたりのWordを想定しています。それ以上ある場合は、分割も簡単にする必要があります。

3
Reut Sharabani