СЛУЧАЯ ЧАСТОЧНАЯ СКРЫТАЯ СКРАТИРОВАНИЕ слишком медленным
-
30-09-2019 - |
Вопрос
Задний план
Создал скрипт для счета частоты слов в простом текстовом файле. Сценарий выполняет следующие шаги:
- Считайте частоту слов из корпуса.
- Сохранить каждое слово в корпусе, найденном в словаре.
- Создайте разделенный запятыми файл частот.
Сценарий: 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
Параметр останавливает сканирование, когда совпадение найдено.)
Вопрос
Как бы вы оптимизировали сценарий, так что словарь не отсканирован от начала до конца для каждого слова? Большинство слов не будут в словаре.
Спасибо!
Решение
Вы можете использовать grep -f
Для поиска всех слов в одном проходе через Chrade.txt:
awk '{print $2}' frequency.txt | grep -Fxf dictionary.txt > corpus-lexicon.txt
-F
Для поиска фиксированных строк.-x
соответствовать только цельным линиям.-f
Чтобы прочитать шаблоны поиска из Dictionary.txt
На самом деле, вы можете даже объединить это со вторым циклом и устранить промежуточный файл CORPUS-LECKINON.TXT. Два для петель могут быть заменены одним GREP:
grep -Fwf dictionary.txt frequency.txt | awk '{print $2 "," $1}'
Обратите внимание, что я изменил -x
к -w
.
Другие советы
Обычно это один из тех сценариев, которые вы пишете в 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
В этой версии.
Используйте реальный язык программирования. Все запуска приложения и сканирования файлов убивают вас. Например, вот пример, который я только что взбил в 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 МБ, 80 000 слов в соответствии с WC), это завершится под во второй (18 тысяч уникальных слов) на 5-летнем Powermac.