Pràctiques de Nous Usos de la Informàtica

Naive Bayes i Classificació

ENTREGA: El dia límit per a l'entrega d'aquesta pràctica és el dia 17 de Novembre a les 23.55h

Format de l'entrega L'entrega s'efecturà mitjançant el campus virtual. S'ha de penjar un arxiu per grup. El nom del fitxer ha de seguir el següent patro: NUI_2_PrimeralletranomCognomMembre1_PrimeralletranomCognomMembre2.iypnb

Exemple:
Membre 1: Maria del Carme Vilà
Membre 2: Francesc Castell

Nom de l'arxiu: NUI_2_MVila_FCastell.ipynb

Què s’ha de fer?
Volem classificar blogs corresponents a militants de quatre partits polítics segons la seva ideologia. Per fer-ho farem servir les seves entrades RSS o Atom. A partir de les seves entrades crearem un vector de característiques que ens descrigui el contingut del blog. Finalment desenvoluparem un classificador probabilístic del tipus Naive Bayes que ens permeti identificar la ideologia d'un nou blog donat la seva descripció en les característiques escollides.

Quina és la idea del sistema de classificació que s’ha de desenvolupar?
El classificador és un concepte de l'aprenentatge automàtic supervisat. L'objectiu del classificador és donat un vector de característiques que descriuen els objectes que es volen classificar indicar a quina categoria o classe pertanyen d'entre un conjunt predeterminat. El procés de classificació consta de dues parts: (a) el procés d'aprenentatge i (b) el procés d'explotació o testeig. El procés d'aprenentatge rep exemples de parelles (x,y) on x són les característiques, usualment nombres reals, i y és la categoria a la que pertanyen. Aquest conjunt se'l coneix com a conjunt d'entrenament i ens servirà per trobar una funció y=h(x) que donada una x m'indiqui quina és la y. Per altra banda el procés de testeig aplica la funció h(x) apresa a l'entrenament a una nova descripció per veure quina categoria li correspon.

Classificació i llenguatge natural
La descripció dels exemples en característiques és el punt més crític de tot sistema d'aprenentatge automàtic. Una de les representacions més simples per tal de descriure un text és la representació bag-of-words. Aquesta representació converteix un text en un vector de N paraules. Consisteix en seleccionar un conjunt d'N paraules i per cada paraula contar quants cops apareix en el text. Una versió alternativa d'aquest procés pot ser simplement indicar si apareix o no en el text.

