En R tenemos disponibles distintos sistemas gráficos, aunque los más comunes son:
R Core Team and contributors
). El núcleo (core en inglés) gráfico en R se encuentra en los paquetes graphics (contiene las funciones de gráficos base: plot, hist, etc.) y grDevices (que implementa los distintos dispositivos gráficos: pdf, ps, png, …).Deepayan Sarkar
). El libro Lattice. Multivariate Data Visualization with R (http://www.springer.com/gp/book/9780387759685) proporciona una gran cantidad de ejemplos (con código R).Lattice (Deepayan Sarkar, 2008)
Hadley Wickham
). Hadley Wickham ha escrito un libro en el que explica de forma muy didáctica los fundamentos para elaborar gráficos con este paquete. En este enlace lo encontraréis http://link.springer.com/book/10.1007%2F978-3-319-24277-4 (desde un ordenador de la Universitat de València se puede descargar, ¡tenemos acceso!).ggplot2 (Hadley Wickham, 2016)
En este tutorial vamos a aprender a crear gráficos (básicos), puesto que constituyen herramientas enormemente valiosas para transmitir información. “Más vale una imagen que mil palabras”.
Comenzaremos haciendo distintos tipos de gráficos usando el sistema base y después los replicaremos utilizando ggplot2.
Para realizar los gráficos de este tutorial vamos a utilizar datos que se encuentran accesibles en el paquete AER. Por tanto, cargamos la librería AER (si no la tenemos hay que instalar el paquete).
if(!require("AER")) {install.packages("AER")}
El paquete AER contiene más de 100 bases de datos (data sets). ¿Recordamos cómo podemos obtener el listado de datos que contiene un paquete?
data(package="AER")
La base de datos que utilizaremos para crear las gráficas de este tutorial es CPS1985 (más información aquí). Primero cargamos la librería y luego cargamos la base de datos.
library(AER)
data("CPS1985")
¿Cuántas observaciones hay? ¿Qué variables contiene la base de datos CPS1985?
dim(CPS1985)
## [1] 534 11
names(CPS1985)
## [1] "wage" "education" "experience" "age" "ethnicity"
## [6] "region" "gender" "occupation" "sector" "union"
## [11] "married"
Así pues, tenemos un total de 554 observaciones y las siguientes 11 variables:
Antes de comenzar con los gráficos, vamos a obtener un resumen de las variables.
summary(CPS1985)
## wage education experience age
## Min. : 1.000 Min. : 2.00 Min. : 0.00 Min. :18.00
## 1st Qu.: 5.250 1st Qu.:12.00 1st Qu.: 8.00 1st Qu.:28.00
## Median : 7.780 Median :12.00 Median :15.00 Median :35.00
## Mean : 9.024 Mean :13.02 Mean :17.82 Mean :36.83
## 3rd Qu.:11.250 3rd Qu.:15.00 3rd Qu.:26.00 3rd Qu.:44.00
## Max. :44.500 Max. :18.00 Max. :55.00 Max. :64.00
## ethnicity region gender occupation
## cauc :440 south:156 male :289 worker :156
## hispanic: 27 other:378 female:245 technical :105
## other : 67 services : 83
## office : 97
## sales : 38
## management: 55
## sector union married
## manufacturing: 99 no :438 no :184
## construction : 24 yes: 96 yes:350
## other :411
##
##
##
En el caso de variables cuantitativas, la función summary() proporciona como resumen de la variable los “cinco números” (mínimo, cuartil 1, cuartil 2, cuartil 3, máximo) y la media. Si el variable es categórica proporciona el total de observaciones en cada categoría.
Bien. Ya tenemos una idea de los datos. Ahora…a representarlos!!!!
Vamos a comenzar por representar un (sencillo) histograma del Salario (wage). La función que utilizamos es hist().
hist(CPS1985$wage)
Así queda un poco feo. ¿Qué podemos hacer para mejorarlo?
Antes que nada, como no vamos a abrir ninguna otra base de datos, para evitar tener que escribir continuamente CPS1985$variable, hacemos un attach. Esto nos permite acceder directamente a las variables de CPS1985 (y nos evitamos escribir mucho).
attach(CPS1985) # acceder directamente a las variables (columnas)
p<-hist(wage)
text(p$mids,p$counts,labels=p$counts, adj=c(0.5, -0.5), cex=.5)
Ahora si, vamos a mejorar el histograma. Pero para ello tenemos que conocer los argumentos de la función hist(). Por tanto, pedimos la ayuda.
?hist()
En primer lugar, para mejorar nuestro histograma, modificamos el número de intervalos (breaks) (se obtiene con la Regla de Sturges, ¡cuánto tiempo sin verlo!). A continuación, escribimos el título del gráfico y rotulamos los ejes.
hist(wage,
breaks = 20, # número de intervalos
main = "Distribución del Salario (dólares por hora)", # título del gráfico
xlab = "Salario", # título del eje x
ylab = "Número de personas" # título del eje y
)
¿Qué más podemos hacer? Por ejemplo, podemos modificar el rango de valores del eje x (y/o del eje y) y colorear el histograma. La paleta de colores que por defecto tiene R se puede consultar ejecutando la función palette(). Para consultar nombres de colores podemos ejecutar en la consola colors() o hacer clic aquí.
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
xlab = "Salario",
ylab = "Número de personas",
xlim = c(0,50), # rango de valores del eje x
col = "blue", # color de relleno
border = "white" # color del borde del rectángulo
)
Si en lugar de representar la frecuencia absoluta estamos interesados en representar la frecuencia relativa (probability Density ) tenemos que cambiar el argumento freq de la función hist().
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
freq = FALSE, # frecuencias relativas (en tanto por uno)
xlab = "Salario",
ylab = "Frecuencia relativa (en tanto por uno)",
xlim = c(0,50),
ylim =c (0,0.15), # cambiamos rango de valores del eje y
col = "blue",
border = "white"
)
En ocasiones, por no decir casi siempre, nos gusta añadir al histograma la curva de la distribución normal; vamos a ver cómo hacerlo.
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
freq = FALSE,
xlab = "Salario",
ylab = "Frecuencia relativa (en tanto por uno)",
xlim = c(0,50),
ylim = c(0,0.15),
col = "blue",
border = "white"
)
lines(density(wage), col="yellow", lwd=3) # dibujamos la distribución normal empírica con los datos que tenemos
lines(density(wage, adjust=2), col="red", lwd=3, lty=2) # adjust=2 lo que hace es suavizar un poco la curva (probad con adjust=1 y adjust=5 para ver las diferencias).
En el gráfico anterior se han utilizado los argumentos lwd y lty. El primero se refiere al grosor de la línea, el segundo al tipo de línea.
La siguiente gráfica sería una alternativa a la anterior.
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
freq = FALSE,
xlab = "Salario",
ylab = "Frecuencia relativa (en tanto por uno)",
xlim = c(0,50),
ylim = c(0,0.15),
col = "blue",
border = "white"
)
curve(dnorm(x, mean=mean(wage), sd=sd(wage)), add=TRUE, col="red", lwd=3) # aquí simulamos valores de una normal con la misma media y deviación típica que wage y la representamos.
En los gráficos anteriores hemos seleccionado el color (muy básico) para el relleno o para el borde escribiendo directamente el nombre (del color). El tema de los colores es otro universo. Aquí tenéis una cheat sheet de colores. Esta web, o esta otra web, también están bien para orientarnos sobre la selección de (códigos) de colores. Cuando hagamos gráficos con ggplot2 comentaremos el tema de las paletas de colores. Ahora, vamos a replicar nuestro histograma pero seleccionamos un color mediante su código Hex.
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
freq = FALSE,
xlab = "Salario",
ylab = "Frecuencia relativa (en tanto por uno)",
xlim = c(0,50),
ylim = c(0,0.15),
col = "#ff9900",
border = "#0040ff"
)
curve(dnorm(x, mean=mean(wage), sd=sd(wage)), add=TRUE, col="#00ff00", lwd=3)
Por último, tal vez estemos interesados en indicar/señalar la posición de la media (o cualquier otra medida) en el histograma. Para hacer esto utilizamos la función abline().
hist(wage,
breaks = 20,
main = "Distribución del Salario (dólares por hora)",
freq = FALSE,
xlab = "Salario",
ylab = "Frecuencia relativa (en tanto por uno)",
xlim = c(0,50),
ylim = c(0,0.15),
col = "#ff9900",
border = "#0040ff"
)
curve(dnorm(x, mean=mean(wage), sd=sd(wage)), add=TRUE, col="#00ff00", lwd=3)
abline(v=mean(wage), lwd=2, lty=3, col="darkblue") # añadimos posición de la media. Recordemos que e argumento "lwd"" hace referencia al grosor de la línea y "lty"" al tipo de línea.
¿Suficiente para un curso introductorio?
Es momento de practicar lo aprendido. Ahí van dos tareas…
En los gráficos Boxplot (o Box and Whiskers, más conocidos como Caja y Bigotes) se representan los cinco números (mínimo, cuartil 1, mediana, cuartil 3 y máximo), lo que nos puede ayudar a hacernos una idea de la asimetría de la distribución. Los gráficos de Caja y Bigotes también permiten detectar valores atípicos y outliers.
La función para representar los diagramas de Caja y Bigotes es boxplot(). Como siempre, es recomendable echar un vistazo a los argumentos de la función.
?boxplot # recordad que también podemos escribir en la consola el nombre de la función y pulsar la tecla F1.
Vamos a hacer el boxplot de la variable wage.
boxplot(wage)
Hay va la pregunta habitual… ¿Qué podemos hacer para mejorar este gráfico?
Podemos incluir el título principal del gráfico, el nombre de la variable que estamos representando, colorear la caja y los bordes… (Recordad la hoja o la función donde podemos encontrar los nombres de colores).
boxplot(wage,
main = "Salario (dólares por hora)",
ylab = "Salario",
col = "pink",
border = "purple"
)
¿Cómo son los salarios de hombres y mujeres? Vamos a ver su distribución en un boxplot (básico).
boxplot(wage ~ gender)
Incluimos el título del gráfico e información sobre las variables que estamos representando.
boxplot(wage ~ gender,
main = "Salario (dólares hora) según género",
ylab = "Salario",
names = c("Hombres","Mujeres") # cambiamos la etiqueta de la variable cualitativa
)
Muchos colegas prefieren presentar el boxplot en horizontal (¡igual ya me lo habéis preguntado!). También resulta muy ilustrativo incluir la media de salario según género. Para hacer esto último utilizamos la función points().
datos_mujeres <- subset(CPS1985, gender=="female") # seleccionamos los datos de las mujeres
datos_hombres <- subset(CPS1985, gender=="male") # seleccionamos los datos de los hombres
boxplot(wage ~ gender,
main = "Salario (dólares hora) según género",
ylab = "Salario",
names = c("Hombres","Mujeres"),
horizontal = T # boxplot en posición horizontal
)
points(mean(datos_hombres$wage), 1, pch=25, bg="red") # incluimos salario medio de hombres
points(mean(datos_mujeres$wage), 2, pch=22, bg="blue") # incluimos salario medio de mujeres
Para colorear el diagrama de Caja y Bigotes vamos a utilizar las paletas de colores (bueno, las paletas se utilizan para cualquier gráfico). Aquí podéis encontrar una Cheat sheet sobre este tema. Paletas de colores hay muchas, pero de las más utilizadas son:
Para gráficos sencillos también se utilizan las paletas topo.colors, cm.colors, heat.colors o terrain.colors. Nosotros mismos podemos definir nuestra propia paleta (hacer clic aquí para ver cómo hacerlo).
Veamos el efecto de algunas de las paletas a las que se ha hecho referencia.
boxplot(wage ~ gender,
main = "Salario (dólares hora) según género",
ylab = "Salario",
names = c("Hombres","Mujeres"),
horizontal = T,
col = rainbow(2) # el número hace referencia al número de colores a utilizar
# introducir el algumento alpha (entre 0 y 1) para transperencias de color. Observar los cambios
)
boxplot(wage ~ gender,
main = "Salario (dólares hora) según género",
ylab = "Salario",
names = c("Hombres","Mujeres"),
horizontal = T,
col = heat.colors(2)
)
library(colorspace)
#pal <- choose_palette(diverge_hcl) # para seleccionar la paleta que queremos
boxplot(wage ~ gender,
main = "Salario (dólares hora) según género",
ylab = "Salario",
names = c("Hombres","Mujeres"),
horizontal = T,
#col = pal(2) # seleccionamos 2 colores de la paleta elegida
)
Tal vez no hayamos percibido el efecto de las paletas porque hemos representado el salario (wage) en función de una variable categórica que solo tenía dos categorías (gender). Realizar la siguiente tarea.
boxplot(wage ~ occupation,
main = "Salario (dólares hora) según ocupación",
ylab = "Salario",
xlab = "Ocupación",
col = rainbow(6, alpha=0.2),
border = rainbow(6, v=0.6)
)
Con lo que hemos visto hasta ahora de gráficos, podemos darnos cuenta que en R tenemos realmente control sobre todos los elementos que componen el gráfico. Para un ejemplo, observemos los detalles del siguiente boxplot (basado en este post).
a <- rainbow(6)
b <- rainbow(6, alpha=0.2)
c <- rainbow(6, v=0.5)
boxplot(wage ~ occupation,
col = b, # color de relleno de la caja
boxcol = c, # color del borde de la caja
medcol = c, # color de la mediana (línea dentro de la caja)
whiskcol = a, # color de los bigotes
staplecol = c, # color de las líneas finales de los bigotes
outcol = c, # color de los puntos (atípicos y outliers)
outbg = c, # relleno de los puntos
pch = 20, # forma de los puntos
cex = 1, # tamaño de los puntos
horizontal = T, # cambiamos la posición de los diagramas a horizontal
main = "Salario (dólares hora) según ocupación",
ylab = "Ocupación",
xlab = "Salario"
)
Como vemos, al cambiar la orientación del gráfico se originan ciertos problemas de visualización. Debemos modificar los márgenes. Esta Cheat sheet es una buena ayuda para modificar los parámetros re
opar <- par() # guardamos los parámetros por defecto
par(mar=c(5,6,4,2)) # modificamos los márgenes (inferios, izquierdo, superior, derecho)
a <- rainbow(6)
b <- rainbow(6, alpha=0.2)
c <- rainbow(6, v=0.5)
boxplot(wage ~ occupation,
col = b,
boxcol = c,
medcol = c,
whiskcol = a,
staplecol = c,
outcol = c,
outbg = c,
pch = 20,
cex = 1,
horizontal = T,
main ="Salario (dólares hora) según ocupación",
xlab = "Salario",
ylim = c(0,50), # ampliamos el rango del eje
las = 1, # cambiamos la orientación de las etiquetas
frame = F # eliminamos el marco del gráfico
)
par(opar) # volvemos a los parámetros originales
Otra opción sería hacer referencia a las categorías de ocupación en una leyenda anexa al gráfico. Por ejemplo:
a <- rainbow(6)
b <- rainbow(6, alpha=0.2)
c <- rainbow(6, v=0.5)
boxplot(wage ~ occupation,
col = b,
boxcol = c,
medcol = c,
whiskcol = a,
staplecol = c,
outcol = c,
outbg = c,
pch = 20,
cex = 1,
horizontal = T,
main = "Salario (dólares hora) según ocupación",
xlab = "Salario",
yaxt = "n" # elminamos el eje y
)
legend("bottomright", title="Salario (dólares hora)", levels(occupation), fill=b)
Los diagramas de dispersión permiten efectuar un diagnóstico visual de la posible relación (funcional) entre dos variables de naturaleza cuantitativa. La función que permite realizar este tipo de gráfico es plot(). Si solicitamos la ayuda de R sobre esta función (?plot), veremos que la función plot tiene un argumento llamado type que sirve para seleccionar el tipo de gráfico a dibujar. Las opciones de type son:
Por defecto la opción plot es type=p. Por tanto, si queremos hacer un diagrama de dispersión para representar la nube de puntos no será necesario indicar el tipo de gráfico.
Como la variable Salario presenta una importante dispersión y asimetría (ver histograma), vamos a representar el logaritmo del salario (log(wage)) en función de la experiencia (experience).
plot (experience, log(wage))
También lo podíamos haber escrito así:
plot (log(wage) ~ experience)
Como siempre, lo primero para mejorar la presentación de nuestro gráfico de dispersión será incluir el título del gráfico y de los ejes (también podemos incluir esta información utilizando la función title() (echad un vistazo a esta función, ?title).
plot (log(wage) ~ experience,
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
¿Queremos ver el salario en función de la experiencia y del género?
plot (log(wage) ~ experience,
col = rainbow(2)[c(gender)], # asigna color a los puntos en función del género
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
legend("topright", pch=16, col=rainbow(2), legend=c("Hombres","Mujeres")) # añadimos una leyenda al gráfico
Con la función abline() podemos añadir la recta de regresión en el diagrama de dispersión. Por ahora únicamente vamos a limitarnos a ver el efecto de la recta de regresión sobre el diagrama de dispersión. En otro curso estudiaremos, con cierta profundidad, los modelos de regresión con R.
# En primer lugar, obtenemos las regresiones
regresion_hombres <- lm(log(wage) ~ experience, data=CPS1985, subset=gender=="male")
regresion_mujeres <- lm(log(wage) ~ experience, data=CPS1985, subset=gender=="female")
plot (log(wage) ~ experience,
col = rainbow(2)[c(gender)], # asigna color a los puntos en función del género
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
legend("topright", pch=16, col=rainbow(2), legend=c("Hombres","Mujeres"))
abline(regresion_hombres, col="red")
abline(regresion_mujeres, col="blue")
Un diagrama de dispersión alternativo al anterior.
sexo <- as.numeric(gender)
plot (log(wage) ~ experience,
pch = sexo,
col = sexo,
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
with(CPS1985[gender=="male",], abline(lm(log(wage)~experience), col="red"))
with(CPS1985[gender=="female",], abline(lm(log(wage)~experience), col="black"))
legend("topright", legend=c("Hombres","Mujeres"), pch=1:2, col=1:2, bty="y")
Para concluir, vamos a localizar el centro de gravedad (coordenadas correspondientes a las medias de las variables) en el gráfico de dispersión. Para ello usaremos la función points(). Como hay muchos puntos en el diagrama de dispersión, vamos a quitar los colores para poder ver donde cae el centro de gravedad.
sexo <- as.numeric(gender)
plot (log(wage) ~ experience,
pch = sexo,
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
with(CPS1985[gender=="male",], abline(lm(log(wage)~experience), col="black"))
with(CPS1985[gender=="female",], abline(lm(log(wage)~experience), col="black"))
legend("topright", legend=c("Hombres","Mujeres"), pch=1:2, bty="y")
points(mean(experience), mean(log(wage)), pch=16, col="red")
y utilizando la función abline() podemos dibujar las rectas que pasan representar las líneas (quitamos las rectas de regresión para poder ver con mayor claridad el efecto).
sexo <- as.numeric(gender)
plot (log(wage) ~ experience,
pch = sexo,
main = "Salario (en logaritmos) en función de la experiencia",
xlab = "Experiencia (en años)",
ylab = "Salario (en logaritmos)"
)
legend("topright", legend=c("Hombres","Mujeres"), pch=1:2, bty="y")
points(mean(experience), mean(log(wage)), pch=16, col="red")
abline(v=mean(experience), h=mean(log(wage)))
Para realizar un diagrama de barras primero tenemos que obtener la tabla de las frecuencias y después la representamos utilizando la función barplot().
Comenzamos realizando un diagrama de barras para ver la distribución de la variable Ocupación (Occupation).
frecuencias <- table(occupation) # con variable categórica
frecuencias
## occupation
## worker technical services office sales management
## 156 105 83 97 38 55
barplot(frecuencias,
col = heat.colors(6)
)
Vamos a hacer un diagrama de barras con una variable discreta, por ejemplo: educación (education).
educacion <- table(education)
p <- barplot(educacion,
col = 1:dim(educacion), # para establecer el número de diferentes colores (=diferentes valores)
space = 0, # no espacio entre las barras
horiz = F, # orientación de las barras
density = 50 , # trama de las barras (se puede modificar el ángulo con el argumento angle)
ylim = c(0,250)
)
title("Distribución de la edad", xlab= "Edad (en años)")
text(p[,1],educacion,labels=educacion, pos=3,cex=0.75)
Para ver cómo se distribuye una variable categórica según, generalmente, las categorías de una segunda variable suele utilizarse un diagrama de barras apiladas.
g <- table(ethnicity, gender)
barplot(g,
beside = F, # para apilar las barras
col = rainbow(3),
ylim = c(0,350)
)
# Add text at top of bars
legend("topright", title="Raza", legend=levels(ethnicity), fill=rainbow(3), cex=.75, border=F, horiz=T, text.col="purple")
Observemos la diferencia del anterior gráfico respecto de este otro.
e <- table(gender, ethnicity)
barplot(e,
beside = F,
col = cm.colors(2),
ylim = c(0,500),
border = "blue"
)
p <- table(ethnicity, gender)
barplot(p,
beside=T, # si es FALSE apila
col=rainbow(3),
legend=levels(ethnicity),
names.arg= levels(gender),
ylim=c(0,250)
)
Para dibujar diagramas de barras también se puede utilizar la función plot(). Aquí hay algún ejemplo:
plot(frecuencias, type=“h”)
plot(density(log(wage)))
plot (gender ~ occupation)
Gráficos de sectores
datos <- table(ethnicity)
pie(datos,
labels=c("Caucásicos", "Hispanos", "Otros"),
col=heat.colors(3),
main= "Gráfico de sectores")
Gráfico de mosaico
mosaicplot(ethnicity ~ occupation,
col=2:5,
main="Gráfico de mosaico",
xlab="Raza",
ylab="Ocupación",
cex.axis=.5,
las=1)
#install.packages("vcd")
library(vcd)
datos <- ftable(occupation, ethnicity, gender)
mosaic(~ occupation + ethnicity + gender , dat=datos, las=1)
Si trabajamos con la interfaz de R que proporciona RStudio y queremos guardar o exportar un gráfico normalmente hacemos lo siguiente:
Ejecutamos el script del gráfico que queremos guardar. Este gráfico aparecerá en la pestaña Plots del entorno de trabajo de RStudio.
Guardar gráfico en R
Al seleccionar la opción Export se desplegará una lista con tres opciones:
Opciones de salida para guardar gráfico
Guardar como pdf (Save as pdf…).
Copiar al portapapeles (Copy to clipboard…)
Existen otras opciones para guardar los gráficos. Aquí os dejamos un enlace a un vídeo en youtube titulado Exportar gráficos del blog R para principiantes, y este otro enlace al blog Picando con R tenéis un post donde se explica de forma muy sencilla cómo guardar los gráficos como archivo de imagen.
Una vez nos hemos familiarizado con la creación de gráficos (básicos) utilizando R base, en este apartado vamos a reproducir los gráficos que hemos ido trabajando pero haciendo uso de ggplot2. Creo que no nos equivocamos si decimos que, hoy en día, las representaciones gráficas en R pasan por ggplot2. Esta es la Cheat Sheet de RStudio con la ayuda para la visualización de datos con ggplot2.
Recordad que el manual “ggplot2. Elegant graphics for data analysis” de Wickham lo podéis descargar en este enlace si estáis en la red de la Universitat. También hay una lista de ggplot (http://ggplot2.org/) a la que os podéis subscribir. En palabras de Wickham “The list (…) is very friendly to new users”.
Un gráfico realizado con ggplot2 presenta, al menos, tres elementos:
Por tanto, para construir un gráfico con ggplot2 comenzamos con la siguiente estructura de código:
ggplot(datos, aes()) + geom_tipo()
A partir de esta estructura básica puede mejorarse la presentación de los gráficos introduciendo, por ejemplo, características estéticas en los objetos geométricos, rotulando el gráficos, etc.
Otros elementos que conviene tener presente en un gráfico de ggplot2 son:
Vamos a realizar algunos gráficos con ggplot. Para ello, cargamos la librería ggplot2. Si no está instalado el paquete lo instalamos.
#install.packages("ggplot2")
library(ggplot2)
En los ejemplos que siguen tratamos de ir introduciendo poco a poco distintos elementos y argumentos para mejorar la apariencia de los gráficos.
Vamos a comenzar haciendo un histograma muy sencillo del Salario (wage). Para ello, recordemos que la instrucción comienza con la función ggplot(), en la que incluimos los datos y la estética con la que queremos que se presenten en el gráfico. Seguidamente le añadimos (+) la geometría (tipo histograma) con la función geom_histogram().
Muy importante: con ggplot2 añadimos capas (layers) con el símbolo +.
El histograma del Salario será:
ggplot(CPS1985, aes(x=wage)) +
geom_histogram()
Dos cosas a considerar del histograma anterior:
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(aes(y=..density..))
A partir de esta estructura básica, podemos ir añadiendo elementos para mejorar la presentación. Observad la diferencia entre los siguientes dos histogramas.
# Histograma con 20 intervalos
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, color="white", fill="blue")
# Histograma con amplitud de intervalo igual a 5. Línea discontinua para el contorno del intervalo
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(binwidth=5, color="white", fill="blue", linetype=2)
Ahora vamos a insertar un título al gráfico y también rotularemos los ejes. Para modificar las etiquetas de los ejes se utilizan las funciones xlab() y ylab(). Si, por ejemplo, quisiéramos omitir la etiqueta del eje Y: ylab(NULL).
También podemos cambiar los límites de los ejes, para ello se emplean las funciones xlim() y ylim().
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, color="white", fill="blue") +
ggtitle("Distribución del salario (dólares por hora)") +
xlab("Salario") +
ylab("Número de empleados")
# Una alternativa al anterior sería:
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, color="white", fill="blue") +
labs(title = "Distribución del salario (dólares por hora)",
x = "Salario",
y = "Número de empleados") +
ylim(c(0,150))
Para tratar de observar las diferencias en la distribución del salario según distintos grupo, por ejemplo, el género podemos:
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, color="white", fill="blue") +
facet_grid(gender~.)
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, color="white", fill="blue") +
facet_wrap(~gender)
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, aes(color=gender) ) +
ylim(c(0,150))
ggplot(CPS1985, aes(x=wage, fill=gender)) +
geom_histogram(bins=20) +
ylim(c(0,150))
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(bins=20, aes(fill=gender), position="fill", alpha=0.4) +
labs(x= "Salario", y="Empleados", fill="Género") + # títulos de ejes y leyenda
scale_fill_discrete(labels=c("Hombre","Mujer")) # títulos claves leyenda
ggplot(CPS1985, aes(x=wage)) +
geom_freqpoly(bins=20, aes(color=gender)) +
labs(color="Género")
y casi mejor con la densidad de frecuencias:
ggplot(CPS1985, aes(x=wage)) +
geom_freqpoly(bins=20, aes(y=..density.., color=gender)) +
labs(color="Género")
A continuación, vamos a añadir dos líneas para situar la media y la mediana.
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(aes(fill=..count..), bins=20, color="white") +
geom_vline(aes(xintercept=mean(wage)), color="red") +
geom_vline(aes(xintercept=median(wage)), color= "darkgreen") +
labs(title = "Distribución del salario (dólares por hora)",
x = "Salario",
y = "Número de empleados") +
scale_fill_continuous(name="Empleados") +
theme_classic()
Añadimos la curva normal al histograma
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(aes(y=..density..), bins=20, color="white", fill="grey") +
stat_function(fun = dnorm, colour = "red",
arg = list(mean = mean(wage, na.rm = TRUE),
sd = sd(wage, na.rm = TRUE))) +
geom_density(color="blue")
Con el argumento expand_limits podemos evitar que la curva de densidad aparezca unida. Veámoslo con el siguiente ejemplo:
ggplot(CPS1985, aes(x=wage)) +
geom_histogram(aes(y=..density..), bins=20, color="white", fill="grey") +
stat_function(fun = dnorm, colour = "red",
arg = list(mean = mean(wage, na.rm = TRUE),
sd = sd(wage, na.rm = TRUE))) +
geom_line(stat="density", color="blue") +
expand_limits(y=0)
Podéis consultar las siguientes dos páginas web para aprender más sobre cómo crear histogramas con ggplot2:
Ahora, ha llegado el momento de que practiquéis lo visto en este apartado del tutorial. Vamos con la siguiente tarea:
Para realizar un diagrama de caja utilizamos la geometría: geom_boxplot().
ggplot(CPS1985, aes(x=gender, y=wage)) +
geom_boxplot()
Podemos colorear las cajas del gráfico incluyendo en las características estéticas la variable que utilizaremos para el relleno. Automáticamente se crea una leyenda para facilitar la lectura del gráfico, que podemos cambiar de posición o eliminar si se considera que no aporta información relevante.
# Diagrama de caja con color de relleno
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender)) +
geom_boxplot() +
labs(x="Género", y="Salario", fill="Género") + # titulo ejes y leyenda
scale_x_discrete(labels=c("Hombre","Mujer")) + # etiquetas del eje x
scale_fill_discrete(labels=c("Hombre","Mujer")) # etiquetas claves leyenda
# Diagrama de caja con cambio de posición de la leyenda
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender)) +
geom_boxplot() +
labs(x="Género", y="Salario", fill="Género") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
scale_fill_discrete(labels=c("Hombre","Mujer")) +
theme(legend.position="top") # cambio posición de leyenda
# Diagrama de caja sin leyenda por considerar redundante la información
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender)) +
geom_boxplot(outlier.colour = "blue") + # color de los outliers
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) # eliminamos la leyenda
Para cambiar la orientación de las cajas y que en lugar de representarse verticalmente se representen horizontalmente se utiliza la función coord_flip(). Esta forma de presentar los diagramas de caja es muy frecuente porque facilita la interpretación de los mismos al situar la variable cuantitativa en el eje de abscisas.
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender)) +
geom_boxplot(outlier.colour = "blue") +
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) +
coord_flip() # cambio dirección de las cajas
Puede resultar interesante localizar la media de cada grupo en los diagramas de caja. Para ello, hacemos uso del elemento stat (transformación estadística). Se puede añadir stat, básicamente, de dos formas:
Veamos las dos formas comentadas de añadir .
# Añadimos la media utilizando la función stat_()
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender)) +
geom_boxplot() +
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) +
coord_flip() +
stat_summary(fun.y=mean, geom="point", shape=5, size=4)
# Añadimos la media utilizando la función geom_() y modificando el elemento stat
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender) ) +
geom_boxplot(alpha=0.3, outlier.colour = "blue") +
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) +
coord_flip() +
geom_point(stat= "summary", fun.y=mean, shape=16, size=2, color="red")
Por último, si queremos hacernos una idea de cómo se distribuyen nuestras observaciones/individuos respecto del diagrama de caja podemos hacer uso de la función position_jitter(). Cuando hay muchos datos las observaciones se superponen. La función position_jitter() lo que hace es añadir un pequeño “ruido” en cada posición. También podemos utilizar la geometría geom_jitter(). A continuación se muestran los diagramas de caja con estas dos opciones.
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender) ) +
geom_boxplot(alpha=0.3, outlier.colour = "blue") +
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) +
coord_flip() +
geom_point(stat= "summary", fun.y=mean, shape=16, size=4, color="red") +
geom_point(position = position_jitter(width = 0.1), alpha = 0.2)
ggplot(CPS1985, aes(x=gender, y=wage, fill=gender) ) +
geom_boxplot(alpha=0.3, outlier.colour = "blue") +
labs(x="Género", y="Salario") +
scale_x_discrete(labels=c("Hombre","Mujer")) +
guides(fill=FALSE) +
coord_flip() +
geom_point(stat= "summary", fun.y=mean, shape=16, size=4, color="red") +
geom_jitter(width = 0.1, alpha = 0.2)
Para practicar lo aprendido proponemos la siguiente tarea:
Ya hemos visto a lo largo de este tutorial que el salario presenta una gran dispersión y su distribución es asimétrica. Sin embargo, si transformamos el salario y trabajamos con el logaritmo del salario, tanto la dispersión como la asimetría se reducen. Cuando trabajamos con variables monetarias esta situación suele ser frecuente y, por tanto, normalmente se trabaja con logaritmos.
¿Cómo es la relación entre el salario (en logaritmos) y la experiencia?
Hacemos el diagrama de dispersión para ver la posible relación entre ambas variables.
ggplot(CPS1985, aes(experience, log(wage))) +
geom_point() +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)")
Si queremos que los puntos del diagrama de dispersión tengan color tenemos varias alternativas. Observar las diferencias entre los siguientes tres gráficos.
# Opción 1
ggplot(CPS1985, aes(experience, log(wage), color="red")) +
geom_point()
# Opción 2
ggplot(CPS1985, aes(experience, log(wage))) +
geom_point(aes(color="red"))
# Opción 3
ggplot(CPS1985, aes(experience, log(wage))) +
geom_point(color="red")
Las dos primeras opciones -en las que color se ha introducido como una característica estética (con aes())- tienen un efecto similar (no igual, más tarde lo veremos): se crea una variable color que toma el valor red y automáticamente se muestra una leyenda. En la tercera opción el color se introduce como una constante.
Si tenemos muchos datos es muy probable que se produzca un importante solapamiento de las observaciones. Si “muchos datos” no son “muchísimos”, podemos utilizar jitter para introducir una pequeña perturbación en las observaciones y mejorar la visualización de los datos con el diagrama de dispersión.
ggplot(CPS1985, aes(experience,log(wage))) +
geom_jitter(width=.2, alpha=0.5) +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)")
Una alternativa al gráfico anterior es representar lo que se conoce como Hex plot. Este gráfico es equivalente a un histograma en el sentido que se representan puntos hexagonales con distinto color en función del número de observaciones que caen dentro de cada hexágono.
# install.packages("hexbin") # Hay que instalar el paquete hexbin
library(hexbin)
ggplot(CPS1985, aes(experience,log(wage))) +
geom_hex() +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_fill_continuous("Empleados") +
theme_classic()
Cuando se utiliza la geometría geom_hex los puntos hexagonales se colorean, por defecto, según una escala gradual de tonos azules (azul oscuro equivale a poca frecuencia, azul claro equivale a alta frecuencia). Esto no es muy intuitivo para nosotros: ¿claro mucha frecuencia?, ¿oscuro poca frecuencia?. Al revés estamos más acostumbrados. Podemos cambiar la escala de colores utilizando la función scale_fill_gradient.
ggplot(CPS1985, aes(experience,log(wage))) +
geom_hex() +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_fill_gradient("Empleados", low="orange", high="orange4") + # nombres en colors()
theme_classic()
Ahora, en el diagrama de dispersión vamos a colorear los puntos según el género. Observar las diferencias entres los siguientes tres diagramas de dispersión. ¿Alguna diferencia entre los siguientes dos gráficos?
# Opción 1
ggplot(CPS1985, aes(experience,log(wage), color=gender)) +
geom_point() +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_color_discrete(name="Género", labels=c("Hombre","Mujer"))
# Opción 2
ggplot(CPS1985, aes(experience,log(wage))) +
geom_point(aes(color=gender)) +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_color_discrete("Género", labels=c("Hombre","Mujer"))
Como comentamos anteriormente, aparentemente no hay diferencias entre los dos gráficos, pero fijémonos qué sucede si añadimos la recta de regresión. Esto último lo vamos a hacer añadiendo la geometría geom_smooth() y especificando como método de estimación lm (linear model).
# Opción 1
ggplot(CPS1985, aes(experience,log(wage), color=gender)) +
geom_point() +
geom_smooth(method="lm", se=FALSE) +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_color_discrete("Género", labels=c("Hombre","Mujer"))
# Opción 2
ggplot(CPS1985, aes(experience,log(wage))) +
geom_point(aes(color=gender)) +
geom_smooth(method="lm") +
labs(title="Diagrama de dispersión",
subtitle= "ScatterPlot",
caption="Fuente: CPS1985 (paquete AER)",
x="Experiencia (en años)",
y="Salario (en logaritmo)") +
scale_color_discrete("Género", labels=c("Hombre","Mujer"))
Para más detalles prácticos sobre los diagramas de dispersión podéis consultar, por ejemplo, ggplot2 scatter plots : Quick start guide - R software and data visualization o Scatterplots (ggplot2) en Cookbook for R.
Es el momento de realizar una nueva tarea…
El diagrama de barras, como ya sabemos, puede utilizarse para representar variables categóricas (atributos u ordinales) y variables cuantitativas discretas.
ggplot(CPS1985, aes(ethnicity)) +
geom_bar()
ggplot(CPS1985, aes(education)) +
geom_bar()
Como siempre, lo primero sería dar título al gráfico y a los ejes.
ggplot(CPS1985, aes(ethnicity)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros"))
ggplot(CPS1985, aes(education)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Educación (en años)",
y="Empleados")
Coloreamos cada barra con un color distinto:
ggplot(CPS1985, aes(ethnicity, fill=ethnicity)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros"))
# eliminamos la leyendo porque en este caso no es ilustrativa
ggplot(CPS1985, aes(ethnicity, fill=ethnicity)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
guides(fill=FALSE)
Incorporamos información sobre el número de casos en las barras correspondientes a cada categoría y eliminamos el eje Y.
ggplot(CPS1985, aes(ethnicity, fill=ethnicity)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y=NULL) +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
guides(fill=FALSE) +
geom_text(stat='count',aes(label=..count..), vjust=-0.5, size=3)
Ahora estamos interesados en ver cómo se distribuye el género según la raza.
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_discrete("Género", labels=c("Hombre","Mujer"))
Si queremos otros colores los podemos introducir manualmente o a través de una paleta:
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_manual(values=c("#FF0033", "#3300FF"),
"Género",
labels=c("Hombre","Mujer"))
# Ejemplo 1. Paleta de colores
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_manual(values = heat.colors(2),
"Género",
labels=c("Hombre","Mujer"))
# Ejemplo 2. Paleta de colores
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer"))
Como hicimos antes, incluimos el número de casos como parte de la información que proporciona el diagrama de barras.
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer")) +
geom_text(stat='count',aes(label=..count..),
position = "stack",
vjust=1,
size=2,
color="red")
El mismo diagrama de barras apilado pero ahora en porcentajes:
ggplot(CPS1985, aes(ethnicity,y = (..count..)/sum(..count..), fill=gender)) +
geom_bar() +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer")) +
geom_text(stat="count",
aes(label = paste(round((..count..)/sum(..count..)*100), "%")),
position = "stack",
vjust=1,
size=2,
color="red")
Y para ver la proporción de hombres y mujeres en cada categoría hacemos uso del parámetro position = fill dentro de la geometría.
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar(position = "fill") +
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer"))
Como hemos podido comprobar hasta ahora, cuando hacemos un diagrama de barras en el que consideramos más de una variable categórica, las barras aparecen apiladas (la opción por defecto es position = “stack”). Podemos hacer que las barras se sitúen unas junto a las otras con position = “dodge”.
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar(position="dodge") + # también: position=position_dodge()
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer"))
y, finalmente, podemos cambiar la orientación de las barras de vertical a horizontal.
ggplot(CPS1985, aes(ethnicity, fill=gender)) +
geom_bar(position="dodge") + # también: position=position_dodge()
labs(title="Diagrama de barras",
x= "Raza",
y="Empleados") +
scale_x_discrete(labels=c("Caucásico", "Hispano", "Otros")) +
scale_fill_brewer(palette = "Accent",
"Género",
labels=c("Hombre","Mujer")) +
coord_flip()
En R para muy principiantes encontraréis una serie de cuatro video-blogs sobre cómo elaborar un diagrama de barras en ggplot2.
Para que nos hagamos una idea de las posibilidades que ofrece R, tanto en el tratamiento como en la visualización de datos, aquí os dejamos este ejemplo que aparece en la Cheat Sheet del paquete eurostat, desarrollado por Leo Lahti, Janne Huovari, Markus Kainu y Przemyslaw Biecek.
¿Qué os parece?
library(dplyr)
library(eurostat)
# Cargamos los datos de eurostat
fertility <- get_eurostat("demo_r_frate3") %>%
filter(time == "2014-01-01") %>%
mutate(cat = cut_to_classes(values, n=7, decimals=1))
mapdata <- merge_eurostat_geodata(fertility,
resolution = "20")
# Representamos los datos
ggplot(mapdata, aes(x = long, y = lat, group = group)) +
geom_polygon(aes(fill=cat), color="grey", size = .1) +
scale_fill_brewer(palette = "RdYlBu") +
labs(title="Fertility rate, by NUTS-3 regions, 2014",
subtitle="Avg. number of live births per woman",
fill="Total fertility rate(%)") +
theme_light() +
coord_map(xlim=c(-12,44), ylim=c(35,67))
Libros: