Question

Arrière-plan

A créé un script pour compter la fréquence des mots dans un fichier texte brut. Le script réalise les étapes suivantes:

  1. Count la fréquence des mots d'un corpus.
  2. Retain chaque mot dans le corpus trouvé dans un dictionnaire.
  3. Créer un fichier séparé par des virgules des fréquences.

Le script est à: 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

Problème

Les lignes qui suivent en permanence à travers le cycle de dictionnaire pour correspondre les mots:

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

Il fonctionne, mais il est lent, car il scanne les mots qu'il trouve pour enlever tout ce ne sont pas dans le dictionnaire. Le code exécute cette tâche en balayant le dictionnaire pour chaque mot. (Le paramètre -m 1 arrête le balayage lorsque la correspondance est trouvée.)

Question

Comment voulez-vous optimiser le script afin que le dictionnaire ne sont pas analysés de début à la fin pour chaque mot? La plupart des mots ne sera pas dans le dictionnaire.

Merci!

Était-ce utile?

La solution

Vous pouvez utiliser grep -f pour rechercher tous les mots en un seul passage sur frequency.txt:

awk '{print $2}' frequency.txt | grep -Fxf dictionary.txt > corpus-lexicon.txt
  • -F pour rechercher des chaînes fixes.
  • -x pour correspondre uniquement des lignes entières.
  • -f lire les motifs de recherche de dictionary.txt

En fait, vous pouvez même combiner cela avec la deuxième boucle et d'éliminer le fichier corpus lexicon.txt intermédiaire. Les deux pour les boucles peuvent être remplacés par un seul grep:

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

Notez que j'ai changé -x à -w.

Autres conseils

Ceci est généralement l'un de ces scripts que vous écririez en Perl pour la vitesse. Mais si, vous pouvez le faire tout en awk, comme moi, vous détestez les langages de programmation en écriture seule:

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

Pas besoin de la rm -f corpus-lexicon.txt dans cette version.

Utilisez un véritable langage de programmation. Toutes les app start-ups et les analyses de fichiers vous tuent. Par exemple, voici un exemple que je viens fouetté en Python (en minimisant les lignes de code):

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()]))

Test d'une contre un grand fichier texte que j'avais assis aound (1.4MB, 80.000 mots selon wc), ce qui termine dans le cadre d'un second (18k mots uniques) sur un vieux PowerMac 5 ans.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top