1 Intro: factores

Hasta ahora hemos trabajado con diferentes tipos de variable: numéricas,lógicas, caracteres o “strings”, y diferentes clases de vectores, elementos numéricos y factores. Los factores son una forma “avanzada” para representar variables categóricas, donde cada categoría (o nivel) puede ser representado por un string. La utilidad de que exista un tipo especial de variable para representar a los factores no puede exagerarse. Los factores son extramadamente útiles para:

1.1 Funciones para trabajar con factores

Bueno pero, ¿de dónde salen los factores? Cualquier vector de cualquier tipo puede ser coercionado en un factor (lo convertís a prepo en un factor), a través de la función factor(). Veamos entonces cómo crear un factor a partir de una lista de strings:

condicion=c("congruente","congruente","congruente","incongruente","incongruente","neutro","neutro")
condicion.f=factor(condicion) # la función devuelve una copia de "condición", convertda en factor. Me gusta volcar esta copia en una variable que se llame igual pero a la que se le agrega ".f", para denotar que es un factor. 
condicion.f
## [1] congruente   congruente   congruente   incongruente incongruente
## [6] neutro       neutro      
## Levels: congruente incongruente neutro

Nótese que ahora aparece, en la descripción aparece una línea extra que dice “Levels:” seguida de un listado de los niveles de la variable. En ese listado aparecen todos los niveles de la variable, haya o no observaciones de esos niveles. Por ejemplo, quizás en el experimento hay otra condición, “mixta”. Quizás en la observaciones con las que estoy trabajando, no tengo ninguna instancia de la variable. De cualquier manera, al crear el factor puedo especificar la lista de niveles de la variable:

condicion.f2=factor(condicion,levels=c("congruente","incongruente","neutro","mixta"))
condicion.f2
## [1] congruente   congruente   congruente   incongruente incongruente
## [6] neutro       neutro      
## Levels: congruente incongruente neutro mixta

Sin embargo, lo mas frecuente es que suceda la situación contraria: es decir, tenemos un nivel de la variable para el cual no tenemos observaciones, y queremos eliminarlo. La función droplevels() nos ayuda:

condicion.f2=droplevels(condicion.f2)
condicion.f2
## [1] congruente   congruente   congruente   incongruente incongruente
## [6] neutro       neutro      
## Levels: congruente incongruente neutro

1.1.1 Niveles del factor

Podemos ver los niveles de una variable mediante la función levels()

levels(condicion.f)
## [1] "congruente"   "incongruente" "neutro"

Esta función no sólo permite ver los niveles, sino cambiarle las etiquetas a los niveles:

levels(condicion.f)=c("cond1","cond2","cond3")
condicion.f
## [1] cond1 cond1 cond1 cond2 cond2 cond3 cond3
## Levels: cond1 cond2 cond3

No siempre los factores provienen de variables que tienen strings de caracteres. Algunas veces, hay valores “numéricos” que en realidad son etiquetas de variables categóricas, como por ejemplo, cuando se usan indicadores numéricos para identificar a los sujetos. Típica situación: importamos un archivo de datos en el que tenemos una variable “sujeto”, que toma valores 1, 2, 3, etc. Bien podríamos poner “Sujeto1”, “Sujeto2”, etc. Por tanto, estamos ante una variable categórica. En este caso, conviene convertir la variable a factor:

sujetos=c(1,2,3,4,5,6)
sujetos.f=factor(sujetos)
#ya que estamos le cambiamos los niveles a algo más informativo
levels(sujetos.f)=c("s1","s2","s3","s4","s5","s6")

2 Paquetes de R

Uno de los aspectos más destacables de R es la existencia de una gran comunidad de usuarios que realizan aportes para ampliar la funcionalidad del software. Así como vimos que se pueden escribir funciones nuevas, cualquier persona puede juntar un conjunto de funciones (y otras cositas) en un paquete, una pieza de software modular que puede cargarse para usarla cuando uno quiera. El CRAN (Compehensive R Archive Network) es una red de servidores públicos en los que se publican las diferentes versiones de R, y además sirve como repositorio oficial para los miles de paquetes de R disponibles.

2.0.1 Paquetes más usados (mi lista personal)

Éstos son algunos de los paquetes que más uso, varios de ellos los veremos en este curso.

  • ggplot2 Mejora muchísimo las capacidades de ploteo. Lo veremos más adelante.
  • plyr Una “pinza” para trabajar con los datos. Lo veremos hoy.
  • reshape2 Otro clásico del “data-massage”, permite reformatear “fácilmente” data frames. Lo veremos la próxima clase
  • corrplot Útil para plotear correlaciones de forma “linda”
  • foreign Sirve para importar datos de SPSS, entre otros formatos
  • car Sólo lo uso para hacer ANOVAs de tipo III.
  • lsmeans Permite calcular medias marginales y hacer pruebas post-hoc en regresiones lineales. Ya llegaremos ahí.

2.1 Instalando y cargando paquetes

Los paquetes de R no vienen instalados por defecto con R (actualmente son más de 9000), sino que hay que instalarlos una única vez. Para instalarlos, hay que ir al menú Tools->Install Packages y escribir la lisa de paquetes a instlar, o simplemente ir a una consola y ejecutar:

install.packages(c("ggplot2","plyr"))  #instalemos ggplot2 y plyr

Luego de instalado, para poderlo usar, hay que “cargarlo” en memoria. Esto hay que hacerlo cada vez que abramos R y querramos usarlo. Basta con ejecutar:

library("plyr")  #cargamos "plyr" en memoria y lo podemos usar

3 “Sumarizar”" por factores

