Я хочу найти, скажем, 10 самых распространенных слов в текстовом файле. Во-первых, решение должно быть оптимизировано для нажатий клавиш (другими словами - моего времени). Во-вторых, для производительности. Вот что у меня есть на данный момент для получения топ-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 и т. д., в которой я бы сохранял (word, numberOfOccurences) в словаре и сортировал бы значения, или я мог бы использовать MapReduce, но я бы оптимизировал нажатия клавиш.
Есть ли ложные срабатывания? Есть ли лучший способ?
решение1
Это, по сути, самый распространенный способ найти «N самых распространенных вещей», за исключением того, что вы пропустили sort
, и у вас есть бесполезный cat
:
tr -c '[:alnum:]' '[\n*]' < test.txt | sort | uniq -ci | sort -nr | head -10
Если вы не поставите sort
перед , то, uniq -ci
скорее всего, получите много ложных одиночных слов. uniq
проверяет только уникальные серии строк, а не общую уникальность.
Вы можете использовать трюк, «стоп-слова». Если вы смотрите на английский текст (извините, здесь одноязычный североамериканский), такие слова, как «of», «and», «the» почти всегда занимают первые два-три места. Вероятно, вы захотите их исключить. В дистрибутиве GNU Groff есть файл с именем, eign
который содержит довольно приличный список стоп-слов. В моем дистрибутиве Arch есть /usr/share/groff/current/eign
, но я думаю, что я также видел /usr/share/dict/eign
или /usr/dict/eign
в старых Unix.
Вы можете использовать такие стоп-слова:
tr -c '[:alnum:]' '[\n*]' < test.txt |
fgrep -v -w -f -i /usr/share/groff/current/eign |
sort | uniq -ci | sort -nr | head -10
Я предполагаю, что большинству человеческих языков необходимо удалить подобные «стоп-слова» из подсчетов частоты употребления значимых слов, но я не знаю, где можно порекомендовать получить списки стоп-слов для других языков.
Флаг -w
on fgrep
включает сопоставление целых слов. Это позволяет избежать ложных срабатываний для слов, которые просто содержат короткие остановки, например, "a" или "i". Флаг -i
on uniq
и fgrep
игнорирует регистр при сравнении слов.
решение2
Это лучше работает с utf-8:
$ sed -e 's/\s/\n/g' < test.txt | sort | uniq -c | sort -nr | head -10
решение3
Давайте использовать 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
решение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