web-dev-qa-db-fra.com

Compter les bigrammes (paire de deux mots) dans un fichier en utilisant python

Je veux compter le nombre d'occurrences de tous les bigrammes (paire de mots adjacents) dans un fichier en utilisant python. Ici, je traite de très gros fichiers, donc je cherche un moyen efficace. J'ai essayé d'utiliser la méthode count avec l'expression rationnelle "\ w +\s\w +" sur le contenu du fichier, mais cela ne s'est pas avéré efficace.

par exemple. Supposons que je veuille compter le nombre de bigrams d'un fichier a.txt, dont le contenu est le suivant:

"the quick person did not realize his speed and the quick person bumped "

Pour le fichier ci-dessus, l'ensemble bigramme et leur nombre seront:

(the,quick) = 2
(quick,person) = 2
(person,did) = 1
(did, not) = 1
(not, realize) = 1
(realize,his) = 1
(his,speed) = 1
(speed,and) = 1
(and,the) = 1
(person, bumped) = 1

Je suis tombé sur un exemple d'objets Counter en Python, utilisé pour compter des unigrammes (mots simples). Il utilise également l'approche regex.

L'exemple va comme ceci:

>>> # Find the ten most common words in Hamlet
>>> import re
>>> from collections import Counter
>>> words = re.findall('\w+', open('a.txt').read())
>>> print Counter(words)

La sortie du code ci-dessus est:

[('the', 2), ('quick', 2), ('person', 2), ('did', 1), ('not', 1),
 ('realize', 1),  ('his', 1), ('speed', 1), ('bumped', 1)]

Je me demandais s'il était possible d'utiliser l'objet Counter pour obtenir le compte de bigrams. Toute approche autre que Counter object ou regex sera également appréciée.

17
Swapnil Nawale

Un peu de magie itertools:

>>> import re
>>> from itertools import islice, izip
>>> words = re.findall("\w+", 
   "the quick person did not realize his speed and the quick person bumped")
>>> print Counter(izip(words, islice(words, 1, None)))

Sortie:

Counter({('the', 'quick'): 2, ('quick', 'person'): 2, ('person', 'did'): 1, 
  ('did', 'not'): 1, ('not', 'realize'): 1, ('and', 'the'): 1, 
  ('speed', 'and'): 1, ('person', 'bumped'): 1, ('his', 'speed'): 1, 
  ('realize', 'his'): 1})

Prime

Obtenez la fréquence de n'importe quel n-gramme:

from itertools import tee, islice

def ngrams(lst, n):
  tlst = lst
  while True:
    a, b = tee(tlst)
    l = Tuple(islice(a, n))
    if len(l) == n:
      yield l
      next(b)
      tlst = b
    else:
      break

>>> Counter(ngrams(words, 3))

Sortie:

Counter({('the', 'quick', 'person'): 2, ('and', 'the', 'quick'): 1, 
  ('realize', 'his', 'speed'): 1, ('his', 'speed', 'and'): 1, 
  ('person', 'did', 'not'): 1, ('quick', 'person', 'did'): 1, 
  ('quick', 'person', 'bumped'): 1, ('did', 'not', 'realize'): 1, 
  ('speed', 'and', 'the'): 1, ('not', 'realize', 'his'): 1})

Cela fonctionne aussi avec les iterables et les générateurs paresseux. Vous pouvez donc écrire un générateur qui lit un fichier ligne par ligne, générant des mots, et le transmettre à ngarms pour le consommer paresseusement sans lire le fichier entier en mémoire.

40
Abhinav Sarkar

Que diriez-vous de Zip()?

import re
from collections import Counter
words = re.findall('\w+', open('a.txt').read())
print(Counter(Zip(words,words[1:])))
8
st0le

Il y a longtemps que cette question a été posée et a répondu avec succès. Je profite des réponses pour créer ma propre solution. Je voudrais le partager:

    import regex
    bigrams_tst = regex.findall(r"\b\w+\s\w+", open(myfile).read(), overlapped=True)

Cela fournira tous les bigrammes qui ne sont pas interrompus par une ponctuation.

0
hurrial

Vous pouvez simplement utiliser Counter pour tout n_gram comme ceci:

from collections import Counter
from nltk.util import ngrams 

text = "the quick person did not realize his speed and the quick person bumped "
n_gram = 2
Counter(ngrams(text.split(), n_gram))
>>>
Counter({('and', 'the'): 1,
         ('did', 'not'): 1,
         ('his', 'speed'): 1,
         ('not', 'realize'): 1,
         ('person', 'bumped'): 1,
         ('person', 'did'): 1,
         ('quick', 'person'): 2,
         ('realize', 'his'): 1,
         ('speed', 'and'): 1,
         ('the', 'quick'): 2})

Pour 3 grammes, il suffit de changer le n_gram en 3:

n_gram = 3
Counter(ngrams(text.split(), n_gram))
>>>
Counter({('and', 'the', 'quick'): 1,
         ('did', 'not', 'realize'): 1,
         ('his', 'speed', 'and'): 1,
         ('not', 'realize', 'his'): 1,
         ('person', 'did', 'not'): 1,
         ('quick', 'person', 'bumped'): 1,
         ('quick', 'person', 'did'): 1,
         ('realize', 'his', 'speed'): 1,
         ('speed', 'and', 'the'): 1,
         ('the', 'quick', 'person'): 2})
0
Kristada673