파일에서 가장 빈번한 단어 n 개 찾기


34

텍스트 파일에서 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

사전에 단어 (word, numberOfOccurences)를 저장하고 값을 정렬하거나 MapReduce를 사용할 수있는 Java, python 등의 프로그램을 만들 수 있지만 키 입력을 최적화합니다.

오 탐지가 있습니까? 더 좋은 방법이 있습니까?


왜 마지막에 -10을 넣겠습니까? : P
anu

답변:


47

즉 당신이이 누락 제외하고, 거의 "N 가장 일반적인 것들"을 찾는 가장 일반적인 방법 sort, 그리고 당신은 gratuitious있어 cat:

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

sort전에 입력하지 않으면 uniq -c 아마도 많은 잘못된 싱글 톤 단어를 보게 될 것입니다. uniq전반적인 고유성이 아닌 고유 한 라인 만 수행합니다.

편집 : 나는 트릭, "중지 단어"를 잊어 버렸습니다. 영어 텍스트 (미안하고 단일 언어 인 북미 지역)를보고있는 경우 "of", "and", "the"와 같은 단어는 거의 항상 상위 2 ~ 3 자리를 차지합니다. 아마도 그것들을 제거하고 싶을 것입니다. GNU Groff 배포본에는 eign꽤 괜찮은 단어 목록이 들어있는 파일 이 있습니다. 내 아치 배포판이있다 /usr/share/groff/current/eign,하지만 난 또한 본 적이 생각 /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"와 같은 짧은 중지 작업 만 포함하는 단어에 대한 오 탐지가 방지됩니다.


2
cat상당한 성능 오버 헤드가 추가 됩니까 ? 파이프 구문이 마음에 듭니다. '[\ n *]'에서 *는 무엇을합니까?
Lukasz Madon 2012 년

1
"cat test.txt"가 마음에 드시면 꼭 사용하십시오. 데니스 리치 (Dennis Ritchie)가 "cat something | somethingelse"구문이 더 널리 사용되며 '<something'구문이 단일 목적이므로 실수라고 생각하는 기사를 읽었습니다.
Bruce Ediger

find출력 에서 가장 일반적인 디렉토리 이름을 찾으려면 어떻게 합니까? 즉, /공백 문자 대신에 단어를 분리하십시오 .
erb

1
@erb-당신은 아마 다음과 같은 일을 할 것입니다 :find somewhere optoins | tr '/' '\n' | sort | uniq -c | sort -k1.1nr | head -10
Bruce Ediger

1
@erb-주석이 아닌 질문으로 질문하십시오. 당신은 당신이 필요로하는 답을 얻을 수 있도록 더 많은 질문을 할 공간을 갖게 될 것입니다. 예제 입력과 원하는 출력을 제공하십시오. 좋은 질문을함으로써 명성을 얻을 수 있으며, 의견에서 할 수있는 것보다 더 나은 답변을 제공 할 것입니다.
Bruce Ediger


7

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

하스켈을 사용합시다!

이것은 언어 전쟁으로 바뀌고 있지 않습니까?

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

수정 된 버전 무시 사례 : pastebin.com/57T5B6BY
Axel Latvala

클래식보다 훨씬 느리게 작동합니다 sort | uniq -c | sort -nr.
안드리 마 쿠카

@AndriyMakukha 병목 현상은 문자열이 Haskell에서 연결된 문자 목록이라는 것입니다. 우리는 C 로의 속도를 얻을 수 있습니다. Text또는 ByteString대신 스위치를 사용하여 정규화 된 가져 오기와 한정자로 함수의 접두사를 추가하는 것만 큼 간단합니다.
BlackCap

pastebin.com/QtJjQwT9 훨씬 더 빠른 버전, 가독성을 위해 작성
BlackCap

3

이와 같은 것은 일반적으로 사용 가능한 파이썬을 사용하여 작동해야합니다.

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

이것은 줄당 단어를 가정합니다. 더 많은 것이 있으면 분리도 쉬워야합니다.


python3 및 더 cat README.md | python -c 'import collections, sys, pprint; pprint.pprint(collections.Counter(sys.stdin));'
나은

1

이것은 1986 년에 약간의 공명을 가지고 고전적인 문제 도널드 크 누스가 빠른 솔루션 구현 더그 위권에 드는 맥 킬로, 유닉스 파이프의 대부가하는으로 대응하면서, 자신의 글을 읽고 프로그래밍 기술을 설명하기 위해 8 페이지에 걸친 프로그램에 해시 시도와를 한 줄짜리, 그것은 빠르지는 않았지만 일을 마쳤습니다.

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 ++ 구현입니다.

다음은 시간 복잡도 O (N + k log Q)의 해시 사전 및 힙을 사용하는 빠른 Python 구현입니다. 여기서 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)

CPU 시간 비교 (초) :

                                     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

노트:

  • bible32는 성경 자체와 32 배 (135MB), bible256 – 256 배 (1.1GB)로 연결되어 있습니다.
  • 파이썬 스크립트의 비선형 감속은 순수한 메모리에서 파일을 완전히 처리하기 때문에 발생합니다. 따라서 대용량 파일의 경우 오버 헤드가 커지고 있습니다.
  • 힙을 구성하고 힙 맨 위에서 n 개의 요소를 선택할 수있는 Unix 도구가있는 경우 AWK 솔루션은 거의 선형적인 시간 복잡성을 달성 할 수 있지만 현재는 O (N + Q log Q)입니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.