bir dosyada en sık kullanılan kelimeleri bulun


34

Bir metin dosyasında en yaygın 10 kelimeyi bulmak istiyorum. Öncelikle, çözüm tuş vuruşları için optimize edilmelidir (başka bir deyişle - benim zamanım). İkincisi, performans için. İşte şimdiye kadar ilk 10'a girebileceklerim var:

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

Bir sözlükte sakladığım (word, numberOfOccurences) bir java, python vs. programı yapabilir ve değeri sıralayabilir ya da MapReduce kullanabilirim, ancak tuş vuruşları için optimize edebilirim.

Herhangi bir yanlış pozitif var mı? Daha iyi bir yolu var mı?


sonunda neden -10 koydun? : P
anu

Yanıtlar:


47

Yani bir kaçırdığınızı dışında hemen hemen "N en yaygın şeyler" bulmanın en yaygın yolu var sortve bir gratuitious var cat:

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

Daha sortönce uniq -c girmediyseniz, muhtemelen birçok yanlış tekil sözcük elde edersiniz. uniqGenel olarak tek bir dizide değil, sadece benzersiz satır çizgileri yapar.

EDIT: Bir numara unuttum, "kelimeleri durdur". İngilizce metne bakıyorsanız (üzgünüm, tek dilli Kuzey Amerika burada), "", "ve", "gibi kelimeler neredeyse her zaman ilk iki ya da üç sırayı alır. Muhtemelen onları ortadan kaldırmak istiyorsun. GNU Groff dağıtımında adında eignbir durma kelimelerinin oldukça iyi bir listesini içeren bir dosya vardır . Benim Arch dağıtıma sahip /usr/share/groff/current/eign, ama ben de gördüğüm düşünüyorum /usr/share/dict/eignveya /usr/dict/eigneski Unix'ler içinde.

Bu gibi dur sözcükleri kullanabilirsiniz:

tr -c '[:alnum:]' '[\n*]' < test.txt |
fgrep -v -w -f /usr/share/groff/current/eign |
sort | uniq -c | sort -nr | head  -10

Tahminime göre çoğu insan dili, anlamlı kelime sıklığı sayımlarından çıkarılmış benzer "durma kelimeler" e ihtiyaç duyuyor, ancak başka dillerin kelime listelerini durdurmasını nereden önereceğimi bilmiyorum.

EDIT: tüm kelime eşleşmesini sağlayan komutu fgrepkullanmalıdır -w. Bu, "a" veya "i" gibi, yalnızca kısa duraklama eserleri içeren sözcüklerdeki yanlış pozitifleri önler.


2
Does catbazı önemli performans ek yük? Boru sözdizimini seviyorum. '[\ N *]' içindeki * ne işe yarar?
Lukasz Madon

1
"Cat test.txt" dosyasını seviyorsanız, elbette kullanın. Dennis Ritchie'nin "kedi bir şey | başka bir şey" sözdiziminin daha yaygın kullanıldığı ve '<bir şey' sözdiziminin tek bir amaç olduğu için yanlış bir şey olduğunu söylediği bir makale okudum.
Bruce Ediger

Bir findçıktıdaki en yaygın dizin adını bulmak istersem ne olur ? Başka bir deyişle, kelimeleri /boşluk karakterleri ve benzeri yerine ayırın.
erb

1
@ erb - muhtemelen şöyle bir şey yaparsınız:find somewhere optoins | tr '/' '\n' | sort | uniq -c | sort -k1.1nr | head -10
Bruce Ediger

1
@ erb - Bunu bir yorumda değil, bir soru olarak sorun. İhtiyacınız olan cevabı elde etmek için sorunuzu çerçevelemek için daha fazla alana sahip olacaksınız. Örnek girdi ve istenen çıktı verin. İyi bir soru sormak için bazı itibar puanları kazanabilirsiniz ve bir yorumda verebileceğimden daha iyi bir cevap vermek için puan kazanacağım.
Bruce Ediger

