TP2 - Recherche d'information

Le but de ce TP est de manipuler des documents sous la forme de vecteurs et d'implémenter un petit moteur de recherche.


Préliminaires

Pour cette séance, vous avez à disposition une collection de dépêches Reuters à cette adresse. Le fichier collection.lst contient la liste des textes de la collection.


Exercice 1 - Représentation d'un texte

Q1Écrivez une classe Text qui représente un texte ou une requête sous une forme vectorielle (un dictionnaire Python qui a pour clés les token-mots du texte et pour valeurs leurs nombres d'occurrences dans le texte). Le texte se trouve soit dans un fichier soit dans une chaîne de caractères. Son constructeur prend donc deux paramètres optionnels (par défaut None) : le nom du fichier texte et une chaîne de caractères.

Exercice 2 - Représentation d'une collection

Q2Écrivez une classe TextCollection qui représente une collection de documents sous la forme d'une liste (de Text). Son constructeur prend comme paramètre le nom du fichier définissant la collection. Elle contient la méthode getTexts() qui renvoie la liste des textes.

Exercice 3 - Moteur de recherche

À partir des classes précédentes, vous allez écrire un moteur de recherche simple qui lit en entrée standard les requêtes.


Module texts.py contenant les classes Text et TextCollection :

import nltk
import codecs
import pickle
import sys
import math


class Text:
  def __init__(self,filename=None,text=None):
    self.__tokens = {}
    self.__name = filename
    for w in self.__getWords(filename,text):
      self.__tokens[w] = self.__tokens.get(w,0) + 1
      
  def __getWords(self,filename,text):
    if filename is not None:
      f = codecs.open(filename,'r','utf-8')
      text= f.read().lower()
    if text is None:
      text = ''
    l = nltk.regexp_tokenize(text,"\w+")
    if filename is not None:
      f.close()
    return l

  def getName(self):
    return self.__name

  def getWordTokens(self):
    return self.__tokens.keys()

  def setWeight(self,w,val):
    self.__tokens[w] = val

  def getWeight(self,w):
    return self.__tokens.get(w,0)

  def norm(self):
    n = 0
    for w in self.getWordTokens():
      n += self.getWeight(w)*self.getWeight(w)
    return math.sqrt(n)

  def scalarProduct(self,t):
    p = 0
    for w in self.getWordTokens():
      p += self.getWeight(w)*t.getWeight(w)
    return p


  def cosine(self,t):
    return float(self.scalarProduct(t))/self.norm()/t.norm()



class TextCollection:
  def __init__(self,filename=None):
    self.__list = []
    if filename is None:
      return
    pos = filename.rfind('/')
    prefix = ''
    if pos != -1:
      prefix = filename[:pos]+'/'
    f = codecs.open(filename,'r','utf-8')
    for line in f.readlines():
      #print prefix+line[:-1]
      self.__list.append(Text(filename=prefix+line[:-1]))
    f.close()

  def getTexts(self):
    return self.__list

  def save(self,filename):
    pickle.dump(self.__list,open(filename,'w'))

  def load(self,filename):
    self.__list = pickle.load(open(filename,'r'))

  def search(self,query,N):
    q = Text(text=query)
    scores = {}
    for t in self.getTexts():
      scores[t.getName()] = t.cosine(q)
    l = sorted(scores.keys(),cmp=lambda x,y:cmp(scores[y],scores[x]))[:N]
    return [x for x in l if scores[x] > 0]


TP2 :
from texts import TextCollection

c = TextCollection('/home/ens/mconstan/tal/reuter-collection-tp/collection.lst')
while True:
  query = raw_input('Enter a query:')
  print 'RESULT:'
  print c.search(query,10)

Exercice 4 - Bonus