単語周波数の集計スクリプトが遅すぎます
-
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
freick.txtを1つのパスで1つのパスですべての単語を検索するには:
awk '{print $2}' frequency.txt | grep -Fxf dictionary.txt > corpus-lexicon.txt
-F
固定文字列を検索します。-x
ライン全体のみに一致させる。-f
dictionary.txtの検索パターンを読むには
実際、これを2番目のループと組み合わせて、中間corpus-lexicon.txtファイルを排除することもできます。ループ用の2つは、単一のグレップに置き換えることができます。
grep -Fwf dictionary.txt frequency.txt | awk '{print $2 "," $1}'
私が変わったことに注意してください -x
に -w
.
他のヒント
これは通常、SpeedでPerlで書くスクリプトの1つです。しかし、私のように、あなたが書き込みのみのプログラミング言語を嫌うなら、あなたはそれをすべて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()]))
私が座っていた大きなテキストファイル(WCによると1.4MB、80,000ワード)に対してテストを行い、これは5年前のPowerMacの2秒未満(18Kユニークな単語)で完了します。