【问题标题】:Word frequency tally script is too slow词频统计脚本太慢
【发布时间】:2011-01-07 15:49:22
【问题描述】:

背景

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

  1. 计算语料库中单词的频率。
  2. 保留在字典中找到的语料库中的每个单词。
  3. 创建一个以逗号分隔的频率文件。

脚本位于: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 参数在找到匹配时停止扫描。)

问题

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

谢谢!

【问题讨论】:

  • 顺便说一句,如果您将重定向移动到循环的末尾,您不必rm 文件(因为您不会使用附加):foo; done > outputfile 代替foo >> outputfile; done.
  • @Dennis:很好的提示。谢谢!

标签: bash optimization dictionary corpus lexicon


【解决方案1】:

您可以使用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

【讨论】:

  • 完美。谢谢你。它跑得太快了,我认为它不可能是正确的。运行脚本需要 34 秒,其中 30 秒被计票。
【解决方案2】:

这通常是您为了提高速度而用 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

【讨论】:

    【解决方案3】:

    使用真正的编程语言。所有的应用程序启动和文件扫描都在杀死你。例如,这是我刚刚用 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 个唯一字)。

    【讨论】:

    • 它是什么样的服务器?我真的不知道任何不包含某些版本的 python 的现代 unix。无论如何,这个概念在 Perl、Ruby 等中都是一样的。
    • 约翰的单线是我想要的。原来服务器确实有 Python。 Mea cupla.
    猜你喜欢
    • 2012-01-30
    • 2019-10-29
    • 1970-01-01
    • 1970-01-01
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多