Частота подсчета слов слишком медленная - PullRequest
1 голос
/ 07 января 2011

Фон

Создан скрипт для подсчета частоты слов в текстовом файле.Сценарий выполняет следующие шаги:

  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 останавливает сканирование при обнаружении совпадения.)

Вопрос

Как бы вы оптимизировали скрипт, чтобы словарь не сканировался от начала до конца для каждого отдельного слова?Большинство слов не будет в словаре.

Спасибо!

Ответы [ 3 ]

2 голосов
/ 07 января 2011

Вы можете использовать grep -f для поиска всех слов за один проход по частоте .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 голос
/ 07 января 2011

Обычно это один из тех скриптов, которые вы пишете на 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 в этой версии.

0 голосов
/ 07 января 2011

Используйте настоящий язык программирования. Все запуска приложений и сканирования файлов убивают вас. Например, вот пример, который я только что написал в 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,4 МБ, 80000 слов в соответствии с wc), это завершается менее чем за секунду (18 000 уникальных слов) на 5-летнем Powermac.

...