Una de las operaciones más comunes cuando trabajamos por datos, es obtener estadísticos descriptivos (número de datos, media, desvío, o cualquier otra cuenta con los datos) sumarizados para cada nivel de algún factor (por sujeto, por tarea, por condición), o por alguna combinación de factores (para cada sujeto, por condición).

Pongamos como ejemplo un experimento Stroop con 3 condiciones, congruente, incongruente y neutro. Tenemos además datos para dos sujetos, “s1” y “s2”. Si uno quisiera calcuar por ejemplo, la media de tiempos de reacción para cada condición, podríamos hacer lo que vimos en las clases anteriores. Indexar las líneas que corresponden a cada tarea, y luego tomar la media de la columna de los tiempos de reacción. ¿Qué pasa si ahora quisieramos tener la media de cada sujeto para cada condición? ¿Y si en realidad me interesa conocer el z-score de cada TR?

3.1 ‘plyr’: la pinza multiuso.

Hay muchas formas de hacer todo esto, pero algunos paquetes, como doBy o plyr hacen la vida mucho más fácil. Aquí veremos cómo funciona este último. Primero, debemos instalarlo, claro está. Esto se hace una única vez. Una vez instalado, hay que cargarlo.

En mi experiencia, por lejos, la función más usada de plyr es ddply. Es una función que actúa sobre d ata frames, y devuelve d ata frames. Por el nombre de la función empieza con dd. Por ejemplo, una que actúa sobre data frames y devuelve listas, se llama dlply. Y así.

Veamos a ddply en acción. Para eso, nos inventaremos unos datos de la tarea Stroop. Nada como una línea de R para generarlos. Copien, peguen, y ejecuten:

stroop=data.frame( rt=exp(rnorm(60,-0.4,.1)),condicion=rep(rep(c("congruente","incongruente","neutro"),times=c(15,10,5)),2),sujeto=rep(c("s1","s2"),each=30),nItem=rep(1:30,2))  #la magia de R... 1 línea, 1 experimento inventado

Para ver el data frame creado, usemos View(). Para ver su estructura, str(). Copiad, pegad, ejecutad.

View(stroop)
str(stroop)

La función ddply nos permite, en una línea, obtener las medias, los desvíos, o lo que uno quiera. Veamos cómo calcular las medias por condición:

ddply(stroop,"condicion",summarise,media=mean(rt))

Veamos la sintaxis. El primer argumento de la función es el objeto sobre el que va a operar, o sea, un data frame. El segundo argumento, es la variable (típicamente un factor) sobre el cual hacer los grupos. El tercer argumento puede tener 3 valores, o puede ignorarse. Aquí vemos summarize, que justamente, hace un sumario de los datos. Es decir, realiza los cálculos agrupando los datos por cada nivel de la variable de grupo. A partir del cuarto argumento, pueden incluirse los cálculos que queremos realizar. En este caso, simplmente le pedimos la media de los tiempos de reacción. Noten que tiene forma de asignación: aquí media es un nombre cualquiera: fíjense que en el data frame que devuelve, aparece una columna que se llama, justamente media.

¿Cómo hago entonces para calcular las medias por condición para cada sujeto por separado? Simplemente agregamos otra variable de grupo:

ddply(stroop,c("sujeto","condicion"),summarise,media=mean(rt))

Ya que estamos, podríamos calcular también los desvíos…

ddply(stroop,c("sujeto","condicion"),summarise,media=mean(rt), sd(rt))

Oops, me olvidé de asignarle un nombre a la columan del desvío.

Otro ejemplo: calcular el número de ensayos. Podemos hacerlo así:

ddply(stroop,c("sujeto","condicion"),summarise,nEnsayos=length(nItem))

¿Qué está pasando tras bambalinas? Bueno, lo primero que hace ddply es cortar el data frame en pedazos, según los niveles de las variables indicadas. Para cada combinación entonces tenemos un data frame que tiene las mismas columnas y valores, sólo que menos filas. En el ejemplo anterior, primer crea un data frame donde están sólo las filas de sujeto “s1” y condición “congruente”, luego otro con “s1” y la condición “incongruente”, etc. Para cada data frame, calcula la operación que se le indique sobre cualquiers de las columnas, para todas las filas. El resultado de las operaciones va a parar a una sola fila del data frame resultante.

En el ejemplo anterior, lo que quiero tener es el número de ensayos por cada combinación de sujeto y condición, y por tanto me interesa el número de filas de cada data frame “fileteado”. Entonces, tanto da si uso length(nItem) como en el ejemplo, o length(rt).

Veamos ahora un ejemplo un poco más complejo.

ddply(stroop,c("sujeto","condicion"),summarize,media=mean(rt), desv=sd(rt), umbralOutlier=media+3*desv )

Aquí estamos operando no sólo sobre las columnas que estaban antes en el data frame origial, sino que además estamos operando sobre las columnas que acabo de crear. Esto resulta extremadamente útil, ya lo verán.

3.2 Summarize vs Mutate

Mientras que summarize nos devuelve un data frame “sumarizado”, es decir, resumido, con menos filas que el data frame original, clave mutate nos devuelve un data frame con el mismo número de filas. Es más, nos devuelve una copia de nuestro data frame, con columnas agregadas. ¿Para qué puede servir? Para muchísimas cosas, por supuesto. Veamos un ejemplo:

Supónganse que quiero tener el z-score de cada tiempo de reacción. Pero quiero el z-score calculado para cada sujeto por separado. Y para cada condición. Es más, quiero el z-score para cada combinación de sujeto y condición. Piensen en cómo lo harían en SPSS. Ahora admiren el simpleza de la pinza todopoderosa:

ddply(stroop,c("sujeto","condicion"),mutate,media=mean(rt), desv=sd(rt), zscore=(rt-media)/desv )

Tadaaa!!