El paquete ggplot2

Referencias bibliográficas

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

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:

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.96093
## 2  Femenino 51.03907
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.8928571
## 2         De acuerdo Lima-Callao 18.7500000
## 3      En desacuerdo Lima-Callao 62.0535714
## 4  Muy en desacuerdo Lima-Callao 13.1696429
## 5            NS / NR Lima-Callao  5.1339286
## 6     Muy de acuerdo       Norte  3.4375000
## 7         De acuerdo       Norte 25.9375000
## 8      En desacuerdo       Norte 55.3125000
## 9  Muy en desacuerdo       Norte 10.0000000
## 10           NS / NR       Norte  5.3125000
## 11    Muy de acuerdo         Sur  7.3469388
## 12        De acuerdo         Sur 31.0204082
## 13     En desacuerdo         Sur 43.6734694
## 14 Muy en desacuerdo         Sur 13.0612245
## 15           NS / NR         Sur  4.8979592
## 16    Muy de acuerdo      Centro 13.3333333
## 17        De acuerdo      Centro 22.8571429
## 18     En desacuerdo      Centro 35.2380952
## 19 Muy en desacuerdo      Centro 20.9523810
## 20           NS / NR      Centro  7.6190476
## 21    Muy de acuerdo     Oriente  0.0000000
## 22        De acuerdo     Oriente 20.0000000
## 23     En desacuerdo     Oriente 72.9411765
## 24 Muy en desacuerdo     Oriente  3.5294118
## 25           NS / NR     Oriente  3.5294118
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.58273      78.41727
##   (25,35]   26.56827      73.43173
##   (35,45]   29.33884      70.66116
##   (45,55]   35.22013      64.77987
##   (55,92]   37.89474      62.10526

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.58273
## 2  (25,35]    De acuerdo 26.56827
## 3  (35,45]    De acuerdo 29.33884
## 4  (45,55]    De acuerdo 35.22013
## 5  (55,92]    De acuerdo 37.89474
## 6  [18,25] En desacuerdo 78.41727
## 7  (25,35] En desacuerdo 73.43173
## 8  (35,45] En desacuerdo 70.66116
## 9  (45,55] En desacuerdo 64.77987
## 10 (55,92] En desacuerdo 62.10526

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.41727
## 7  (25,35] En desacuerdo 73.43173
## 8  (35,45] En desacuerdo 70.66116
## 9  (45,55] En desacuerdo 64.77987
## 10 (55,92] En desacuerdo 62.10526

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.71065 Lima-Callao
## Norte           25.83442       Norte
## Sur             26.26250         Sur
## Centro          26.22680      Centro
## Oriente         25.04762     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)