25 de agosto de 2014

El paquete ggplot2

  • Con el R es posible obtener el mismo resultado usando diferentes "caminos"
  • El paquete ggplot es uno de los entornos gráficos del R
  • Permite elaborar un gráfico a partir de un proceso de acumulación de capas o layers.
  • Tiene un cierto nivel de complejidad pero se obtienen resultados muy profesionales.

Referencias bibliográficas

Textos disponibles en la biblioteca de CCSS de la PUCP para el uso de ggplot2

  • Chang, Winston. 2012. R Graphics Cookbook. Sebastopol, CA: O’Reilly Media.

  • Field, Andy P. 2012. Discovering statistics using R. London; Thousand Oaks, Calif: Sage.

  • Wickham, Hadley. 2009. Ggplot2: elegant graphics for data analysis. New York: Springer.

Capas o layers en ggplot2

Un gráfico en ggplot2 puede tener varias capas, en su conjunto, las capas forman el gráfico al combinar:

  • Un data frame y las variables a ser graficadas
  • Una o varias capas indicando, entre otros:
    • El tipo de objeto a graficar o "geom" (barra, línea, punto, etc.)
    • Las transformaciones estadísticas a los datos
    • La posición de los objetos en el gráfico
  • Una escala para cada variable a ser graficada
  • Un sistema de coordenadas
  • La especificación de "facetas" del gráfico

Cargamos los datos de trabajo

Base de datos para estos ejercicios: Familia y roles de género 2012, a descargar de:

http://iop-data.pucp.edu.pe/busqueda/encuesta/71?

Descomprimir y grabar el archivo SPSS en el directorio de trabajo de R

# Importar la base de datos del SPSS a un data frame de R
library(foreign)
genero <- as.data.frame(read.spss("IOP_1212_01_B.sav"))
## re-encoding from UTF-8

Un gráfico simple: Gráfico de barras

El esquema básico de la gramática de ggplot es:

ggplot(data.frame, aes(x = variable)) + geom_forma()

library(ggplot2)
ggplot(genero, aes(x = SEXO)) + geom_bar()

Podemos guardar el gráfico en un objeto y añadir más capas:

gr1 <- ggplot(genero, aes(x = SEXO)) + geom_bar()
gr1 + xlab("Sexo del entrevistado") + ylab ("Número de casos")# etiquetas de los ejes

gr1 + xlab("Sexo del entrevistado") + ylab ("Número de casos") + 
  ggtitle("Sexo del entrevistado")

Podemos cambiar cómo se ven algunos elementos, por ejemplo, que las barras sean más pequeñas, en este caso la mitad (0.5) del tamaño por defecto:

gr1 <- ggplot(genero, aes(x = SEXO)) + geom_bar(width=0.5)
gr1 + xlab("Sexo del entrevistado") + ylab ("Número de casos") + 
  ggtitle("Sexo del entrevistado")

Con la opción "colour" y "fill" en el comando geom_bar podemos cambiar el color del controno de las barras y de su relleno

gr1 <- ggplot(genero, aes(x = SEXO)) + geom_bar(width=0.5, colour="blue", fill="red")
gr1 + xlab("Sexo del entrevistado") + ylab ("Número de casos") + 
   ggtitle("Sexo del entrevistado")

Temas

Los temas (theme) son un conjunto de opciones predefinidas sobre la apariencia de los objetos en ggplot. El tema por defecto del ggplot dibuja el gráfico sobre un fondo gris. Podemos cambiarlo a blanco y negro añadiendo el comando theme_bw()

gr1 <- ggplot(genero, aes(x = SEXO)) + geom_bar(width=0.5, colour="blue", fill="red")
gr1 + xlab("Sexo del entrevistado") + ylab ("Número de casos") + 
  ggtitle("Sexo del entrevistado") + theme_bw()

Complicando un poco más el asunto…

Hacer un gráfico de barras de sexo, pero con porcentajes en el eje vertical. Primero preparar los datos:

tab.sex1 <- as.data.frame(prop.table(table(genero$SEXO))*100)
tab.sex1
##        Var1  Freq
## 1 Masculino 48.96
## 2  Femenino 51.04
colnames(tab.sex1) <- c("Sexo", "Porcentaje")

Ahora el gráfico:

ggplot(tab.sex1, aes(x=Sexo, y=Porcentaje)) + geom_bar(stat="identity")

Afinamos el gráfico:

gr2 <- ggplot(tab.sex1, aes(x=Sexo, y = Porcentaje)) + 
  geom_bar(stat="identity", width=0.5, fill = "grey") 

gr2 + xlab(NULL) + ylab("% de entrevistados") + 
  ggtitle("Distribución de los entrevistados\n por sexo (%)") + theme_bw()

Otra manera de llegar al mismo resultado

library(scales) # requiere instalar el paquete "scales"
gr2.a <- ggplot(genero, aes(SEXO)) + 
  geom_bar(aes(SEXO, (..count..)/sum(..count..)), width=0.5, fill = "grey") 

