Es un paquete de gráficos muy versátil, flexible y consistente.
A grammar of graphics is a tool that enables us to concisely describe the components of a graphic. Such a grammar allows us to move beyond named graphics (e.g., the scatterplot) and gain insight into the deep structure that underlies statistical graphics. (Wickham, 2010)
Prácticamente todo tipo de gráficos pueden ser generados por ggplot, con excepción de gráficos 3D, gráficos de redes y gráficos interactivos. Pero se puede conectar con otros paquetes para lograrlo.
A diferencia de los gráficos básicos de R, en ggplot el gráfico en sí es tratado como un objeto, y por lo tanto se puede asignar y se puede modificar.
La idea con ggplot es que construyes el gráfico en capas, añadiendo una por una. La geometría sólo constituye una de estas capas, y por lo tanto los comandos son generalizables para cualquier gráfico que estés realizando.
La función ggplot()
debe recibir como argumento principal una tabla (data frame), donde cada columna corresponda a una variable.
El comando de ggplot toma como primer argumento el data frame (data = ___
)
Y como segundo argumento mapea las variables, a las cuales llama “estéticos” (aesthetics en inglés, abreviado aes
).
aes = (...)
, que contiene una asignación, y aes(...)
, que no contiene asignación. La segunda forma es la sintáxis correcta para ggplot.La siguiente capa del gráfico corresponde a la geometría.
Hay varios tipos de geometrías: de puntos (geom_point
), caja y bigotes (geom_boxplot
), de líneas (geom_line
), de barras (geom_col
o geom_bar
), etc. La geometría debe ser adecuada para el tipo de datos que estés utilizando, dependiendo si son continuos o discretos.
Una nueva capa se agrega con el signo de más (+), como si se le fueran sumando. Es posible agregar varias geometrías en un mismo gráfico.
Las geometrías disponibles pueden ser consultadas en la documentación de ggplot2:
https://ggplot2.tidyverse.org/reference/#section-layer-geoms
Incluso es posible consultar en la lista de funciones del paquete aquellas que contengan la palabra “geom”:
ls( pattern = 'geom_', envir = as.environment('package:ggplot2'))
[1] "geom_abline" "geom_area" "geom_bar"
[4] "geom_bin2d" "geom_blank" "geom_boxplot"
[7] "geom_col" "geom_contour" "geom_count"
[10] "geom_crossbar" "geom_curve" "geom_density"
[13] "geom_density_2d" "geom_density2d" "geom_dotplot"
[16] "geom_errorbar" "geom_errorbarh" "geom_freqpoly"
[19] "geom_hex" "geom_histogram" "geom_hline"
[22] "geom_jitter" "geom_label" "geom_line"
[25] "geom_linerange" "geom_map" "geom_path"
[28] "geom_point" "geom_pointrange" "geom_polygon"
[31] "geom_qq" "geom_qq_line" "geom_quantile"
[34] "geom_raster" "geom_rect" "geom_ribbon"
[37] "geom_rug" "geom_segment" "geom_sf"
[40] "geom_sf_label" "geom_sf_text" "geom_smooth"
[43] "geom_spoke" "geom_step" "geom_text"
[46] "geom_tile" "geom_violin" "geom_vline"
[49] "update_geom_defaults"
Hay una enorme diversidad de argumentos visuales.
color
,fill
y alpha
.size
y shape
. Las shapes tienen claves numéricas que van del 0 al 25.linetype
, al que se le pueden pasar opciones como "solid", "dotted", "dashed", "dotdash"
entre otras.Para recordar las claves numéricas de las formas (shapes) disponibles de puntos, aquí hay una gráfica también generada en ggplot:
En ggplot, estos argumentos pueden ser aplicados en general a todo el gráfico, o bien pueden ser recursos visuales que representen la información de otras variables (por lo general se utilizan para distinguir grupos de observaciones).
Si se utilizan de esta última forma, estos argumentos deben ser mapeados como estéticos adicionales, y comúnmente se agregan dentro del paréntesis de la geometría.
Ejemplo: mi_plot + geom_point(aes(shape = ..., color = ...))
Cheat Sheet: https://www.rstudio.com/wp-content/uploads/2016/11/ggplot2-cheatsheet-2.1.pdf
Al principio puede parecer demasiado complicado, pero igual que el resto de las funciones, con repetición y práctica se logra manejar y desarrollar intuición.
Ejemplos:
http://r-statistics.co/Top50-Ggplot2-Visualizations-MasterList-R-Code.html
Visita la página de: https://hoyodecrimen.com/
Es una página creada por un citadino con datos de criminalidad en la CDMX que consiguió a través de una solicitud de información a la SSP - CDMX para los años 2013-2019.
Su objetivo es que:
Con estos datos y su visualización, los ciudadanos y las autoridades podrían saber dónde se encuentran las áreas más violentas; los empresarios escogerían mejor dónde situar sus negocios, y los conductores sabrían en cuáles estacionamientos es más probable que les roben el auto… También se les pueden pedir cuentas a los policías y comandantes responsables de cada cuadrante o sector.
A partir de 2019 el gobierno de la Ciudad de México optó por una mayor transparencia con la iniciativa del “Portal de datos abiertos”. En esta página hay una gran cantidad de datos, incluyendo la información de las Carpetas de investigación de la PGJ-CDMX iniciadas por delitos ‘a nivel de calle’. Es una base mucho más completa y confiable que la de la SSP.
Vamos a importar los datos, explorar la estructura de la base, el acomodo y el tipo de variables y comenzar a crear gráficas con ggplot
.
# Primero cargar todos los paquetes que se van a utilizar: tidyverse y lubridate
library(lubridate) # para trabajar facilmente con fechas
library(tidyverse)
# O bien library(dplyr) ; library(ggplot2) ; library(tidyr) ; library(forcats)
# Importar los datos
crimen <- read.csv("crimen_2013_2019.csv", stringsAsFactors = FALSE)
# Explorar los datos
head(crimen) ; str(crimen)
cuadrante crime date hour year month
1 N-1.1.18 VIOLACION 2013-01-01 10:07:00 2013 1
2 O-3.1.3 ROBO A NEGOCIO C.V. 2013-01-01 18:36:00 2013 1
3 P-1.2.7 ROBO DE VEHICULO AUTOMOTOR S.V. 2013-01-01 01:31:00 2013 1
4 P-1.2.9 ROBO A TRANSEUNTE C.V. 2013-01-01 14:54:00 2013 1
5 P-1.2.2 ROBO DE VEHICULO AUTOMOTOR S.V. 2013-01-01 18:31:00 2013 1
6 P-1.5.2 LESIONES POR ARMA DE FUEGO 2013-01-01 20:03:00 2013 1
lat long
1 19.54408 -99.12472
2 19.18836 -99.07448
3 19.38082 -99.19989
4 19.36950 -99.19983
5 19.39336 -99.19945
6 19.38712 -99.22178
'data.frame': 211832 obs. of 8 variables:
$ cuadrante: chr "N-1.1.18" "O-3.1.3" "P-1.2.7" "P-1.2.9" ...
$ crime : chr "VIOLACION" "ROBO A NEGOCIO C.V." "ROBO DE VEHICULO AUTOMOTOR S.V." "ROBO A TRANSEUNTE C.V." ...
$ date : chr "2013-01-01" "2013-01-01" "2013-01-01" "2013-01-01" ...
$ hour : chr "10:07:00" "18:36:00" "01:31:00" "14:54:00" ...
$ year : int 2013 2013 2013 2013 2013 2013 2013 2013 2013 2013 ...
$ month : int 1 1 1 1 1 1 1 1 1 1 ...
$ lat : num 19.5 19.2 19.4 19.4 19.4 ...
$ long : num -99.1 -99.1 -99.2 -99.2 -99.2 ...
Podemos tener la curiosidad de saber cuáles fueron los crímenes más frecuentes en la ciudad durante el periodo comprendido entre 2013-2019.
En primer lugar, tenemos que contar los casos de cada cateogría de la columna de interés, que en este caso sería la columna crime
. Posteriormente se ordenan del más frecuente al menos frecuente.
frec_crimen <- crimen %>% count(crime) %>% arrange(-n)
# A tibble: 20 x 2
crime n
<chr> <int>
1 ROBO DE VEHICULO AUTOMOTOR S.V. 55009
2 ROBO A TRANSEUNTE C.V. 49983
3 ROBO DE VEHICULO AUTOMOTOR C.V. 27070
4 ROBO A NEGOCIO C.V. 20624
5 ROBO A REPARTIDOR C.V. 10522
6 LESIONES POR ARMA DE FUEGO 10481
7 HOMICIDIO DOLOSO 6914
8 ROBO A TRANSEUNTE S.V. 5535
9 ROBO A BORDO DE MICROBUS C.V. 5302
10 ROBO A BORDO DE METRO S.V. 4250
11 VIOLACION 3660
12 ROBO A CASA HABITACION C.V. 3383
13 ROBO A CUENTAHABIENTE C.V. 2789
14 ROBO A REPARTIDOR S.V. 2565
15 ROBO A BORDO DE TAXI C.V. 1449
16 ROBO A TRANSPORTISTA C.V. 961
17 ROBO A BORDO DE METRO C.V. 821
18 ROBO A BORDO DE MICROBUS S.V. 339
19 ROBO A TRANSPORTISTA S.V. 108
20 SECUESTRO 67
Ahora podemos intentar crear una gráfica a partir de esta información.
Hay varios ajustes que necesitamos hacer para obtener un resultado con el que estemos satisfechos.
Podemos voltear la orientación, ajusar colores, nombrar las etiquetas, modificar la leyenda, etc.
Un primer intento podría generar un gráfico como el siguiente, donde simplemente mapeamos las variables, elegimos una geometría y lo orientamos horizontalmente para facilitar la lectura de los tipos de delitos.
frec_crimen %>%
ggplot(aes(x = crime, y = n)) + geom_col() + coord_flip()
fct_reorder()
Cabe resaltar que aunque la tabla está ordenada de manera descendente, ggplot grafica la variable crime
en orden alfabético, por lo que es necesario reordenar con la función fct_reorder()
los ‘niveles’ de la variable para conseguir el efecto deseado.
frec_crimen %>%
ggplot(aes(x = fct_reorder(crime, -n), y = n)) +
geom_col() + coord_flip()
Por último podemos ajustar algunos argumentos adicionales para agregar color, etiquetas a los ejes, etc.
frec_crimen %>%
ggplot(aes(x = fct_reorder(crime, -n), y = n)) +
geom_col(aes(fill = fct_reorder(crime, -n))) + coord_flip() +
scale_fill_grey() +
labs(x = "",
y = "Total para el periodo 2013 - 2019",
title = "Frecuencia de cada delito en CDMX") +
theme(legend.position="none")
En este gráfico se puede apreciar fácilmente como se compara el robo de vehículo (sin violencia), con el resto de los 19 delitos incluidos en los datos.
Es rápidamente visible también que entre los delitos más frecuentes, la mayoría son C.V., es decir, realizados de manera violenta.
Podemos explorar los datos por zonas geográfica.
Los delitos están geo-referenciados, se indica el “cuadrante” donde ocurrió, además de las coordenadas geográficas.
Al haber tantas observaciones en este dataset, sencillamente graficar las coordenadas produce el contorno de la ciudad (particularmente hacia el norte).
Las variables lat
y long
contienen varios Missing Values, por lo que vamos primero a filtrar (quitar) esas observaciones, y después visualizar los datos.
crimen %>%
filter(!is.na(lat) & !is.na(long)) %>%
ggplot(aes(x = long, y = lat)) + geom_point()
Adicionalmente sería interesante poder visualizar diferencialmente las zonas con mayor número de delitos. Para ello podemos utilizar la variable de “cuadrante”. Hay 847 cuadrantes, así que podemos empezar por contar el total de delitos reportados en cada uno.
Lo vamos a hacer para todos los delitos y todos los años, pero con algunos cambios, también podríamos hacer el análisis para comparar delitos o comparar periodos de tiempo.
# Conteo de delitos por cuadrante y ordenando de mayor a menor
frec_cuadrante <- crimen %>% count(cuadrante) %>% arrange(-n)
Vamos a ver las primeras filas (los cuadrantes con más reportes):
# A tibble: 6 x 2
cuadrante n
<chr> <int>
1 (NO ESPECIFICADO) 7820
2 O-3.6.4 1190
3 O-2.5.1 984
4 P-3.5.9 971
5 N-4.4.1 912
6 O-2.5.7 876
Como pueden ver, existen variables que no tienen NA para los Missing Values sino un valor particular como “NO ESPECIFICADO”, que al fin y al cabo significa que no se tiene el dato. Es importante identificar estos casos para poder eliminar dichas observaciones.
frec_cuadrante <- crimen %>%
filter(cuadrante != "(NO ESPECIFICADO)") %>%
count(cuadrante) %>%
arrange(-n) %>%
rename(total_cuadrante = n)
# A tibble: 6 x 2
cuadrante total_cuadrante
<chr> <int>
1 O-3.6.4 1190
2 O-2.5.1 984
3 P-3.5.9 971
4 N-4.4.1 912
5 O-2.5.7 876
6 N-2.4.6 848
Esos números son un indicador sencillo para caracterizar cada cuadrante (tal vez se podría mejorar tomando la tasa de delitos de acuerdo a la población de cuada cuadrante por ejemplo).
Ahora una opción es agregar este indicador a la tabla original de manera que cada observación (cada punto) esté relacionada con un valor de este indicador.
Una manera de conjuntar ambas tablas es mediante una ‘unión’. DPLYR tiene una serie de funciones join()
para juntar dos tablas mediante una variable que tengan en común, en este caso sería la de cuadrante
.
crimen2 <- inner_join(crimen, frec_cuadrante, by = "cuadrante")
cuadrante crime date hour lat
1 N-1.1.18 VIOLACION 2013-01-01 10:07:00 19.54408
2 O-3.1.3 ROBO A NEGOCIO C.V. 2013-01-01 18:36:00 19.18836
3 P-1.2.7 ROBO DE VEHICULO AUTOMOTOR S.V. 2013-01-01 01:31:00 19.38082
4 P-1.2.9 ROBO A TRANSEUNTE C.V. 2013-01-01 14:54:00 19.36950
5 P-1.2.2 ROBO DE VEHICULO AUTOMOTOR S.V. 2013-01-01 18:31:00 19.39336
6 P-1.5.2 LESIONES POR ARMA DE FUEGO 2013-01-01 20:03:00 19.38712
long total_cuadrante
1 -99.12472 259
2 -99.07448 79
3 -99.19989 468
4 -99.19983 344
5 -99.19945 387
6 -99.22178 352
Una vez que tenemos esta columna, podemos mapearla como un estético para los puntos del gráfico. La opción más intuitiva es mapear esta nueva variable como el color. De esa manera el color resulta indicativo del nivel de actividad delictiva en las distintas zonas de la ciudad.
crimen2 %>%
filter(!is.na(lat) & !is.na(long)) %>%
ggplot(aes(x = long, y = lat)) +
geom_point(aes(color = total_cuadrante)) +
scale_color_continuous(name = "Delitos\nreportados\n2013-2019",
low = "white", high = "red3") +
theme_dark()