たとえば、テキストファイルで最も一般的な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を使用できますが、キーストローク用に最適化します。
誤検知はありますか?もっと良い方法はありますか?
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"のように短いストップストップを含む単語の誤検出が回避されます。
これはutf-8でうまく機能します:
$ sed -e 's/\s/\n/g' < test.txt | sort | uniq -c | sort -nr | head -10
この関数は、提供されたファイルで出現する各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
これは言語戦争になりつつありますね。
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
これは、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
ノート:
このようなものは、pythonを使用して機能するはずです。これは一般的に利用可能です:
cat slowest-names.log | python -c 'import collections, sys; print collections.Counter(sys.stdin);'
これは、1行あたりのWordを想定しています。それ以上ある場合は、分割も簡単にする必要があります。