Quero encontrar, digamos, as 10 palavras mais comuns em um arquivo de texto. Em primeiro lugar, a solução deve ser otimizada para pressionamentos de tecla (em outras palavras - meu tempo). Em segundo lugar, pelo desempenho. Aqui está o que tenho até agora para ficar entre os 10 primeiros:
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
Eu poderia fazer um programa java, python etc. onde armazeno (palavra, númeroOfOccurences) em um dicionário e classifico o valor ou poderia usar MapReduce, mas otimizo para pressionamentos de tecla.
Existem falsos positivos? Existe uma maneira melhor?
Responder1
Essa é praticamente a maneira mais comum de encontrar "N coisas mais comuns", exceto que você está faltando um sort
e tem um gratuito cat
:
tr -c '[:alnum:]' '[\n*]' < test.txt | sort | uniq -ci | sort -nr | head -10
Se você não colocar um sort
antes de, uniq -ci
provavelmente obterá muitas palavras falsas de singleton. uniq
apenas execuções únicas de linhas, não exclusividade geral.
Você pode querer usar um truque, "palavras de parada". Se você estiver olhando para um texto em inglês (desculpe, monolíngue norte-americano aqui), palavras como "de", "e", "o" quase sempre ocupam os dois ou três primeiros lugares. Você provavelmente deseja eliminá-los. A distribuição GNU Groff possui um arquivo nomeado eign
que contém uma lista bastante decente de palavras irrelevantes. Minha distro Arch tem /usr/share/groff/current/eign
, mas acho que também vi /usr/share/dict/eign
ou /usr/dict/eign
em Unixes antigos.
Você pode usar palavras irrelevantes como esta:
tr -c '[:alnum:]' '[\n*]' < test.txt |
fgrep -v -w -f -i /usr/share/groff/current/eign |
sort | uniq -ci | sort -nr | head -10
Meu palpite é que a maioria dos idiomas humanos precisa de "palavras de parada" semelhantes removidas das contagens de frequência de palavras significativas, mas não sei onde sugerir a obtenção de listas de palavras de parada de outros idiomas.
O -w
sinalizador ativado fgrep
permite a correspondência de palavras inteiras. Isso evita falsos positivos em palavras que contêm apenas palavras curtas, como "a" ou "i". O -i
sinalizador ativa uniq
e fgrep
ignora maiúsculas e minúsculas ao comparar palavras.
Responder2
Isso funciona melhor com utf-8:
$ sed -e 's/\s/\n/g' < test.txt | sort | uniq -c | sort -nr | head -10
Responder3
Vamos usar o AWK!
Esta função lista a frequência de cada palavra que ocorre no arquivo fornecido em ordem decrescente:
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
}
Você pode chamá-lo em seu arquivo assim:
$ cat your_file.txt | wordfrequency
e para as 10 principais palavras:
$ cat your_file.txt | wordfrequency | head -10
Fonte:Ruby AWK
Responder4
Vamos usar Haskell!
Isto está se transformando em uma guerra linguística, não é?
import Data.List
import Data.Ord
main = interact $ (=<<) (\x -> show (length x) ++ " - " ++ head x ++ "\n")
. sortBy (flip $ comparing length)
. group . sort
. words
Uso:
cat input | wordfreq
Alternativamente:
cat input | wordfreq | head -10