Exemple de Reference Classes

A la última reunió vam parlar sobre les reference classes de R, de les quals en destaquem dos utilitats principals:

1) Organitzar millor el codi: més llegible, més net, més classificat (i per tant més fàcil de detectar errors).

2) Quan es passa un argument a una funció, aquest és copiat automàticament per R. Per tant, al passar “data.frames” o “matrix” molt grans a funcions, triguem més temps i gastem més memoria, i en definitiva, menys eficiència. Les reference classes son la manera d’evitar que els arguments es copiin.

Per crear noves reference Classes (i per veure els detalls de com fer-ho) recomanaria seguir els passos de la presentació: definició, initialize(), … En aquest post, posarem un (esborrany) exemple d’utilització de les reference Classes.

Imaginem que habitualment utilitzem R per fer gràfiques. Treballem amb fitxers de dades que ocupen varis megues i com fa temps que fem servir R, el nostre codi creix cada dia més. Aleshores el nostre codi podria assemblar-se a


library(c("ggplot2", ...))
### Lectura de dades: llegim les dades possiblement de varis fitxers.
...
### Neteja i manipulació de dades: treiem alguns valors que no tenen sentit, afegim columnes, canviem el format d'algunes variables (de "character" a "Date", de "character" a "factor", etc.),...
...
### Seleccionem subset de variables o d'observacions (per exemple si tenim dades per dies o per hores)
...
### Fem estadístiques: mitjanes, desviacions estandar, alguna regressió lineal possiblement,...
...
### Fem gràfiques
...
### Escrivim resultats
...

Si ho escrivim tot en un mateix fitxer, queda bàsicament il·legible i poc manipulable (quan es vol canviar alguna cosa, s’ha de borrar codi, o comentar, fent el fitxer encara més gran).

Una primera solució és escriure una funció per a cada una de les tasques, del tipus:

dades = read.table(nomFitxer)
dades = funcioNeteja(dades, llista.dopcions)

 

Podem destacar-ne alguns inconvenients:
1) al passar l’objecte dades, R el copia dintre de la funció. Si l’objecte és gran, això és una ineficiència.
2) per a cada funció ens hem de preocupar de quins paràmetres passar. O bé creem una “list” o les capçaleres de les funcions i la seva crida tendeixen a ser llargues.
3) si la funció retorna varis objectes (que poden ser grossos), com que les funcions nomès retornen un objecte, hem de crear una llista amb tots els objectes que vulguem crear i després recuperar-los un a un.
4) creem funcions pensant en set de dades en particular, però alhora de la veritat no hi ha cap lligam entre la funció i la estructura de dades que puguem tenir.

5) Si no som realment ordenats, el codi no queda tan estructurat com en el primer exemple, les tasques no queden tan ben escrites/definides i tot queda una mica (o molt) difòs.

Les reference classes donen un marc per a que tot això no passi. Una reference class és una estructura que conté objectes (que anomenarem fields) i funcions (que anomenarem methods). La idea es que cada classe es responsabilitza dels seus objectes. Si els objectes no tenen el format o informació necessària, és per culpa d’algun dels seus methods.

Un cop definida l’estructura, crearem objectes (que anomenarem objectes o instàcies) amb aquesta estructura. Per exemple:

referenceClass = estructuraDades

fields = dades,

                parametres de neteja, manipulacio,…

                fitxers de lectura,

                opcions gràfiques,

                …..

methods = initialize(nomsFitxers, parametres,…),

                       funcioLectura(),

                       funcioNeteja(),

                       funcioManipulacio(),

                       ….

Els objectes es creen de la següent manera:


dadesNeixaments = estructuraDades$new(parametres,...)

dadesNeixaments té ara tots els objectes que li haguem donat i totes les funcions que li haguem definit. D’aquesta manera, els methods estan fortament lligats a les característiques de l’objecte que estiguem utilitzant.

Els methods no necessiten cap dels fields com a arguments (pero si que poden acceptar altres arguments), perque ja entenen que utilitzaran els fields de la reference class. És a dir, suposem que les nostres dades son un data.frame i volem canviar la classe del primer camp, aleshores podem escriure la funció


funcioNeteja =function(){

class(dades[,1]) <<- "numeric"

}

A més, qualsevol modificació que fem a dades es manté un cop la crida de la funció acaba (veure presentació).

Per altra banda, aquesta nova instància, “dadesNeixaments”, si la passem a una funció, aquesta no en fa la còpia (veure presentació). És més, no necessito pensar quins parametres o objectes haig de passar-li a la funció: passo la instancia sencera i allà està tot el que faci referencia al meu set de dades ( i de manera òptima perque no en faig cap còpia).


novaFuncio = functio(dadesNeixaments){

plot(dadesNeixaments[,1],...)

....

}

D’aquesta manera, els inconvenients (1)-(5) queden resolts. Finalment el codi (sense les definicions de la classe, que estarien al fitxer “estructuraDades.r”) tindria la forma:


library(c("ggplot2", ...))
source("estructuraDades.r")
### Definim parametres i opcions
nomFitxers = c("fitxer1", ...)
parametres = c(par1,...)
### Creem un nou objecte
dadesNeixaments = estructuraDades$new(nomFitxers, parametres,...)
### Lectura
dadesNeixaments$lectura()
### Neteja i manipulació de dades
dadesNeixaments$manipulacio()
### Seleccionem subset de variables o d'observacions
dadesNeixaments$seleccio()
### Estadístiques
dadesNeixaments$estadistiques()
### Gràfiques
dadesNeixaments$grafiques()
### Resultats
dadesNeixaments$resultats()

Referencies:
1) ?setRefClass ( a la consola de R)
2) https://github.com/hadley/devtools/wiki

Aleix, RUGBCN

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: