22 maggio 2009

Alberi di Classificazione in Excel

Il titolo del post è veramente "forte", nel senso che sto dicendo che in Excel è possibile ricorrere ai famosi Alberi di Classificazione, più noti con il termine di CART: Classification and Regression Trees. In realtà c'è il solito trucchetto: il dataset è in Excel, ma le elaborazioni le fa R. La comunicazione tra i due strumenti, come ampiamente ripetuto in questo blog, avviene mediante statconnDCOM.
Prima di tutto, quindi, è necessario avere sulla propria macchina i giusti tools, come già descritto in questo mio precedente post. In realtà alla data in cui scrivo è disponbile RAndFriends, che fino a poco tempo fa era disponibile solo in versione "light". Io direi quindi di installare tutto, evitando scelte personalizzate delle componenti . In genere, comunque, nelle applicazioni eseguite in XP non ho mai riscontrato problemi di installazione (come ad esempio installare solo statconnDCOM).

Per quanto riguarda gli Alberi di Classificazione si fa riferimento a questa monografia, il cui progetto è coperto da copyright ed il cui software originale è distribuito dalla Salford System. In R è disponibile la libreria rpart, il cui maintainer è Brian Ripley (che a sua volta ha "portato" in R il codice disponibile in SPLUS, vedi qui per maggiori info). In R quindi si parla di Recursive partitioning and regression trees, e considerando l'autore dovremmo poter avere una certa fiducia per quanto riguarda l'affidabilità del codice!
La teoria che è alla base è abbastanza semplice da un punto di vista matematico, io infatti ho ritenuto molto scorrevole da lettura del libro di Friedman.

Per quanto riguarda lo sviluppo in Excel, direi che è praticamente semplicissimo, non dovendo fare altro che riportare in VBA il codice che si è testato direttamente in R. Nell'esempio seguente ho lavorato in modalità "Macro programming", ossia ricorrendo a RExcel.xla. Nell'esercizio riportato in Access, invece, si è sviluppato in modalità "RDCOM Server". La prima, infatti, è possibile solo in Excel. Nessuno comunque ci vietava di fare ricorso anche qui in Excel all'oggetto StatConnector. Su questa differenza, comunque, scriverò presto un post (nel frattempo, quello che c'è da sapere è sempre qui!!!!!).

Passiamo ora alla descrizione del dataset utilizzato per l'esempio.
Ho pensato di toccare un argomento molto attuale, ossia la previsione dello spam. Trattasi di un tipico esempio di classificazione di una variabile dicotomica sulla base di informazioni relative al testo dell'email. Quindi la variabile risposta assumerà valori 0 (non spam) o 1 (spam).
Nel nostro dataset di esempio le variabile predittive sono semplicemente variabili di conteggio relative all presenza (in %) di alcune parole. Potete scaricare i dati e tutte le informazioni direttamente qui.
Riporto di seguito il codice associato ad un pulsante di comando. Al suo click ai avvia R in back-end e si caricano in R i dati disponibili in Excel (in un foglio nominato "dbxls"). Successivamente avviene la stima del modello e la visualizzazione dell'albero di classificazione finale. Ovviamente quando si chiude il server (Call Rinterface.StopRServer) si chiuderà anche la visualizzazione del grafico. Per evitare questo ho semplicemente messo un MsgBox che permette la visualizzazione dell'immagine fino al click su OK. Se volete riprovare ad eseguire il tutto dovete cambiare i nomi delle variabili predittive in v1, v2, ... , v57, mentre l'ultima sarà nominata "spam" (appunto la variabile dipendente).
Trattandosi di un esempio, non mi sono soffermato su vari aspetti della costruzione dell'Albero (quali previsione e pruning), ma la loro implementazione è semplice una volta capito il meccanismo.
Riporto il codice VBA:

'*************************************
Private Sub cmd_Click()
On Error GoTo errore
Dim rng As Range
Set rng = Worksheets("dbxls").Range("A1:BF4602")
Call Rinterface.StartRServer
Call Rinterface.RRun("library(rpart)")

Call Rinterface.PutDataFrame("db", rng)
Call Rinterface.RRun _
("db<-cbind(db[,1:57],spam=as.factor(db[,58]))") Call Rinterface.RRun("fit<-rpart(spam~.-spam, data=db)") Call Rinterface.RRun("plot(fit)") Call Rinterface.RRun("text(fit)")

MsgBox _
"Cliccando ok procedi con la chiusura di RServer"
Call Rinterface.StopRServer


Exit Sub
errore:
Call Rinterface.StopRServer

End Sub
'*************************************

La bellezza di tale metodo sta prima di tutto nell'immediatezza del grafico. Come potete vedere, facendo "cadere" un dato attraverso l'albero si arriva immediatamente alla soluzione:
  1. v53 è minore di 0.05? Sì, allora vai a destra dell'albero.
  2. v25 è >= 0.4? Sì, allora vai a destra dell'albero.
  3. spam=1
quindi la previsione più probabile è che l'email in questione sia spam! Una piccola curiosità: in tali dati la variabile più significativa (quella che determina lo split più discriminante) è la v53, ossia la presenza in percentuale del carattere dollaro: "$". Se tale percentuale supera il 5.5% si passa ad analizzare la v25, ossia la presenza della parola "hp". Se quest'ultima è a sua volta presente con una percentuale maggiore o uguale al 40% allora l'email è considerata spam. Sicuramente più interessante (in quanto meno scontata) è l'analisi dell'albero a sinistra del primo split:
  1. v53: percentage of character "$"
  2. v25: percentage of word "hp"
  3. v7: percentage of word "remove"
  4. v52: percentage of character "!"
  5. v57: sum of length of uninterrupted sequences of capital letters
  6. v16: percentage of word "free".
Infine una piccola considerazione.
La "facilità" di cui parlo sempre per l'uso di R ed Excel (o Access) è sempre legata ad una certa padronanza di R e di VBA, ovviamente! Nello stesso tempo è necessaria una certa conoscenza teorica del metodo che si andrà ad utilizzare. Inoltre l'uso contemporaneo di Excel/Access ed R è da me pensato per un'implementazione (facile) di modelli statistici di analisi e previsione da parte di uno statistico che lavora per utente finale "non esperto" (e che quindi vuole una soluzione finale del tipo "click and go" :-) .