web-dev-qa-db-fra.com

Un moyen plus rapide de supprimer les mots vides dans Python

J'essaie de supprimer les mots vides d'une chaîne de texte:

from nltk.corpus import stopwords
text = 'hello bye the the hi'
text = ' '.join([Word for Word in text.split() if Word not in (stopwords.words('english'))])

Je traite 6 mil de ces cordes, donc la vitesse est importante. Profilage de mon code, la partie la plus lente est les lignes ci-dessus, existe-t-il une meilleure façon de le faire? Je pense à utiliser quelque chose comme re.sub De regex mais je ne sais pas comment écrire le modèle d'un ensemble de mots. Quelqu'un peut-il me donner un coup de main et je suis également heureux d'entendre d'autres méthodes éventuellement plus rapides.

Remarque: J'ai essayé la suggestion de quelqu'un de regrouper stopwords.words('english') avec set() mais cela n'a fait aucune différence.

Je vous remercie.

37
mchangun

Essayez de mettre en cache l'objet stopwords, comme indiqué ci-dessous. Construire cela chaque fois que vous appelez la fonction semble être le goulot d'étranglement.

    from nltk.corpus import stopwords

    cachedStopWords = stopwords.words("english")

    def testFuncOld():
        text = 'hello bye the the hi'
        text = ' '.join([Word for Word in text.split() if Word not in stopwords.words("english")])

    def testFuncNew():
        text = 'hello bye the the hi'
        text = ' '.join([Word for Word in text.split() if Word not in cachedStopWords])

    if __name__ == "__main__":
        for i in xrange(10000):
            testFuncOld()
            testFuncNew()

J'ai exécuté cela via le profileur: python -m cProfile -s cumulative test.py. Les lignes pertinentes sont affichées ci-dessous.

nCalls Temps cumulé

10000 7,723 mots.py:7(testFuncOld)

10000 0,140 mots.py:11(testFuncNew)

Ainsi, la mise en cache de l'instance de mots vides donne une accélération de ~ 70x.

83
Andy Rimmer

Utilisez une expression rationnelle pour supprimer tous les mots qui ne correspondent pas:

import re
pattern = re.compile(r'\b(' + r'|'.join(stopwords.words('english')) + r')\b\s*')
text = pattern.sub('', text)

Ce sera probablement façon plus rapide que de boucler vous-même, en particulier pour les grandes chaînes d'entrée.

Si le dernier mot du texte est supprimé par cela, vous pouvez avoir un espace de fin. Je propose de gérer cela séparément.

15
Alfe

Tout d'abord, vous créez des mots vides pour chaque chaîne. Créez-le une fois. L'ensemble serait formidable ici en effet.

forbidden_words = set(stopwords.words('english'))

Plus tard, supprimez [] Dans join. Utilisez plutôt un générateur.

' '.join([x for x in ['a', 'b', 'c']])

remplacer pour

' '.join(x for x in ['a', 'b', 'c'])

La prochaine chose à faire serait de faire en sorte que .split() produise des valeurs au lieu de renvoyer un tableau. Je crois que regex serait un bon remplacement ici. Voir thist hread pour savoir pourquoi s.split() est réellement rapide.

Enfin, faites un tel travail en parallèle (en supprimant les mots vides dans les chaînes de 6 m). C'est un tout autre sujet.

4
Krzysztof Szularz

Désolé pour la réponse tardive. Se révélerait utile pour les nouveaux utilisateurs.

  • Créer un dictionnaire de mots vides à l'aide de la bibliothèque de collections
  • Utilisez ce dictionnaire pour une recherche très rapide (time = O(1)) plutôt que de le faire sur la liste (time = O (mots vides))

    from collections import Counter
    stop_words = stopwords.words('english')
    stopwords_dict = Counter(stop_words)
    text = ' '.join([Word for Word in text.split() if Word not in stopwords_dict])
    
4
Gulshan Jangid