gr2.a + scale_y_continuous(labels=percent) + xlab(NULL) + 
  ylab("% de casos") + ggtitle("Distribución de los\n entrevistados por sexo") +  theme_bw()

Gráfico de barras múltiples

La distribución de frecuencias de la pregunta P36 según sexo

ggplot(genero, aes(P36, fill=SEXO)) + geom_bar(position="dodge")

¿Cómo quitamos "NS/NR" y establecemos que la escala del eje vertical sean %?

gr3 <- ggplot(genero[genero$P36!="NS/NR", ], (aes(P36, fill=SEXO))) + 
geom_bar(aes(P36, (..count..)/sum(..count..)), position="dodge") + 
scale_y_continuous(labels=percent) + scale_fill_grey() + theme_bw()

gr3

Completamos el gráfico

gr3 + xlab(NULL) + ylab("% de casos") + ggtitle("¿Los demás miembros del hogar deberían pagarle un sueldo o salario\n al miembro del hogar que se encarga de las tareas domésticas?:\n % de respuestas según sexo del entrevistado")

Usando facets

Las facetas o "facets" en ggplot me permiten reproducir el mismo gráfico en diferentes niveles de un factor. Hagamos un gráfico de la distribución en % de la pregunta P51D, para los diferentes dominios geográficos

# Primero preparamos los datos en una tabla que convertimos en data frame
tab.5 <- as.data.frame(prop.table(table(genero$P51D, genero$DOMINIO), 2)*100)
tab.5
##                 Var1        Var2    Freq
## 1     Muy de acuerdo Lima-Callao  0.8929
## 2         De acuerdo Lima-Callao 18.7500
## 3      En desacuerdo Lima-Callao 62.0536
## 4  Muy en desacuerdo Lima-Callao 13.1696
## 5            NS / NR Lima-Callao  5.1339
## 6     Muy de acuerdo       Norte  3.4375
## 7         De acuerdo       Norte 25.9375
## 8      En desacuerdo       Norte 55.3125
## 9  Muy en desacuerdo       Norte 10.0000
## 10           NS / NR       Norte  5.3125
## 11    Muy de acuerdo         Sur  7.3469
## 12        De acuerdo         Sur 31.0204
## 13     En desacuerdo         Sur 43.6735
## 14 Muy en desacuerdo         Sur 13.0612
## 15           NS / NR         Sur  4.8980
## 16    Muy de acuerdo      Centro 13.3333
## 17        De acuerdo      Centro 22.8571
## 18     En desacuerdo      Centro 35.2381
## 19 Muy en desacuerdo      Centro 20.9524
## 20           NS / NR      Centro  7.6190
## 21    Muy de acuerdo     Oriente  0.0000
## 22        De acuerdo     Oriente 20.0000
## 23     En desacuerdo     Oriente 72.9412
## 24 Muy en desacuerdo     Oriente  3.5294
## 25           NS / NR     Oriente  3.5294
colnames(tab.5) <- c("Rptas", "Dominio", "Pct")

Este sería en gráfico de base:

gr4 <- ggplot(tab.5, aes(x=Rptas, y=Pct)) + geom_bar(stat="identity") 
gr4

Hacemos las facetas verticales

gr4 + facet_grid(Dominio ~.)

Si las queremos horizontales:

gr4 + facet_grid(.~ Dominio)

Si queremos que roten a lo largo de columnas y filas:

gr4 + facet_wrap(~ Dominio)

Podemos rotar el gráfico para que se aprecien mejor las etiquetas de respuestas

gr4 + coord_flip() + facet_wrap(~ Dominio)

Histogramas

Histograma básico de la pregunta P19A

ggplot(genero, aes(P19A)) + geom_histogram()
## stat_bin: binwidth defaulted to range/30. Use 'binwidth = x' to adjust this.

Amplitud de los intervalos del histograma

range(genero$P19A, na.rm=TRUE) 
## [1]   0 168
168/30
## [1] 5.6

Cambiamos la amplitud del intervalo

ggplot(genero, aes(P19A)) + geom_histogram(binwidth = 10)

Algunas mejoras:

 hist1 <- ggplot(genero, aes(P19A)) + geom_histogram(binwidth = 10, 
                                           fill="red", colour="black")
hist1

Podemos comparar el histograma de los hombres y de las mujeres

hist1 + facet_grid(SEXO ~.)

Curvas de Densidad de Kernell

Otra forma de ver la distribución de una variable cuantitativa, sobre la base del cálculo de densidades a partir del histograma:

## Las sintaxis nos dan el mismo resultado. La segunda evita dibujar la línea de abajo
ggplot(genero, aes(P23)) + geom_density()
ggplot(genero, aes(P23)) + geom_line(stat="density")

Curvas de densidad para grupos diferentes

Distribución de la edad en la que se casó según NSE

ggplot(genero, aes(P23)) + geom_line(stat="density") + facet_grid(NSEGrup ~.)

Distribución de la edad en la que se casó según NSE y Sexo

ggplot(genero, aes(P23)) + geom_line(stat="density") + facet_grid(NSEGrup ~ SEXO)

Gráfico de líneas

