Quero encontrar, digamos, 10 palavras mais comuns em um arquivo de texto. Em primeiro lugar, a solução deve ser otimizada para pressionar as teclas (em outras palavras - o meu tempo). Em segundo lugar, pelo desempenho. Aqui está o que eu tenho até agora para obter o top 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
Eu poderia criar um programa em java, python etc. onde armazeno (palavra, numberOfOccurences) em um dicionário e classifico o valor ou posso usar o MapReduce, mas otimizo para pressionar as teclas.
Existem falsos positivos? Existe uma maneira melhor?
command-line
shell-script
Lukasz Madon
fonte
fonte
Respostas:
Essa é a maneira mais comum de encontrar "N coisas mais comuns", exceto que você está perdendo um
sort
e recebe um brindecat
:Se você não escrever
sort
antes,uniq -c
provavelmente terá muitas palavras falsas em singleton.uniq
somente executa linhas únicas, não a exclusividade geral.EDIT: esqueci um truque, "pare palavras". Se você estiver visualizando o texto em inglês (desculpe, monolíngue na América do Norte aqui), palavras como "of", "e", "the" quase sempre ocupam os dois ou três primeiros lugares. Você provavelmente deseja eliminá-los. A distribuição GNU Groff tem um arquivo nomeado
eign
, que contém uma lista bastante decente de palavras de parada. Minha distro Arch tem/usr/share/groff/current/eign
, mas acho que também vi/usr/share/dict/eign
ou/usr/dict/eign
nos Unixes antigos.Você pode usar palavras de parada assim:
Meu palpite é que a maioria das línguas humanas precisa de "palavras de parada" semelhantes removidas das contagens significativas de frequência de palavras, mas não sei onde sugerir a obtenção de outras línguas nas listas de palavras de parada.
EDIT:
fgrep
deve usar o-w
comando, que permite a correspondência de palavras inteiras. Isso evita falsos positivos em palavras que apenas contêm trabalhos de parada curta, como "a" ou "i".fonte
cat
adicionar alguma sobrecarga de desempenho significativa? Eu gosto da sintaxe do pipe. O que o * em '[\ n *]' faz?find
saída? Ou seja, divida as palavras em/
vez de caracteres de espaço em branco e similares.find somewhere optoins | tr '/' '\n' | sort | uniq -c | sort -k1.1nr | head -10
Isso funciona melhor com o utf-8:
fonte
Vamos usar o AWK!
Esta função lista a frequência de cada palavra que ocorre no arquivo fornecido em ordem decrescente:
Você pode chamá-lo em seu arquivo assim:
e para as 10 principais palavras:
Fonte: Ruby da ala AWK
fonte
Vamos usar o Haskell!
Isso está se transformando em uma guerra de idiomas, não é?
Uso:
Alternativamente:
fonte
sort | uniq -c | sort -nr
.Text
ou emByteString
vez disso, o que é tão simples quanto importar qualificadas e prefixar as funções com o qualificador.Algo assim deve funcionar usando python, que é comumente disponível:
Isso assume a palavra por linha. Se houver mais, a divisão também deve ser fácil.
fonte
cat README.md | python -c 'import collections, sys, pprint; pprint.pprint(collections.Counter(sys.stdin));'
Esse é um problema clássico que teve alguma ressonância em 1986, quando Donald Knuth implementou uma solução rápida com tentativas de hash em um programa de 8 páginas para ilustrar sua técnica de programação, enquanto Doug McIlroy, o padrinho dos pipes Unix, respondeu com uma one-liner, que não foi tão rápido, mas fez o trabalho:
Obviamente, a solução da McIlroy possui complexidade de tempo O (N log N), onde N é um número total de palavras. Existem soluções muito mais rápidas. Por exemplo:
Aqui está uma implementação C ++ com a complexidade de tempo limite superior O ((N + k) log k), normalmente - quase linear.
Abaixo está uma implementação rápida do Python usando dicionários de hash e heap com complexidade de tempo O (N + k log Q), onde Q é um número de palavras exclusivas:
Comparação de tempo de CPU (em segundos):
Notas:
fonte