ค้นหาคำที่พบบ่อยที่สุดในไฟล์


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

ฉันสามารถสร้างโปรแกรมจาวา, ไพ ธ อน ฯลฯ ที่ฉันเก็บ (คำ, numberOfOccurences) ในพจนานุกรมและเรียงลำดับค่าหรือฉันสามารถใช้ MapReduce แต่ฉันปรับการกดแป้นพิมพ์ให้เหมาะที่สุด

มีผลบวกที่ผิดพลาดหรือไม่? มีวิธีที่ดีกว่า?


ทำไมคุณถึงใส่ -10 ในตอนท้าย? : P
anu

คำตอบ:


47

นั่นเป็นวิธีที่พบได้บ่อยที่สุดในการค้นหา "N สิ่งที่พบบ่อยที่สุด" ยกเว้นว่าคุณพลาดsortและคุณก็มีสิทธิ์ได้รับcat:

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

หากคุณไม่เคยพูดมาsortก่อนuniq -c คุณอาจจะได้คำพูดผิด ๆ มากมาย uniqทำเฉพาะการทำงานของบรรทัดเท่านั้นไม่รวม uniquness

แก้ไข:ฉันลืมเคล็ดลับ "หยุดคำ" หากคุณกำลังมองหาข้อความภาษาอังกฤษ (ขออภัยอเมริกาเหนือเดียวที่นี่) คำเช่น "ของ", "และ", "" "มักจะเป็นสองหรือสามอันดับแรก คุณอาจต้องการกำจัดพวกเขา การแจกจ่าย GNU Groff มีไฟล์ชื่อeignอยู่ในนั้นซึ่งมีรายการคำหยุดที่เหมาะสม Distro Arch ของฉันมี/usr/share/groff/current/eignแต่ฉันคิดว่าฉันเคยเห็น/usr/share/dict/eignหรือ/usr/dict/eignใน Unixes เก่า

คุณสามารถใช้คำหยุดแบบนี้:

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

1
หากคุณชอบ "cat test.txt" คุณก็สามารถใช้มันได้ ฉันได้อ่านบทความบางแห่งที่เดนนิสริชชี่บอกว่ามีการใช้ไวยากรณ์ "cat something | somethingelse" กันอย่างแพร่หลายและมีการใช้ไวยากรณ์ <<บางสิ่งบางอย่างผิดพลาดเนื่องจากเป็นจุดประสงค์เดียว
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-Ward Ruby


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

กรณีที่แก้ไขแล้วโดยไม่สนใจรุ่น: pastebin.com/57T5B6BY
Axel Latvala

ทำงานช้ากว่าคลาสสิกsort | uniq -c | sort -nrมาก
Andriy Makukha

@AndriyMakukha คอขวดคือสตริงที่เชื่อมโยงรายชื่อตัวละครใน Haskell เราสามารถรับความเร็ว C-like ได้โดยการสลับไปTextหรือByteStringแทนซึ่งง่ายเหมือนการนำเข้ามันที่ผ่านการรับรองและนำหน้าฟังก์ชั่นด้วย qualifier
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));'
Lukasz Madon

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 คือจำนวนคำทั้งหมด มีวิธีแก้ปัญหาที่เร็วกว่ามาก ตัวอย่างเช่น:

นี่คือการประยุกต์ใช้ C ++ ที่มีความซับซ้อนของเวลาที่ถูกผูกไว้ O ((N + k) บันทึก k) ซึ่งโดยทั่วไป - เกือบเป็นเชิงเส้น

ด้านล่างนี้เป็นการนำ Python ไปใช้อย่างรวดเร็วโดยใช้พจนานุกรมแฮชและฮีปที่มีความซับซ้อนของเวลา O (N + k บันทึก Q) โดยที่ 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 ครั้ง (135 MB), bible256 - 256 ครั้งตามลำดับ (1.1 GB)
  • การที่สคริปต์ไม่ทำงานช้าลงของ Python เกิดจากความจริงที่ว่ามันประมวลผลไฟล์ในหน่วยความจำอย่างสมบูรณ์ดังนั้นค่าโสหุ้ยจะเพิ่มขึ้นสำหรับไฟล์ขนาดใหญ่
  • หากมีเครื่องมือ Unix ที่สามารถสร้างฮีปและเลือกองค์ประกอบจากด้านบนของฮีปโซลูชัน AWK สามารถสร้างความซับซ้อนของเวลาใกล้เชิงเส้นได้ในขณะนี้คือ O (N + Q log Q)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.