El curso base en el que este notebook está basado es R graphics with ggplot2 workshop notes. Aquí está la traducción y algunas modificaciones para su mejor adaptación a un notebook.
Los materiales y datasets están aquí. También debes instalar el paquete tidyverse y cargarlo. Este paquete incluye la librería ggplot2. También hay algún ejercicio incluido, hacerlos sin ver el código, y usar el código incluido para chequear el tuyo.
Por qué usar ggplot2:
Ventajas
Desventajas o Cosas que no puedes (o no deberías) hacer con ggplot2:
La idea básica es construir el gráfico con bloqeus independientes, que se construyen de forma aislada. Los bloques que constituyen un gráfico son los siguientes:
Cargamos los datos de ejemplo, que son los precios de las casas en los diferentes estados de USA.
housing <- read_csv("dataSets/landdata-states.csv")
## Parsed with column specification:
## cols(
## State = col_character(),
## region = col_character(),
## Date = col_double(),
## Home.Value = col_double(),
## Structure.Cost = col_double(),
## Land.Value = col_double(),
## Land.Share..Pct. = col_double(),
## Home.Price.Index = col_double(),
## Land.Price.Index = col_double(),
## Year = col_double(),
## Qrtr = col_double()
## )
head(housing)
| State | region | Date | Home.Value | Structure.Cost | Land.Value | Land.Share..Pct. | Home.Price.Index | Land.Price.Index | Year | Qrtr |
|---|---|---|---|---|---|---|---|---|---|---|
| AK | West | 2010.25 | 224952 | 160599 | 64352 | 28.6 | 1.481 | 1.552 | 2010 | 1 |
| AK | West | 2010.50 | 225511 | 160252 | 65259 | 28.9 | 1.484 | 1.576 | 2010 | 2 |
| AK | West | 2009.75 | 225820 | 163791 | 62029 | 27.5 | 1.486 | 1.494 | 2009 | 3 |
| AK | West | 2010.00 | 224994 | 161787 | 63207 | 28.1 | 1.481 | 1.524 | 2009 | 4 |
| AK | West | 2008.00 | 234590 | 155400 | 79190 | 33.8 | 1.544 | 1.885 | 2007 | 4 |
| AK | West | 2008.25 | 233714 | 157458 | 76256 | 32.6 | 1.538 | 1.817 | 2008 | 1 |
Comparando con los gráficos base de R, ggplot2:
Histograma con los gráficos base:
hist(housing$Home.Value)
Histograma con ggplot2:
ggplot(housing, aes(x = Home.Value)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Ahora vamos a ver un gráfico con colores y varias series de datos.
Con los gráficos base:
plot(Home.Value ~ Date,
col = factor(State),
data = filter(housing, State %in% c("MA", "TX")))
legend("topleft",
legend = c("MA", "TX"),
col = c("black", "red"),
pch = 1)
Con ggplot2:
ggplot(filter(housing, State %in% c("MA", "TX")),
aes(x=Date,
y=Home.Value,
color=State))+
geom_point()
Con ggplot2, en una sola instrucción, hacemos el filtrado, especificamos los ejes y la series de datos y el tipo de gráfico.
En ggplot2, aestético significa “algo que puedes ver”. Algunos ejemplos son:
Cada tipo de objeto geométrico (geom) solo acepta un subconjunto de todos los aestéticos. Puedes consultar la pagina de ayuda de geom() para ver que aestéticos acepta. El mapeo aestético se hace con la función aes().
Los objetos geométricos son las formas que puede tomar un gráfico. Algunos ejemplos son:
Un gráfico debe tener al menos un geom, pero no hay limite. Puedes añadir más geom usando el signo +.
Con el siguiente código podrás ver una lista de los objetos geométricos:
help.search("geom_", package = "ggplot2")
## starting httpd help server ... done
Ahora que ya sabemos que es el mapeo aestético y los objetos geométricos, podemos hacer un grafico de dispersión (scatter plot). geom_point() requiere mapeo para la x y la y, el resto es opcional.
hp2001Q1 <- filter(housing, Date == 2001.25)
ggplot(hp2001Q1,
aes(y = Structure.Cost, x = Land.Value)) +
geom_point()
ggplot(hp2001Q1,
aes(y = Structure.Cost, x = log(Land.Value))) +
geom_point()
Podemos combinar objetos geom() para añadir al gráfico una recta de regresión, y añadir un color a los puntos en base a una variable.
hp2001Q1$pred.SC <- predict(lm(Structure.Cost ~ log(Land.Value), data = hp2001Q1)) #añadimos una variable con los valores de la recta de regresión del coste de estructura en base al valor del terreno
p1 <- ggplot(hp2001Q1, aes(x = log(Land.Value), y = Structure.Cost)) #gráfico base
p1 + geom_point(aes(color = Home.Value)) + #añade el color a los puntos en base al valor
geom_line(aes(y = pred.SC)) #añade la recta de regresión
El geom_smooth() añade una aproximación de los datos x e y, e incluye una cinta con el margen de confianza.
p1 +
geom_point(aes(color = Home.Value)) +
geom_smooth() #sustituimos la recta de regresión geom_line() anterior por la aproximación
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Podemos añadir una etiquetas a los puntos del gráfico de dispersión con el objeto geom_text().
p1 +
geom_point() +
geom_text(aes(label=State), size = 3)
Con el objeto geom_text_repel() de la libreria ggrepel, las etiquetas no se colocan encima de los puntos, sino a un lado.
p1 +
geom_point() +
geom_text_repel(aes(label=State), size = 3)
Dentro de aes() debemos poner las variables aestéticas, mientras que valores fijos para todas las variables las podemos poner fuera.
p1 +
geom_point(aes(size = 2),# incorrecto! 2 no es una variable
color="red") # correcto, todos los puntos en rojo
p1 +
geom_point(size = 2,# correcto, todos los puntos de tamaño 2
color="red") # correcto, todos los puntos en rojo
Se pueden mapear otros aestéticos, como x e y, para visualizar algunas cosas concretas.
p1 +
geom_point(aes(color=Home.Value, shape = region))
Lee los datos del fichero dataSets/EconomistData.csv. En estos datos vemos los valores del Human Development Index y Corruption Perception Index para varios paises.
Hay que crear un gráfico con los siguientes parámetros:
dat <- read_csv("dataSets/EconomistData.csv")
## Parsed with column specification:
## cols(
## Country = col_character(),
## HDI.Rank = col_double(),
## HDI = col_double(),
## CPI = col_double(),
## Region = col_character()
## )
head(dat)
| Country | HDI.Rank | HDI | CPI | Region |
|---|---|---|---|---|
| Afghanistan | 172 | 0.398 | 1.5 | Asia Pacific |
| Albania | 70 | 0.739 | 3.1 | East EU Cemt Asia |
| Algeria | 96 | 0.698 | 2.9 | MENA |
| Angola | 148 | 0.486 | 2.0 | SSA |
| Argentina | 45 | 0.797 | 3.0 | Americas |
| Armenia | 86 | 0.716 | 2.6 | East EU Cemt Asia |
Punto 1:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point()
Punto 2:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(color="blue")
Punto 3:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region))
Punto 4:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region),size = 2)
Punto 5:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region, size = HDI.Rank))
Algunos gráficos, como los gráficos de dispersión, no requieren transformación: cada punto es colocado en las coordenadas x e y igual a su valor original. Otros gráficos, como los boxplot, histogramas, rectas de predicción… necesitan transformaciones estadísticas:
Cada objeto geom tiene un estadistico por defecto, pero se pueden cambiar. Por ejemplo, el estadistico por defecto para geom_bar es stat_bin:
args(geom_histogram)
## function (mapping = NULL, data = NULL, stat = "bin", position = "stack",
## ..., binwidth = NULL, bins = NULL, na.rm = FALSE, show.legend = NA,
## inherit.aes = TRUE)
## NULL
args(stat_bin)
## function (mapping = NULL, data = NULL, geom = "bar", position = "stack",
## ..., binwidth = NULL, bins = NULL, center = NULL, boundary = NULL,
## breaks = NULL, closed = c("right", "left"), pad = FALSE,
## na.rm = FALSE, show.legend = NA, inherit.aes = TRUE)
## NULL
Los argumentos a las funciones stat_ se deben pasar a través de la funcion geom_. Para cambiar el valor por defecto, primero debemos saber que stat usa el geom, para después cambiarlo.
Por ejemplo, para el histograma de Home.Value:
p2 <- ggplot(housing, aes(x = Home.Value))
p2 + geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
En el mensaje de aviso especifica que se puede cambiar el valor del stat_bin() con la opción binwidth.
p2 + geom_histogram(stat = "bin", binwidth=4000) #hacemos 4000 particiones de los datos, en lugar de los 30 por defecto
Algunas veces la transformación estadística por defecto no es la que necesitas. Esto pasa a menudo con datos con algún tratamiento previo, como las agrupaciones.
housing.sum <- aggregate(housing["Home.Value"], housing["State"], FUN=mean)
rbind(head(housing.sum), tail(housing.sum))
| State | Home.Value | |
|---|---|---|
| 1 | AK | 147385.14 |
| 2 | AL | 92545.22 |
| 3 | AR | 82076.84 |
| 4 | AZ | 140755.59 |
| 5 | CA | 282808.08 |
| 6 | CO | 158175.99 |
| 46 | VA | 155391.44 |
| 47 | VT | 132394.60 |
| 48 | WA | 178522.58 |
| 49 | WI | 108359.45 |
| 50 | WV | 77161.71 |
| 51 | WY | 122897.25 |
Y ahora intentamos hacer un gráfico de barras:
#ggplot(housing.sum, aes(x=State, y=Home.Value)) +
# geom_bar()
¿Cual es el problema? Basicamente, que hemos agregado los datos, y después pedimos a ggplot que los vuelva a agregar (geom_bar() por defecto utiliza stat = stat_count) y obviamente esto no funciona. Podemos solucionarlo diciendole a geom_bar que use una transformación estadística diferente:
ggplot(housing.sum, aes(x = State, y = Home.Value)) +
geom_bar(stat = "identity")
BONUS: Superpone una linea de predicción usando el metodo loess por defecto, pero que sea menos suavizada. Pista: consulta ?loess
Punto 1:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point()
Punto 2:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth()
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Punto 3:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth(method = "lm")
Punto 4:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point() +
geom_line(stat = "smooth",method = "loess")
BONUS
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point() +
geom_smooth(span = 0.1) #valores span mas bajos dan una curva menos suavizada
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Con aes() especificamos que variables incluimos en el gráfico, pero no especificamos como se muestran. Cuando especificamos color = x, no escogemos el color, o con shape = x no elegimos la forma. Para esto se utilizan las escalas.
En ggplot2, las escalas incluyen:
Los siguientes argumentos son comunes a la mayoría de las escalas de ggplot2:
name: el primer argumento da el eje o el titulo de la leyenda limits: el minimo y el maximo de la escala breaks: los punto a lo largo de la escala donde las etiquetas deben aparecer labels: las etiquetas que apareceran en cada break
Escalas específicas pueden tener argumentos adicionales: por ejemplo, la función scale_color_continouos tiene los argumentos low y high para establecer los colores para la parte baja y alta de la escala respectivamente.
Empezamos construyendo un gráfico de puntos básico:
p3 <- ggplot(housing,
aes(x = State,
y = Home.Price.Index)) +
theme(legend.position="top",
axis.text=element_text(size = 6))
p4 <- p3 + geom_point(aes(color = Date),
alpha = 0.5,
size = 1.5,
position = position_jitter(width = 0.25, height = 0))
p4
Ahora modificamos los breaks de la escala de colores en la variable Date:
p4 + scale_x_discrete(name="State Abbreviation") +
scale_color_continuous(name="",
breaks = c(1976, 1994, 2013),
labels = c("'76", "'94", "'13")) #cambiamos los breaks para la escala de colores de la variable Date
Ahora cambiamos la escala de colores para que los valores bajos sean azules y los valores altos sean rojos:
p4 +
scale_x_discrete(name="State Abbreviation") +
scale_color_continuous(name="",
breaks = c(1976, 1994, 2013),
labels = c("'76", "'94", "'13"),
low = "blue", high = "red")
En la librería scales hay funciones que pueden modificar más las escalas que los argumentos básicos:
p4 +
scale_color_continuous(name="",
breaks = c(1976, 1994, 2013),
labels = c("76", "94", "13"),
low = muted("blue"), high = muted("red")) #la funcion muted está en la librería scales, y apaga los colores.
ggplot2 tiene una amplia variedad de escalas de color. Usando la funcion scale_color_gradient2 permite interpolar entre 3 colores diferentes.
p4 +
scale_color_gradient2(name="",
breaks = c(1976, 1994, 2013),
labels = c("'76", "'94", "'13"),
low = muted("blue"),
high = muted("red"),
mid = "gray60", #color intermedio
midpoint = 1994) #establece el punto medio
Matriz parcial de escalas disponibles:
| Escala | Tipo | Ejemplo |
|---|---|---|
| scale_color_ | identity | scale_color_discrete |
| scale_fill_ | manual | scale_fill_continuous |
| scale_size_ | continuous | scale_size_manual |
| - | discrete | scale_size_discrete |
| scale_shape_ | discrete | scale_shape_discrete |
| scale_linetype_ | identity | scale_linetype_identity |
| - | manual | scale_linetype_manual |
| scale_x_ | continuous | scale_x_continuous |
| scale_y_ | discrete | scale_y_discrete |
| - | reverse | scale_y_reverse |
| - | log | scale_y_log |
| - | date | scale_y_date |
| - | datetime | scale_y_datetime |
En RStudio puedes escribir scale_ y con la tecla TAB puedes obtener la lista completa de escalas disponible.
Punto 1:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region))
Punto 2:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region),size=2) +
scale_x_continuous(name = "Corruption Perception Index*") +
scale_y_continuous(name = "Human Development Index") +
scale_color_discrete(name = "World Region", h = c(0, 270))
Punto 3:
ggplot(dat,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region),size=2) +
scale_x_continuous(name = "Corruption Perception Index*") +
scale_y_continuous(name = "Human Development Index") +
scale_color_manual(name = "World Region", values = c("Americas" = "red","Asia Pacific" = "blue","East EU Cemt Asia" = "yellow","EU W. Europe" = "green", "MENA" = "orange","SSA" = "purple"))
Con esta funcionalidad podemos crear graficos diferentes para subsets de datos. Esto facilita la comparación de los datos, no solo de diferentes geom en un solo gráfico.
ggplot2 ofrece dos funciones para crear gráficos multiples:
Grafico básico con la evolución de precios por estado:
p5 <- ggplot(housing, aes(x = Date, y = Home.Value))
p5 + geom_line(aes(color = State))
Con el gráfico anterior hay dos problemas: demasiados estados para distinguir el color de uno del otro; y que hay demasiadas lineas y se ocultan unas a otras.
Con la modificación del aspecto (faceting):
(p5 <- p5 + geom_line() +
facet_wrap(~State, ncol = 10))
También está facet_grid() para dos dimensiones. Tiene cierta dificultad de visualización porque las variables categoricas tienen muchos niveles. Con un filtrado previo se facilitaría esta visualización.
(p6 <- p5 + geom_line() +
facet_grid(cols = vars(State), rows = vars(region)))
El sistema ggplot2 theme maneja los elementos del gráfico que no están ligados a los datos como:
Los temas incluidos son:
Ejemplos:
p5 + theme_linedraw()
p5 + theme_light()
p5 + theme_minimal()
Se pueden modificar los temas por defecto usando la función theme():
p5 + theme_minimal() +
theme(text = element_text(color = "turquoise"))
Todas las opciones están documentadas en ?theme.
Puedes crear nuevos temas, como se puede ver en el siguiente ejemplo:
theme_new <- theme_bw() +
theme(plot.background = element_rect(size = 1, color = "blue", fill = "black"),
text=element_text(size = 12, family = "Serif", color = "ivory"),
axis.text.y = element_text(colour = "purple"),
axis.text.x = element_text(colour = "red"),
panel.background = element_rect(fill = "pink"),
strip.background = element_rect(fill = muted("orange")))
p5 + theme_new
La pregunta que se hace más frecuentemente es algo asi: tengo dos variables en mi dataset y quiero hacer un grafico con ellas en puntos separados, con diferente color dependiendo de la variable. ¿Como lo puedo hacer?
ERRONEO
housing.byyear <- aggregate(cbind(Home.Value, Land.Value) ~ Date, data = housing, mean)
ggplot(housing.byyear,
aes(x=Date)) +
geom_line(aes(y=Home.Value), color="red") +
geom_line(aes(y=Land.Value), color="blue")
CORRECTA
home.land.byyear <- gather(housing.byyear,
value = "value",
key = "type",
Home.Value, Land.Value)
ggplot(home.land.byyear,
aes(x=Date,
y=value,
color=type)) +
geom_line()
Como ultimo ejercicio, hay que reproducir el gráfico de The Economist que hay al inicio de este enlace. Con este codigo lo conseguimos:
df <- read_csv("dataSets/EconomistData.csv")
## Parsed with column specification:
## cols(
## Country = col_character(),
## HDI.Rank = col_double(),
## HDI = col_double(),
## CPI = col_double(),
## Region = col_character()
## )
head(df)
| Country | HDI.Rank | HDI | CPI | Region |
|---|---|---|---|---|
| Afghanistan | 172 | 0.398 | 1.5 | Asia Pacific |
| Albania | 70 | 0.739 | 3.1 | East EU Cemt Asia |
| Algeria | 96 | 0.698 | 2.9 | MENA |
| Angola | 148 | 0.486 | 2.0 | SSA |
| Argentina | 45 | 0.797 | 3.0 | Americas |
| Armenia | 86 | 0.716 | 2.6 | East EU Cemt Asia |
#renombramos las regiones según aparecen en el gráfico original
df$Region <- factor(dat$Region,
levels = c("EU W. Europe",
"Americas",
"Asia Pacific",
"East EU Cemt Asia",
"MENA",
"SSA"),
labels = c("OECD",
"Americas",
"Asia &\nOceania",
"Central &\nEastern Europe",
"Middle East &\nnorth Africa",
"Sub-Saharan\nAfrica"))
#Para evitar saturación, solo elegimos mostrar el nombre de algunos paises
pointsToLabel <- c("Russia", "Venezuela", "Iraq", "Myanmar", "Sudan",
"Afghanistan", "Congo", "Greece", "Argentina", "Brazil",
"India", "Italy", "China", "South Africa", "Spain",
"Botswana", "Cape Verde", "Bhutan", "Rwanda", "France",
"United States", "Germany", "Britain", "Barbados", "Norway", "Japan",
"New Zealand", "Singapore")
ggplot(df,aes(x = CPI, y = HDI)) +
geom_point(aes(color = Region),size=2.5, shape = 1, stroke = 1.25) +
geom_line(stat = "smooth",method = "loess", span = 1, colour = "red") +
scale_x_continuous(name = "Corruption Perception Index, 2011 (10=least corrupt)", limits = c(1,10), breaks = c(1:10)) +
theme(legend.position="top",
legend.text.align = 0,
legend.justification = c("left", "top")) +
labs(title = "Corruption and human development",
caption = "Sources: Transparency International; UN Human Development Report") +
geom_text_repel(aes(label=Country), size = 3, data = filter(df, Country %in% pointsToLabel)) +
scale_y_continuous(name = "Human Development Index, 2011 (1=best)", limits = c(0.2,1.0), breaks = seq(0.2,1.0, by = 0.1)) +
scale_color_manual(name = " ",
values = c("Americas" = "blue",
"Asia &\nOceania" = "cyan2",
"Central &\nEastern Europe" = "green",
"OECD" = "blue4",
"Middle East &\nnorth Africa" = "orange",
"Sub-Saharan\nAfrica" = "brown"),
guide = guide_legend(nrow = 1, order = 1))