单词频率计数脚本太慢

时间:2011-01-07 15:49:23

标签: bash optimization dictionary corpus lexicon

背景

创建一个脚本来计算纯文本文件中单词的频率。该脚本执行以下步骤:

  1. 计算语料库中单词的频率。
  2. 保留在词典中找到的语料库中的每个单词。
  3. 创建以逗号分隔的频率文件。
  4. 脚本位于:http://pastebin.com/VAZdeKXs

    #!/bin/bash
    
    # Create a tally of all the words in the corpus.
    #
    echo Creating tally of word frequencies...
    sed -e 's/ /\n/g' -e 's/[^a-zA-Z\n]//g' corpus.txt | \
      tr [:upper:] [:lower:] | \
      sort | \
      uniq -c | \
      sort -rn > frequency.txt
    
    echo Creating corpus lexicon...
    rm -f corpus-lexicon.txt
    
    for i in $(awk '{if( $2 ) print $2}' frequency.txt); do
      grep -m 1 ^$i\$ dictionary.txt >> corpus-lexicon.txt;
    done
    
    echo Creating lexicon...
    rm -f lexicon.txt
    
    for i in $(cat corpus-lexicon.txt); do
      egrep -m 1 "^[0-9 ]* $i\$" frequency.txt | \
        awk '{print $2, $1}' | \
        tr ' ' ',' >> lexicon.txt;
    done
    

    问题

    以下几行不断循环通过字典来匹配单词:

    for i in $(awk '{if( $2 ) print $2}' frequency.txt); do
      grep -m 1 ^$i\$ dictionary.txt >> corpus-lexicon.txt;
    done
    

    它有效,但它很慢,因为它正在扫描它发现的单词,以删除字典中没有的单词。代码通过扫描每个单词的字典来执行此任务。 (-m 1参数在找到匹配项时停止扫描。)

    问题

    您如何优化脚本,以便不会为每个单词从头到尾扫描字典?大多数单词都不在字典中。

    谢谢!

3 个答案:

答案 0 :(得分:2)

您可以使用grep -f在frequency.txt上一次搜索所有字词:

awk '{print $2}' frequency.txt | grep -Fxf dictionary.txt > corpus-lexicon.txt
  • -F搜索固定字符串。
  • -x仅匹配整行。
  • -f从dictionary.txt
  • 读取搜索模式

实际上,你甚至可以将它与第二个循环结合起来,并删除中间的corpus-lexicon.txt文件。两个for循环可以用一个grep替换:

grep -Fwf dictionary.txt frequency.txt | awk '{print $2 "," $1}'

请注意,我已将-x更改为-w

答案 1 :(得分:1)

这通常是您在Perl中为速度编写的脚本之一。但是,如果和我一样,你讨厌只能使用编程语言,那么你可以在Awk中完成所有这些:

awk '
    BEGIN {
        while ((getline < "dictionary.txt") > 0)
            dict[$1] = 1
    }
    ($2 && $2 in dict) { print $2 }
' < frequency.txt > corpus-lexicon.txt

此版本中不需要rm -f corpus-lexicon.txt

答案 2 :(得分:0)

使用真正的编程语言。所有的应用程序启动和文件扫描都会让你失望。例如,这是一个我用Python编写的示例(最小化代码行):

import sys, re
words = re.findall(r'(\w+)',open(sys.argv[1]).read())
counts = {}
for word in words:
  counts[word] = counts.setdefault(word,0) + 1
open(sys.argv[2],'w').write("\n".join([w+','+str(c) for (w,c) in counts.iteritems()]))

根据一个大文本文件测试一个我已经坐好的文本文件(1.4MB,根据wc为80,000个单词),这在一个5岁的powermac上以不到一秒钟(18k个独特单词)完成。