Objetivo: generar un gráfico de líneas que muestre cómo varía el % de personas que están en desacuerdo con la frase "Jamás tendría un amigo homosexual o una amiga lesbiana" (P51D)

Primero preparamos los datos:

Paso 1: Crear una variable categórica que agrupe las edades de los entrevistados en intervalos

edad <- genero$EDAD
gr.edad <- as.factor(cut(edad, breaks = c(18, 25, 35, 45, 55, 92), 
              include.lowest = TRUE))
table(gr.edad)
## gr.edad
## [18,25] (25,35] (35,45] (45,55] (55,92] 
##     288     283     258     173     201

Paso 2: Recodificamos la variable P51D en dos grupos, lo que están de acuerdo, los que están en desacuerdo. Los NS/NR los pasamos como NA

table(genero$P51D)
## 
##    Muy de acuerdo        De acuerdo     En desacuerdo Muy en desacuerdo 
##                47               284               661               148 
##           NS / NR 
##                63
p51dr <-as.numeric(genero$P51D)
table(p51dr)
## p51dr
##   1   2   3   4   5 
##  47 284 661 148  63
library(car)
p51dr <- factor(recode(p51dr, "1:2=1; 3:4=2; 5=NA")) # recodificamos
levels(p51dr) <- c("De acuerdo", "En desacuerdo")

Paso 3: Generamos una tabla de % cruzados de respuestas a la pregunta P51D según grupos de edad y la convertimos en un data frame

tab1 <- prop.table(table(gr.edad, p51dr), 1)*100
tab1
##          p51dr
## gr.edad   De acuerdo En desacuerdo
##   [18,25]      21.58         78.42
##   (25,35]      26.57         73.43
##   (35,45]      29.34         70.66
##   (45,55]      35.22         64.78
##   (55,92]      37.89         62.11

Paso 4: Convertimos la tabla generada en un data frame

df.tab1 <- data.frame(tab1)
df.tab1
##    gr.edad         p51dr  Freq
## 1  [18,25]    De acuerdo 21.58
## 2  (25,35]    De acuerdo 26.57
## 3  (35,45]    De acuerdo 29.34
## 4  (45,55]    De acuerdo 35.22
## 5  (55,92]    De acuerdo 37.89
## 6  [18,25] En desacuerdo 78.42
## 7  (25,35] En desacuerdo 73.43
## 8  (35,45] En desacuerdo 70.66
## 9  (45,55] En desacuerdo 64.78
## 10 (55,92] En desacuerdo 62.11

Nos interesa representar sólo los % en desacuerdo. Para ello seleccionamos del data frame unicamente los registros que corresponden a los "desacuerdo"

df.tab2 <- subset(df.tab1, p51dr=="En desacuerdo")
df.tab2
##    gr.edad         p51dr  Freq
## 6  [18,25] En desacuerdo 78.42
## 7  (25,35] En desacuerdo 73.43
## 8  (35,45] En desacuerdo 70.66
## 9  (45,55] En desacuerdo 64.78
## 10 (55,92] En desacuerdo 62.11

Ahora contamos con los datos para elaborar el gráfico de líneas:

graf.linea1 <- ggplot(df.tab2, aes(x=gr.edad, y=Freq, group=1)) + geom_line() 
graf.linea1

Podemos añadir puntos al la línea para marcar mejor el dato correspondiente a los grupos de edad:

graf.linea2 <- graf.linea1 + geom_line() + geom_point()
graf.linea2

Podemos ajustar la escala del eje Y para evitar demasiadas distorsiones y añadir títulos a los ejes y al gráfico.

graf.linea2 + ylim(0, 100) + xlab("Grupos de edad") + ylab("% de casos") + 
  ggtitle("Porcentaje de personas en desacuerdo con la afirmación:\n \"Jamás tendría un amigo homosexual o una amiga lesbiana\",\n según grupo de edad")

Gráfico de puntos

Una alternativa a un gráfico de barras, puede ser un gráfico de puntos, conocido como "Cleveland Dot Plot". Observe este data frame que contiene el promedio de la edad considerada como ideal para que una mujer se case según dominio geográfico:

# Sintaxis para generar la tabla de datos
eideal.m <- genero$P1
eideal.m[genero$P1==99] <- NA
df2 <- data.frame(tapply(eideal.m, genero$DOMINIO, mean, na.rm = TRUE))
df2$dominio <- rownames(df2)
colnames(df2) <- c("mean.ideal.m", "dominio")

## Tabla de datos en forma de un data.frame
df2
##             mean.ideal.m     dominio
## Lima-Callao        26.71 Lima-Callao
## Norte              25.83       Norte
## Sur                26.26         Sur
## Centro             26.23      Centro
## Oriente            25.05     Oriente

Aquí el grafico de puntos tipo "Cleveland":

ggplot(df2, aes(x=mean.ideal.m, y=dominio)) + 
geom_point(size=3)

El mismo gráfico con los datos ordenados según la magnitud de la media

ggplot(df2, aes(x=mean.ideal.m, y=reorder(dominio, mean.ideal.m))) + 
geom_point(size=3)