Exemple. Imaginem que tenim 4 texts que pertanyen només a dues categories y={'ERC, 'ICV'}, podríem seleccionar les següents paraules per tal de distingir les dues categories x={'nuclear', 'verd', 'ecologia', 'independència', 'autonomia', 'referèndum'}

nuclear verd ecologia independència autonomia referèndum
tesxt 1('ERC') 0 0 0 1 2 3
tesxt 2('ERC') 0 1 0 1 2 3
tesxt 1('ICV') 1 3 1 0 1 0
tesxt 2('ICV') 2 1 2 0 0 0


amb aquesta representació el text 2, corresponent a la categoria 'ERC', quedaria representat pel vector numèric (0,1,0,1,2,3). Si fem servir la representació alternativa tindríem (0,1,0,1,1,1) que indica la presència de les paraules. Si la descripció és adient s'espera que les categories es puguin distingir amb facilitat.

El classificador Naïve Bayes.
Un cop tenim una representació necessitem un procés d'aprenentatge que ens permeti passar de la descripció a una categoria. En aquesta pràctica farem servir el classificador Naïve Bayes. Aquest classificador forma part de la família de classificadors probabilístics. La sortida d'un classificador probabilístic és un valor de probabilitat donat un exemple per cadascuna de les categories. La decisió final correspon a la categoria amb més probabilitat. Per exemple, amb la descripció anterior esperem que la sortida sigui de l'estil,
$$p( y = 'ERC' | x = (0,1,0,1,1,1)) = 0.6$$ $$p( y = 'ICV' | x = (0,1,0,1,1,1)) = 0.4$$


Els classificadors probabilistics Bayesians es basen en el teorema de Bayes per realitzar els càlculs per trobar la probabilitat condicionada. Es basen en el teorema de Bayes que diu:

$$ p(x,y) = p(x|y)p(y) = p(y|x)p(x)$$


d'on podem extreure que:
$$ p(y,x) = \frac{p(x|y)p(y)}{p(x)}$$

En molts casos p(y) i p(x) són desconeguts i es consideren equiprobables. Per tant, la decisió es simplifica a:
$$ p(y|x) = c · p(x|y)$$


Les deduccions fins a aquest punt són vàlides per la majoria de classificadors Bayesians. Naïve Bayes es distingeix de la resta perquè imposa una condició encara més restrictiva. Considerem x=(x1,x2,x3,...,xN) un conjunt d'N variables aleatòries. Naïve Bayes assumeix que totes elles són independents entre elles i per tant podem escriure:
$$p(x_1,x_2,...,x_N | y) = p(x_1|y)p(x_2|y)...p(x_N|y)$$


Per tant en el nostre cas es pot veure com:
$$p(y='ERC'|x=(0,1,0,1,1,1)) = p(x_1=0|y='ERC')p(x_2=1|y='ERC')...p(x_6=1|y='ERC')$$


Podem interpretar l'anterior equació de la següent forma: La probabilitat de que el document descrit pel vector de característiques (0,1,0,1,1,1) es proporcional al producte de la probabilitat que la primera paraula del vector no aparegui en els documents de la categoria 'ERC' per la probabilitat que la segona paraula sí aparegui als documents d''ERC', etc.


Estimant les probabilitats marginals condicionades L'últim pas que ens queda és trobar el valor de les probabilitats condicionades. Fem servir la representació de 0 i 1's indicant que la paraula no apareix (0) o sí apareix (1) al document. Per trobar el valor de la probabilitat condicionada farem servir una aproximació freqüentista a la probabilitat. Això vol dir que calcularem la freqüència d'aparició de cada paraula per a cada categoria. Aquest càlcul es fa dividint el nombre de documents de la categoria en que apareix la paraula pel nombre total de documents d'aquella categoria. En l'exemple anterior, p(x2=apareix 'verd'|y='ERC')=1/2 mentres que p(x2=apareix 'verd'|y='ICV')=2/2.
$$p(x_2 = 1 | y = 'ERC')= \frac{A}{B} $$
on A és el número de blocs de 'ERC' on apareix la paraul 'verd' i B és el número de bloc de 'ERC'.

PROBLEMES:
* El problema de la probabilitat 0 Si us fixeu a l'anterior exemple la probabilitat p(x2=no apareix 'verd' | y='ICV') és 0 !! Això vol dir, que si no apareix la paraula 'verd' al blog no pot ser d''ICV' !! No sembla raonable que es penalitzi totalment l'aparició d'aquesta categoria pel fet que una única paraula aparegui o no al text. Per tant el que s'acostuma a fer és donar una baixa probabilitat en comptes de zero. Una de les possibles solucions es fer servir la correcció de Laplace. Seguint l'exemple anterior la correcció de Laplace és


$$p(x_2=1 | y = 'ERC' ) = \frac{A+1}{B+M}$$ on M és el nombre de catergories

* El problema del 'underflow' L'elecció de les paraules que formen el vector de característiques és un pas crític. En funció de quan bona és aquesta descripció millor funcionarà el sistema. Tot i que us deixem a vosaltres la política de creació del vector de característiques us donem una d'exemple. Per saber quines paraules fer servir podeu seleccionar de totes les paraules de tots els blogs aquelles que apareixen entre en un 10 i un 50 percent dels blogs (sense tenir en compte la categoria). Podeu experimentar a variar aquests valors.

Els fitxers python feedparser.py i generatefeedvector.py. El fitxer feedparser.py ens permet recuperar la informació proporcionada per sistemes de feed com RSS o Atom. El fitxer generatefeedvector.py proporciona utilitats bàsiques per dividir el resultat de l'anterior en paraules eliminant tags d'HTML, traduint caràcters unicode i HTML corresponents al sistema de puntuació català. A més trobareu el fitxer blogs.txt que conté una llista de blogs amb el següent format: ID | Partit | Nom | URL

In [19]:
import math
import csv
import generatefeedvector as generate
import pandas as pd
import numpy as np


#Retorna un diccionari amb la seguent estricutra: {id_blog:(nom_politic,{paraula1:numAparicions;paraule2:numAparicision,...})}
def download_text(df):
    dicc={}
    invalid_blogs=[]
    #recorremos la lista con toda la informacion de todos los blogs
    urls=list(df['url'])
    for i in df.index:
        try:
            dicc[i]=generate.getwordcounts(df.ix[i]['url'])
            #print "blog :", df.ix[i]['nom']
        except:
            invalid_blogs.append(df.index[i])
            #print "Link incorrecte, blog", df.ix[i]['nom']
    print "\nLectura dels blogs finalizada.\n",
    return dicc,invalid_blogs

In [20]:
# CREACIO DEL DATAFRAME 
unames = ['id_blog', 'partit_politic', 'nom', 'url']
df = pd.read_table('blogs.dat',sep='::', header=None, names=unames)

# EXEMPLE DE COM BAIXR D'UN CONJUNT DE BLOGS
data_blogs,invalid_blogs=download_text(df)
# EXEMPLE DE COM ELIMINAR AQUELLS BLOGS QUE NO S'HA POGUT ACCEDIR
df=df.drop(invalid_blogs)


Lectura dels blogs finalizada.

Exercici 1 Escriure una funció count_blogs(dataframe) que retorni el nombre total de blocs.


In [21]:
#Retorna el número total de blogs. 
def count_blogs(df):
    return len(set(df['id_blog']))

#prova
print count_blogs(df)


121

Exercici 2: Escriure una funció count_party_blogs(dataframe) que compte quants blogs hi ha per cadascun dels partits.


In [22]:
#Retorna una Series que conté el número de blogs per partit polític
def count_blogs_party(df):
    return pd.Series({ partit:len(df[df['partit_politic'] == partit])  for partit in set(df['partit_politic']) } ) #mode --> "like a boss" dict comprehensions!


#prova
print count_blogs_party(df)


CIU    32
ERC    29
ICV    26
PSC    34

Exercici 3: Escriure una funció count_words(dataframe,data_blogs) que que retorni un diccionari amb totes les paraules que han aparescut en els blogs, indicant la quantitat de cops i el nombre de blogs on ha sortit.
Possible format sortida: {word : {freq: valor;n_cops: valor}}


In [23]:
# Aquesta funció ha de contruir un diccionari que contingui totes les paraules que s'han trobat indicant 
# el total de cops que ha aparescut i el nombre de blogs on apareix
def count_words(df,data_blogs):
    apcount={}

    # Per cada blog, iteram sobre les diverses paraules (data_blogs)
    for key in data_blogs: 
        wc = data_blogs[key][1]
        
        # Per cada paraula donat un blog, contam els cops que surt en general 
        # i ho desam a apcount amb el format desitjat.
        for word,count in wc.items():
            apcount.setdefault(word,{'n_cops':0,'n_blogs':0})
            apcount[word]['n_blogs']+=1
            apcount[word]['n_cops']+=count             

    return apcount

In [24]:
# PROVA D'EXECUCIÓ:
d =count_words(df,data_blogs)
print d['comen']


{'n_blogs': 70, 'n_cops': 398}

Exercici 4: escriure una funció count_words_party(dataframe,dicc_text) que retorna un diccionari que conte el nombre de cops que ha aparescut cada paraula i el número de blogs on ha aparescut. Aquesta informació ha de ser dividida en els diferents gruos polítics.
Possible format sortida: {Partit_potilit : {word : {freq: valor;n_cops: valor} } }


In [25]:
#Cuenta la frecuencia de las palabras por un partido determinado
def count_words_party(df,dicc_text):
    words_partits = {}
    
    for partit in set(df['partit_politic']):    
        blogs_partit = list(df[df['partit_politic']==partit].index.values)
        data_blogs_partit = { i:data_blogs[i] for i in blogs_partit}
        words_partits[partit] = count_words(df,data_blogs_partit)   
          
    return words_partits

d_partits = count_words_party(df,data_blogs)
#prova específica
d_partits['PSC']['espa']


Out[25]:
{'n_blogs': 11, 'n_cops': 116}

Exercici 5: Calcular amb la funció topNword(df,words_partits,N) quines son les N paraules més representatives (les que apareixen amb més freqüència) de cadascun dels partits. Retorneu un diccionari amb els següent format: {PSC: llista_top_words_PSC; ERC: llista_top_words_ERC;...}
Teniu en compte que també haureu de filtrar aquelles paraules que apareixen en la majoria de blogs, així com també, les que únicament apareixen en un conjunt molt petit dels blogs.


In [26]:
# Cacula les N parules més representativa de cada partit polític. La sortida ha de 
# ser un diccionari on tenim tantes entrades com partits politics
# el valors de les entrardes ha de ser una llista amb les paraules seleccionades.

#Nota: les probabilitats de paraules (entre 10 i 50%) s'han  de calcular a partir de tots els blogs, NO d'algun blog particular.
def topNwords(df,words_partits,N):
    
    #Obtenim totes les paraules per partit amb format {PARTIT:{word1:count1},{word2:count2}...}
    d_partits = count_words_party(df,words_partits)
    tots = count_words(df,words_partits)
    
    best_partit = { i:[] for i in d_partits.keys() } #diccionari de sortida: { PARTIT1:[BestList1], PARTIT2:[BestList2],... }
    
    for partit in d_partits.keys():
        wc = d_partits[partit] #Obtenim el diccionari de paraules per partit

        # Ens quedem amb les paraules entre 10% - 50%:
        for w,c in wc.items( ):      
            frac = float(tots[w]['n_blogs'])/count_blogs(df)
            #print "w: ",w,", c: ",c
            #print frac
            if 0.1<frac<0.5:  best_partit[partit] = best_partit[partit] + [(w,c)] #.append((w,c)) --> No li agrada
                
        #Ordenem la sortida del diccionari per Partit        
        best_partit[partit] = sorted(best_partit[partit], key=lambda x: x[1]['n_cops'], reverse=True)[:N]
        
    return best_partit

In [27]:
best = topNwords(df,data_blogs,25)
best['PSC']


Out[27]:
[(u'las', {'n_blogs': 21, 'n_cops': 593}),
 (u'para', {'n_blogs': 22, 'n_cops': 545}),
 (u'con', {'n_blogs': 19, 'n_cops': 428}),
 (u'mso', {'n_blogs': 9, 'n_cops': 360}),
 (u'como', {'n_blogs': 15, 'n_cops': 281}),
 (u'este', {'n_blogs': 14, 'n_cops': 186}),
 (u'socialista', {'n_blogs': 27, 'n_cops': 172}),
 (u'socialistes', {'n_blogs': 24, 'n_cops': 142}),
 (u'dones', {'n_blogs': 17, 'n_cops': 142}),
 (u'todos', {'n_blogs': 15, 'n_cops': 141}),
 (u'style', {'n_blogs': 9, 'n_cops': 134}),
 (u'font', {'n_blogs': 13, 'n_cops': 128}),
 (u'estado', {'n_blogs': 9, 'n_cops': 126}),
 (u'gobierno', {'n_blogs': 10, 'n_cops': 126}),
 (u'sin', {'n_blogs': 16, 'n_cops': 122}),
 (u'sus', {'n_blogs': 12, 'n_cops': 120}),
 (u'europea', {'n_blogs': 12, 'n_cops': 120}),
 (u'hay', {'n_blogs': 15, 'n_cops': 117}),
 (u'espa', {'n_blogs': 11, 'n_cops': 116}),
 (u'federal', {'n_blogs': 11, 'n_cops': 116}),
 (u'porque', {'n_blogs': 13, 'n_cops': 110}),
 (u'tambien', {'n_blogs': 15, 'n_cops': 110}),
 (u'solo', {'n_blogs': 12, 'n_cops': 104}),
 (u'desde', {'n_blogs': 13, 'n_cops': 99}),
 (u'tic', {'n_blogs': 17, 'n_cops': 98})]

Exercici 6: Creeu el vector de característiques necessari per a fer l’entrenament del Naïve Bayes (funció create_features()).


In [28]:
# Crea el vector de características necesari per a l'entrenament del classificador Naive Bayes
# selected_words: ha de ser el diccionari que obteniu amb la funció topNWords amb les paraules seleccionades a partir de la funció TopNwords
# data_blogs : conté el diccionari amb la informació de cada blog
# Rertorna un diccionari que conté un np.array per a cadascun dels blog amb el vector de característiques corresponent(mireu l'exemple de l'enunciat)

def create_features(data_blogs,top_words):
    
    #Primerament obtenim les paraules NO repetides de top_words
    evaluated =[] 
    for partit in top_words.keys():
        for word in top_words[partit]:
            if word[0] not in evaluated: #si la paraula no ha sortit encara, la afegim a la llista d'evaluades
                evaluated.append(word[0])           
    #print evaluated 
    
    dict_feat_vector = {}
    # Per cada paraula que apareix al blog, si no esta a la llista de repetides la tractem (1 o 0)
    for blog in data_blogs.keys(): #per les paraules de cada blog
        vector=[]
        for word in evaluated: #Mirem si les paraules de top_words surten en el blog 
            if data_blogs[blog][1].has_key(word):
                vector.append(1) #Si surten -> posem un 1
            else:
                vector.append(0) #Altrament -> posem un 0
        dict_feat_vector[blog] = np.array(vector)
    return dict_feat_vector

In [29]:
N = 20 # Aquest parametre el podem canviar i fer proves per avaluar quin és el millor valorwords_partits=count_words(df,data_blogs)
valorwords_partits=topNwords(df,data_blogs,N)
dict_feat_vector = create_features(data_blogs,valorwords_partits)
#print dict_feat_vector

Exercici 7: Implementeu la funció d'aprenentatge del classificador Naïve Bayes (funció naive_bayes_learn()). La funció ha de mostrar el resultat obtingut per pantalla
* L'error d'entrenament L'error d'entrenament es troba calculant el percentatge d'errors que s'obtenen quan es fa el testeig amb les mateixes dades utilizades per fer entrenament (aprenentatge). Aquest error es un valor molt optimista de com funcionarà el clasificador i mai s'ha de prendre com a mesura per comparar clasificadors.


In [30]:
#Mètodo que implementa el clasificador Naive_Bayes.Ha de mostrar el resultat obtingut per pantalla
def naive_bayes(df,dict_feat_vector):
    M = len( set(df['partit_politic']) ) #Obtenim el nombre de categories
    
    #Obtenim un diccionari dels blogs per partit { key=partit: value=[ blogs del partit ] }
    data = { i:list(df[df['partit_politic']==i].index.values) for i in set(df['partit_politic']) } 
    #print data
    
    word_prob = {}
    for partit in set(df['partit_politic']):
        feat_vector = np.array([0]*len(dict_feat_vector[0])) #incialitzem un vector tipus np.array amb tants zeros com top N words
        for blog in data[partit]:# per a cada blog del partit
           feat_vector = feat_vector + dict_feat_vector[blog] # fem una suma element a element per cada un dels vectors caracteristics dels blogs d'un partit
        word_prob[partit] = (feat_vector + 1) / float(count_blogs_party(df)[partit] + M) #calculem les probabilitats de que, donat un partit politic, les paraules del vector caracteristic apareguin en els seus blogs 

    #---------------------------------------------------------------------------------------------------
    # Passam a calcular la probabilitat de que un blog (donat el seu featured vector) sigui d'una determinada categoria (Partit Pol.):
    #---------------------------------------------------------------------------------------------------
    blog_prob = { i:{partit:0.0 for partit in set(df['partit_politic']) } for i in dict_feat_vector.keys() } #Inicialitzam diccionari amb format { key=blog : values={ key=partit : values=probabilitat } }
    for partit in set(df['partit_politic']): 
        for blog in range(count_blogs(df)):
            
            for i, n in enumerate(dict_feat_vector[blog]): #recorregut de valors del feact_vector (0 o 1) junt amb el seu índex (per accedir al seu equivalent del word_prob)
                #print 'index:',i,'valor:',n
                
                if n==0:#si la paraula no apareix al blog, sumem la probabilitat de que no aparegui (complementari)
                    blog_prob[blog][partit] = blog_prob[blog][partit] + np.log(1-word_prob[partit][i]) 
                else: #si esta al blog, sumem la seva probabilitat d'apareixer
                    blog_prob[blog][partit] = blog_prob[blog][partit] + np.log(word_prob[partit][i])
    #print blog_prob  # --> tenim el diccionari amb les estimacions de bayes per cada partit donat un blog 
    
    #---------------------------------------------------------------------------------------------------
    # Passam a obtenir l'Output requerit:
    #---------------------------------------------------------------------------------------------------
    error = 0
    for partit in set(df['partit_politic']):
        cont = 0 #comptador d'encerts
        for blog in data[partit]:
             if(max(blog_prob[blog].values())== blog_prob[blog][partit]): #Si donat un blog, la probabilitat màxima de blog_prob coincideix amb el partit al que pertany...
                    cont+=1 #incrementem el comptdador!
        error += 100-((float(cont)/len(data[partit]))*100) # Obtenim, aculem l'error de cada percentatge.
        print ("%s encerts: %i / blogs: %i / %.2f %%" %(partit,cont,len(data[partit]), (float(cont)/len(data[partit]))*100 ))
    
    error/= M # Càlcul de la mitjana d'error de bayes 
    print("\nError naive bayes: %.2f %%" %error)

In [31]:
## EXEMPLE SORTIDA:
#PSC encerts: 24 / blogs: 34 / 70.59 %
#ICV encerts: 21 / blogs: 26 / 80.77 %
#CIU encerts: 29 / blogs: 32 / 90.62 %
#ERC encerts: 26 / blogs: 29 / 89.66 %
#
#Error naive bayes: 17.36 %

naive_bayes(df,dict_feat_vector)


PSC encerts: 20 / blogs: 34 / 58.82 %
ICV encerts: 20 / blogs: 26 / 76.92 %
CIU encerts: 23 / blogs: 32 / 71.88 %
ERC encerts: 24 / blogs: 29 / 82.76 %

Error naive bayes: 27.40 %

Exercici 8: Indiqueu l'error de generalització fent servir Leave-one-out (funció leave1out () )

* Aproximació a l'error de generalització fent servir Leave-one-out Una bona forma de veure com funcionaria el nostre classificador davant de dades sobre les quals no s'ha entrenat és fer servir l'estratègia leave-one-out. Aquesta estratègia entrena el classificador amb totes les dades d'entrenament menys amb una i fa el testeig sobre la dada que hem exclòs de l'entrenament. Aquest procés d'exclusió es repeteix per cadascuna de les dades d'entrenament. El percentatge d'errors fent servir aquesta estratègia permet comparar classificadors.


In [38]:
#Mètode per avaluar el classificador mitjançant la tècnica leave-one-out.  Ha de mostrar el resultat obtingut per pantalla.
def leave1out(df,dict_feat_vector):
    M = len( set(df['partit_politic']) ) #Obtenim el nombre de categories
    
    #Obtenim un diccionari dels blogs per partit { key=partit: value=[ blogs del partit ] }
    data = { i:list(df[df['partit_politic']==i].index.values) for i in set(df['partit_politic']) } 
    
    #Diccionari que contindrà els encerts per partit politic per totes les proves dels blogs exclosos:
    cont = { partit:0 for partit in set(df['partit_politic']) }
    
    #---------------------------------------------------------------------------------------------------
    # Apliquem Leave-On-Out: Entrenam el classificador amb totes les dades d'entrenament menys amb una
    #---------------------------------------------------------------------------------------------------
    for blog2leave_out in range(count_blogs(df)): #El procés es repeteix per cadascuna de les dades d'entrenament (blogs)

        word_prob = {}
        for partit in set(df['partit_politic']):
            feat_vector = np.array([0]*len(dict_feat_vector[0])) # incialitzem un vector tipus np.array amb tants zeros com top N words
            for blog in data[partit]: # per a cada blog del partit
               if blog != blog2leave_out: # no tenim en compte el blog a l'hora de calcular les probabilitats!!
                   feat_vector = feat_vector + dict_feat_vector[blog] # fem una suma element a element per cada un dels vectors caracteristics dels blogs d'un partit
            word_prob[partit] = (feat_vector + 1) / float(count_blogs_party(df)[partit] + M) # calculem les probabilitats de que, donat un partit politic, les paraules del vector caracteristic apareguin en els seus blogs 
    
        
        # Passam a calcular la probabilitat de que un blog (donat el seu featured vector) sigui d'una determinada categoria (Partit Pol.):
        blog_prob = { i:{partit:0.0 for partit in set(df['partit_politic']) } for i in dict_feat_vector.keys() if i != blog2leave_out} #Inicialitzam diccionari amb format { key=blog : values={ key=partit : values=probabilitat } }
        for partit in set(df['partit_politic']): 
            for blog in range(count_blogs(df)):
                if blog != blog2leave_out: #no tenim en compte el blog a l'hora de calcular les probabilitats!!
                    for i, n in enumerate(dict_feat_vector[blog]): #recorregut de valors del feact_vector (0 o 1) junt amb el seu índex (per accedir al seu equivalent del word_prob)
                        #print 'index:',i,'valor:',n
                        
                        if n==0:#si la paraula no apareix al blog, sumem la probabilitat de que no aparegui (complementari)
                            blog_prob[blog][partit] = blog_prob[blog][partit] + np.log(1-word_prob[partit][i]) 
                        else: #si esta al blog, sumem la seva probabilitat d'apareixer
                            blog_prob[blog][partit] = blog_prob[blog][partit] + np.log(word_prob[partit][i])
        #print blog_prob  # --> tenim el diccionari amb les estimacions de bayes per cada partit donat un blog 
        
        #---------------------------------------------------------------------------------------------------
        # Testeig sobre la dada que hem exclòs de l'entrenament (blog2leave_out):
        #---------------------------------------------------------------------------------------------------
        feat_vector = dict_feat_vector[blog2leave_out]
        #print feat_vector 
        prob2leave_out= { partit:0.0 for partit in set(df['partit_politic']) }
        #print prob2leave_out
        for partit in prob2leave_out.keys(): 
            for i, n in enumerate(feat_vector): #recorregut de valors del feact_vector (0 o 1) junt amb el seu índex (per accedir al seu equivalent del word_prob)
                #print 'index:',i,'valor:',n
                
                if n==0:#si la paraula no apareix al blog, sumem la probabilitat de que no aparegui (complementari)
                    prob2leave_out[partit] = prob2leave_out[partit] + np.log(1-word_prob[partit][i]) 
                else: #si esta al blog, sumem la seva probabilitat d'apareixer
                    prob2leave_out[partit] = prob2leave_out[partit] + np.log(word_prob[partit][i])  
        
        if(max(prob2leave_out.values())== prob2leave_out[df.ix[blog2leave_out]['partit_politic']]): #Si donat un blog, la probabilitat màxima de blog_prob coincideix amb el partit al que pertany...
            cont[df.ix[blog2leave_out]['partit_politic']]+=1 #incrementem el comptdador!

    #---------------------------------------------------------------------------------------------------
    # Generem OUTPUT:
    #---------------------------------------------------------------------------------------------------
    #print cont
    error = 0.0
    for partit in cont.keys():
        error += 100-((float(cont[partit])/len(data[partit]))*100) # Obtenim, aculem l'error de cada percentatge.
        print ("%s encerts: %i / blogs: %i / %.2f %%" %(partit,cont[partit],len(data[partit]), (float(cont[partit])/len(data[partit]))*100 ))
        
    error/= M # Càlcul de la mitjana d'error de bayes 
    print("\nError leave one out: %.2f %%" %error)

In [39]:
leave1out(df,dict_feat_vector)
## EXEMPLE SORTIDA:
#PSC encerts: 17 / blogs: 34 / 50.00 %
#ICV encerts: 17 / blogs: 26 / 65.38 %
#CIU encerts: 26 / blogs: 32 / 81.25 %
#ERC encerts: 24 / blogs: 29 / 82.76 %
#
#Error leave one out: 30.58 %


PSC encerts: 13 / blogs: 34 / 38.24 %
ICV encerts: 15 / blogs: 26 / 57.69 %
CIU encerts: 20 / blogs: 32 / 62.50 %
ERC encerts: 24 / blogs: 29 / 82.76 %

Error leave one out: 39.70 %

Exercici 9 Definició de la funció principal. Modifiqueu la funció per tal que s'ajusti a les vostres funcions.


In [42]:
# Main. Se hacen las llamadas a las diversas funciones para la correcta ejecucion del programa    
def main():
    unames = ['id_blog', 'partit_politic', 'nom', 'url']
    df = pd.read_table('blogs.dat',sep='::', header=None, names=unames)

    # EXEMPLE DE COM BAIXR D'UN CONJUNT DE BLOGS
    data_blogs,invalid_blogs=download_text(df)
    # EXEMPLE DE COM ELIMINAR AQUELLS BLOGS QUE NO S'HA POGUT ACCEDIR
    df=df.drop(invalid_blogs)
    
    #agafem el conjunt de paraules que utilziarem
    N = 20 # Aquest parametre el podem canviar i fer proves per avaluar quin és el millor valor
    top_words=topNwords(df,data_blogs,N)
    
    
    # SORTIDA ESPERADA :
    #Paraules més representatives
    #PSC 
    #[u'los', u'por', u'las', u'para', u'mas', u'psc', u'con', u'est', u'mso', u'tant', u'democr', u'partit', u'pais', u'social', u'nos', u'temps', u'nou', u'como', u'primer', u'sino']
    #ICV 
    #[u'los', u'mso', u'las', u'sobre', u'icv', u'social', u'ciutat', u'para', u'por', u'gent', u'est', u'ciu', u'contra', u'persones', u'font', u'fins', u'qui', u'crisi', u'part', u'any']
    #CIU 
    #[u'ciu', u'ciutat', u'nostre', u'barcelona', u'tant', u'nostra', u'sempre', u'pais', u'mso', u'ajuntament', u'fins', u'gent', u'any', u'aquests', u'gran', u'nou', u'avui', u'poble', u'president', u'est']
    #ERC 
    #[u'pais', u'esquerra', u'sobre', u'social', u'nostre', u'fins', u'espanyol', u'part', u'nostra', u'gent', u'tant', u'persones', u'nacional', u'poble', u'any', u'erc', u'encara', u'esta', u'aquests', u'catalana']
   
    
    #Creem el vector de característiques
    feature_vectors = create_features(data_blogs,top_words)
    
    #Creem i avaluem el cassificador amb les mateixes dades d'entranament 
    naive_bayes(df,feature_vectors) 
      
    #PSC encerts: 24 / blogs: 34 / 70.59 %
    #ICV encerts: 21 / blogs: 26 / 80.77 %
    #CIU encerts: 29 / blogs: 32 / 90.62 %
    #ERC encerts: 26 / blogs: 29 / 89.66 %
    #
    #Error naive bayes: 17.36 %
    
    #Avaluem el classificador mitjançant la tècnica de leave-one-out
    leave1out(df, feature_vectors)
    
    #PSC encerts: 17 / blogs: 34 / 50.00 %
    #ICV encerts: 17 / blogs: 26 / 65.38 %
    #CIU encerts: 26 / blogs: 32 / 81.25 %
    #ERC encerts: 24 / blogs: 29 / 82.76 %
    #
    #Error leave one out: 30.58 %

In [43]:
import time

t1 = time.clock()
main()
t2 = time.clock()
print 'Elapsed Time: ',t2-t1,'s'


PSC encerts: 20 / blogs: 34 / 58.82 %
ICV encerts: 20 / blogs: 26 / 76.92 %
CIU encerts: 23 / blogs: 32 / 71.88 %
ERC encerts: 24 / blogs: 29 / 82.76 %

Error naive bayes: 27.40 %
PSC encerts: 13 / blogs: 34 / 38.24 %
ICV encerts: 15 / blogs: 26 / 57.69 %
CIU encerts: 20 / blogs: 32 / 62.50 %
ERC encerts: 24 / blogs: 29 / 82.76 %

Error leave one out: 39.70 %
Elapsed Time:  16.61 s