8

Bu utf-8 ile daha iyi çalışır:

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

7

AWK'yı kullanalım!

Bu işlev, verilen dosyada meydana gelen her kelimenin sıklığını Azalan düzende listeler:

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
}

Dosyanızda şöyle diyebilirsiniz:

$ cat your_file.txt | wordfrequency

ve ilk 10 kelime için:

$ cat your_file.txt | wordfrequency | head -10

Kaynak: AWK-ward Ruby


4

Haskell'i kullanalım!

Bu bir dil savaşına dönüşüyor, değil mi?

import Data.List
import Data.Ord

main = interact $ (=<<) (\x -> show (length x) ++ " - " ++ head x ++ "\n")
                . sortBy (flip $ comparing length)
                . group . sort
                . words

Kullanımı:

cat input | wordfreq

Alternatif:

cat input | wordfreq | head -10

görmezden gelinen
Axel Latvala

Klasikten çok daha yavaş çalışıyor sort | uniq -c | sort -nr.
Andriy Makukha

@AndriyMakukha Darboğaz, karakter dizilerinin Haskell'deki karakterlerin listesidir. Geçiş yerine Textveya ByteStringyerine C benzeri hızlar alabiliriz ; bu özellik, nitelikleri içe aktarma ve niteleyiciye işlevlerin ön eklemesi kadar basittir.
BlackCap

pastebin.com/QtJjQwT9 önemli ölçüde daha hızlı sürüm, okunabilirlik için yazılmış
BlackCap

3

Bunun gibi bir şey, yaygın olarak bulunan python kullanarak çalışmalıdır:

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

Bu, satır başına kelime alır. Daha fazla varsa, bölme de kolay olmalıdır.


python3 ve daha güzel çıktıcat README.md | python -c 'import collections, sys, pprint; pprint.pprint(collections.Counter(sys.stdin));'
Lukasz Madon

1

Bu, 1986'da Donald Knuth'un 8 sayfalık bir programda okuryazar programlama tekniğini göstermek için hash ile hızlı bir çözüm uyguladığı ve bazı unix borularının vaftiz babası Doug McIlroy'un cevap verdiği klasik bir sorundur. tek gömlek, bu kadar hızlı değildi, ama işi halletti:

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

Tabii ki, McIlroy'un çözümü zaman karmaşıklığına sahiptir (N log N), burada N toplam kelime sayısıdır. Çok daha hızlı çözümler var. Örneğin:

İşte , tipik olarak - neredeyse doğrusal olan üst sınır zaman karmaşıklığı O ((N + k) log k) olan bir C ++ uygulamasıdır.

Aşağıda, karma sözlükler ve zaman karmaşıklığı olan O (N + k log Q) içeren yığın kullanan hızlı bir Python uygulaması bulunmaktadır; burada Q, bir dizi benzersiz sözcüktür:

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)

CPU zaman karşılaştırması (saniye olarak):

                                     bible32       bible256
C++ (prefix tree + heap)             5.659         44.730  
Python (Counter)                     10.314        100.487
Sheharyar (AWK + sort)               30.864        251.301
McIlroy (tr + sort + uniq)           60.531        690.906

Notlar:

  • İncil 32 İncil'dir kendisi ile 32 kez (135 MB), sırasıyla İncil256 - 256 kez (1.1 GB).
  • Python komut dosyalarının doğrusal olmayan yavaşlamaları, yalnızca dosyaları tamamen bellekte işlemesi nedeniyle oluşur, bu nedenle genel masraflar büyük dosyalar için daha büyük hale gelir.
  • Bir yığın oluşturabilecek ve yığının üstünden n elemanlar alabilecek bir Unix aracı olsaydı, AWK çözümü şu anda O (N + Q log Q) iken doğrusal-yakın zaman karmaşıklığına ulaşabilirdi.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.