Análisis exploratorio de datos con tidyverse
Parte 1: Fundamentos de análisis exploratorio en R
dplyr para filtrar (filter), seleccionar (select), mutar (mutate), agrupar (group_by), y resumir (summarise) bases de datos en R
Si aún no cuentas con una instalación de tidyverse dentro de R corre la siguiente instrucción:
install.packages("tidyverse")1 El flujo de trabajo de trabajo con datos
En general el flujo de trabajo de un análisis de datos se divide en tres componentes principales:
- Preparación de los datos lo que incluye la recolección (no discutida aquí), la importación de los datos y la limpieza inicial (por ejemplo el poner nombres a las columnas u homologar mayúsculas y minúsculas) para generar una base de trabajo.
Una vez recolectados la recomendación es que las bases de datos no se toquen una vez se tiene la información. Entre menos modifiquemos la base de datos hay menor probabilidad de cometer errores y accidentes que resulten en pérdida de información.
- Análisis de datos incluye tres pasos que fluyen en cualquier orden:
- Formulación de preguntas ¿qué me gustaría saber de mis datos?_
- Visualización de los datos ¿puede una gráfica ayudarme a responder mi pregunta u orientarme hacia qué analizar?
- Análisis de los datos: ¿qué resumen de la información (por ejemplo una tabla o un promedio) resulta eficaz para presentar lo que me interesa?
- Una vez se tienen los datos analizados continuamos a la parte de comunicación donde buscamos generar tablas, gráficas y reportes (entre otros) que comuniquen nuestros datos al público.
El siguiente diagrama (traducido de aquí) intenta resumir el flujo de trabajo:
flowchart TB;
subgraph C[Comunicación]
Tablas --> Reportes
G[Gráficas] --> Reportes
end
subgraph AD[Análisis de datos]
FP[Preguntas] --> V[Visualización]
V --> FP
V --> AN[Análisis]
AN --> FP
AN --> V
FP --> AN
end
subgraph PD[Preparación de los datos]
R[Recolección] --> I[Importación]
I --> L[Limpieza]
end
PD --> AD --> C
2 Armado de un proyecto
En RStudio para un proyecto de análisis de datos la recomendación es crear un Project. Ésta es una carpeta especial en la cual se almacena todo el código, las bases de datos e incluso los registros de las versiones de los paquetes que estás usando Con pasarle a otra persona tu proyecto (Project) ésta podrá reproducir absolutamente todo sin preocuparse por tener que acomodar las rutas a los archivos o instalar los paquetes que tú usaste.
Para armar un proyecto puedes ir a File > New Project. Esto abrirá una ventana como sigue:
Existen diferentes opciones: empezar a crear un proyecto desde cero (New Directory) o bien trabajar con algún folder que ya tenemos Existing directory. El control de versiones (Version Control) es una herramienta más avanzada de programación y no la discutiremos por ahora.
Elige la opción de New Directory y en la ventana que sigue elige New R Proyect. Notarás que esto sirve para muchas cosas entre ellas armar presentaciones y páginas web. Por ahora trabajaremos sólo con código normal.
New Proyect para hacer un proyecto nuevo sin opciones específicas de los otros tipos.Finalmente en la tercer ventana elegimos el nombre del proyecto, el folder dentro de nuestros documentos donde habrá de guardarse y activamos las opciones:
renvsirve para controlar los paquetes deRusados. Si eliges la opciónrenv, cualquier otra persona que use el proyecto se le instalarán los paquetes que tú usaste y las versiones específicas (digamos tuggplot2es del 2021 entonces esa persona tendrá eseggplot2cuando abra el proyecto).gitsirve para guardar un historial de tu código. Explicaremos más adelante su funcionamiento principal pero la idea es que se guarde cada cambio que haces en el código (puesctrl Zes limitado).Open in new sessioncierra la ventana actual deRStudioy te abre el proyecto en un nuevo lienzo en blanco para que comiences tu trabajo.
AnalisisDatos, en mi carpeta de Dropbox con las opciones de git y renv habilitadas. Se solicitó a R además abrir en una sesión nueva.Una vez iniciado el proyecto nos aparecerá un mensaje similar a este en una nueva consola de `R``:
The version of R recorded in the lockfile will be updated:
- R [*] -> [4.2.1]
* Lockfile written to '~/AnalisisDatos/renv.lock'.¡Podemos empezar a usarlo!
Todas las bases de datos que utilicemos dentro de estas notas supondremos están almacenadas dentro del proyecto AnalisisDatos el cual está en tus documentos donde sea hayas elegido.
Por favor copia todos los datos que uses dentro del proyecto para que así, a cualquier persona que le envíes el proyecto pueda usarlo.
3 Lectura de bases de datos
R sirve para abrir cualquier tipo de dato. En particular es posible leer bases de datos desde Excel, csv, txt, Stata. También (aunque no lo veremos) puede leer imágenes, videos, datos provenientes de documentos pdf, páginas web, mapas, etc. Al día de hoy no he encontrado un sólo tipo de dato que no pueda leer R.
Las bases de datos si ya están recolectadas no se tocan. Lo que haremos en R será leerlas, copiarlas en una base nueva y trabajar sobre la base nueva sin modificar la original. De esta forma, cualquier error que cometamos ¡y seguro cometeremos muchos! no afectará la original. Esto permite además tener un flujo de trabajo seguro (en el sentido de que estamos seguros que no borraremos nada).
3.1 Lectura de datos desde un archivo delimitado (csv ó txt).
Los documentos de datos, si son (relativamente) medianas, pueden ser compartidos en archivos de texto que se pueden abrir con el bloc de notas. Por ejemplo, el documento casos_covid_agosto_2022.csv se ve como sigue:
"FECHA_SINTOMAS","n"
"2020-01-01","287"
"2020-01-02","233"
"2020-01-03","247"
"2020-01-04","245"
por otro lado el documento embarazo_adolescente.txt es un archivo delimitado por tabs (a veces llamados tsv) donde cada columna ocurre después de un tab (espacio largo):
Location PovPct Brth15to17 Brth18to19 ViolCrime TeenBrth
Alabama 20.1 31.5 88.7 11.2 54.5
Alaska 7.1 18.9 73.7 9.1 39.5
Arizona 16.1 35 102.5 10.4 61.2
Arkansas 14.9 31.6 101.7 10.4 59.9
finalmente, covid_sinave_bc_bcs.txt es un archivo donde las columnas son separadas por |:
SECTOR|ENTIDAD_UM|SEXO|ENTIDAD_NAC|ENTIDAD_RES|TIPO_PACIENTE|FECHA_INGRESO|FECHA_SINTOMAS|FECHA_DEF|EDAD|HABLA_LENGUA_INDIG|DIABETES|TOMA_MUESTRA_LAB|RESULTADO_LAB|TOMA_MUESTRA_ANTIGENO|RESULTADO_ANTIGENO|CLASIFICACION_FINAL|UCI
4|03|2|12|03|1|2021-07-05T00:00:00Z|2021-07-04T00:00:00Z|NA|22|2|2|2|97|1|1|3|97
4|03|2|03|03|1|2021-07-12T00:00:00Z|2021-07-11T00:00:00Z|NA|52|2|1|2|97|1|2|7|97
4|03|2|03|03|1|2021-07-22T00:00:00Z|2021-07-19T00:00:00Z|NA|19|2|2|2|97|1|2|7|97
12|03|1|03|03|1|2021-07-15T00:00:00Z|2021-07-13T00:00:00Z|NA|10|2|2|2|97|1|1|3|97
Todos estos archivos son archivos de texto simple delimitados y se pueden leer con la librería readr.
Lectura simple
Para leer los archivos basta con File > Import dataset o bien con código:
casos_covid <- read_csv("casos_covid_agosto_2022.csv")Para los archivos delimitados por espacios podemos usar read_tsv:
adolescentes <- read_tsv("embarazo_adolescente.txt")Finalmente archivos con delimitadores que no son comas ni tabs (como los | ) puedes usar read_delim y especificar el separador delim:
covid <- read_delim("covid_sinave_bc_bcs.txt", delim = "|")Complicaciones
La base de datos zapopan.csv contiene las estimaciones de la población del municipio homónimo por parte del INEGI. Sin embargo, al momento de leerla pasan dos cosas:
- Los acentos en algunos equipos no se leen bien
- La primera fila no representa nada pues sólo dice Datos.
Aquí una muestra de cómo viene el archivo. Nota que en mi equipo los acentos y las ñ aparecen como el símbolo ?:
Datos Poblacionales,,,,,,,,,,, Unidad,Nombre Unidad,Zona Coplademun,Habitantes,Hombres,Mujeres,Poblaci
Para leer esta base es necesario saltarnos la primera fila pues Datos Poblacionales no es un nombre de columna. Para ello usamos skip = 1 indicándole que se salte (skip) una fila (= 1). Por otro lado para los acentos usamos el encoding de WINDOWS-1252. Los más comunes para acentos en México son UTF-8 y WINDOWS-1252. Cuando no leemos bien los acentos vale la pena probar ambos para hallar el correcto:
zapopan <- read_csv("zapopan.csv",
locale = locale(encoding = "WINDOWS-1252"),
skip = 1)3.2 Lectura de datos desde un Excel
Lectura simple
Para leer datos desde un Excel podemos usar la Import From > Excel o bien read_excel dentro de la librería readxl:
library(readxl)dado un archivo como persona_cigarros.xlsx podemos leerlo directamente:
cigarros <- read_excel("persona_cigarros.xlsx")Complicaciones
Algunos archivos como Poblacion_01.xlsx contienen filas tanto al inicio como al final que funcionan como descriptores del archivo. Al momento de leerlo debemos ignorar estas partes:
Para ello en read_excel podemos especificar el rango de las celdas que nos interesa exportar indicando las dos esquinas del rectángulo de celdas:
poblacion <- read_excel("Poblacion_01.xlsx", range = "A5:C38")3.3 Ejercicios
- Lee las bases de datos
ile-2019-2021.csv,IME_2020.xlsyniños.txt.
4 Análisis de bases de datos
Vamos a leer la base de datos conjunto_de_datos_defunciones_generales_2017.csv la cual contiene el registro de mortalidad en México para el año 2017 del INEGI:
mortalidad_2017 <- read_csv("conjunto_de_datos_defunciones_generales_2017.csv")Aquí sólo muestro las filas de la base:
| ent_regis | mun_regis | ent_resid | mun_resid | tloc_resid | loc_resid | ent_ocurr | mun_ocurr | tloc_ocurr | loc_ocurr | causa_def | lista_mex | sexo | edad | dia_ocurr | mes_ocurr | anio_ocur | dia_regis | mes_regis | anio_regis | dia_nacim | mes_nacim | anio_nacim | ocupacion | escolarida | edo_civil | presunto | ocurr_trab | lugar_ocur | necropsia | asist_medi | sitio_ocur | cond_cert | nacionalid | derechohab | embarazo | rel_emba | horas | minutos | capitulo | grupo | lista1 | gr_lismex | vio_fami | area_ur | edad_agru | complicaro | dia_cert | mes_cert | anio_cert | maternas | lengua | cond_act | par_agre | ent_ocules | mun_ocules | loc_ocules | razon_m | dis_re_oax |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01 | 001 | 01 | 001 | 15 | 0001 | 01 | 001 | 15 | 0001 | I679 | 30Z | 2 | 4068 | 26 | 7 | 1994 | 19 | 1 | 2017 | 99 | 99 | 1926 | 11 | 3 | 3 | 8 | 8 | 88 | 9 | 1 | 1 | 2 | 1 | 1 | 8 | 8 | 8 | 45 | 9 | 7 | 069 | 30 | 8 | 1 | 18 | 8 | 99 | 1 | 2017 | NA | 9 | 2 | 88 | 88 | 888 | 8888 | 0 | 999 |
| 01 | 009 | 01 | 009 | 1 | 0016 | 01 | 009 | 1 | 0016 | I64X | 30D | 2 | 4090 | 10 | 11 | 2011 | 30 | 1 | 2017 | 31 | 10 | 1921 | 11 | 3 | 5 | 8 | 8 | 88 | 2 | 1 | 11 | 3 | 1 | 7 | 8 | 8 | 19 | 0 | 9 | 7 | 069 | 30 | 8 | 2 | 23 | 8 | 99 | 1 | 2017 | NA | 9 | 2 | 88 | 88 | 888 | 8888 | 0 | 999 |
| 01 | 001 | 01 | 001 | 15 | 0001 | 01 | 001 | 15 | 0001 | E112 | 20D | 1 | 4088 | 12 | 2 | 2011 | 99 | 1 | 2017 | 19 | 11 | 1922 | 4 | 3 | 3 | 8 | 8 | 88 | 2 | 2 | 11 | 3 | 1 | 2 | 8 | 8 | 7 | 0 | 4 | 2 | 052 | 20 | 8 | 1 | 22 | 8 | 99 | 1 | 2017 | NA | 9 | 1 | 88 | 88 | 888 | 8888 | 0 | 999 |
| 01 | 006 | 01 | 006 | 8 | 0001 | 01 | 006 | 8 | 0001 | X590 | 51Z | 2 | 4096 | 27 | 12 | 2016 | 10 | 1 | 2017 | 20 | 1 | 1920 | 11 | 3 | 3 | 1 | 9 | 9 | 9 | 1 | 1 | 1 | 1 | 7 | 8 | 8 | 11 | 10 | 20 | 25 | 103 | E51 | 8 | 1 | 24 | 8 | 27 | 12 | 2016 | NA | 2 | 2 | 88 | 88 | 888 | 8888 | 0 | 999 |
| 01 | 001 | 01 | 001 | 15 | 0001 | 01 | 001 | 15 | 0001 | I251 | 28Z | 2 | 4051 | 1 | 3 | 2016 | 13 | 1 | 2017 | 11 | 11 | 1964 | 9 | 1 | 2 | 8 | 8 | 88 | 2 | 1 | 11 | 3 | 1 | 99 | 9 | 9 | 14 | 30 | 9 | 4 | 067 | 28 | 8 | 1 | 15 | 9 | 1 | 3 | 2016 | NA | 9 | 1 | 88 | 88 | 888 | 8888 | 0 | 999 |
| 01 | 001 | 01 | 001 | 15 | 0001 | 01 | 001 | 15 | 0001 | N009 | 38A | 1 | 4078 | 1 | 7 | 2016 | 10 | 1 | 2017 | 8 | 5 | 1938 | 11 | 3 | 5 | 8 | 8 | 88 | 2 | 1 | 3 | 3 | 1 | 2 | 8 | 8 | 17 | 15 | 14 | 1 | 085 | 38 | 8 | 1 | 20 | 8 | 1 | 7 | 2016 | NA | 2 | 2 | 88 | 88 | 888 | 8888 | 0 | 999 |
La función glimpse nos permite darnos una idea de la composición de nuestros datos:
mortalidad_2017 %>% glimpse() Rows: 703,047
Columns: 59
$ ent_regis <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01",…
$ mun_regis <chr> "001", "009", "001", "006", "001", "001", "001", "001", "00…
$ ent_resid <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01",…
$ mun_resid <chr> "001", "009", "001", "006", "001", "001", "001", "001", "00…
$ tloc_resid <dbl> 15, 1, 15, 8, 15, 15, 15, 15, 15, 5, 15, 1, 15, 15, 1, 15, …
$ loc_resid <chr> "0001", "0016", "0001", "0001", "0001", "0001", "0001", "00…
$ ent_ocurr <chr> "01", "01", "01", "01", "01", "01", "01", "01", "01", "01",…
$ mun_ocurr <chr> "001", "009", "001", "006", "001", "001", "001", "001", "00…
$ tloc_ocurr <dbl> 15, 1, 15, 8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 1…
$ loc_ocurr <chr> "0001", "0016", "0001", "0001", "0001", "0001", "0001", "00…
$ causa_def <chr> "I679", "I64X", "E112", "X590", "I251", "N009", "E129", "M6…
$ lista_mex <chr> "30Z", "30D", "20D", "51Z", "28Z", "38A", "20D", "37H", "09…
$ sexo <dbl> 2, 2, 1, 2, 2, 1, 1, 2, 2, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1,…
$ edad <dbl> 4068, 4090, 4088, 4096, 4051, 4078, 4070, 4079, 4061, 4069,…
$ dia_ocurr <dbl> 26, 10, 12, 27, 1, 1, 19, 20, 7, 18, 18, 20, 1, 20, 28, 20,…
$ mes_ocurr <dbl> 7, 11, 2, 12, 3, 7, 12, 10, 1, 1, 12, 1, 1, 1, 11, 1, 12, 1…
$ anio_ocur <dbl> 1994, 2011, 2011, 2016, 2016, 2016, 2016, 2016, 2017, 2017,…
$ dia_regis <dbl> 19, 30, 99, 10, 13, 10, 13, 4, 11, 25, 3, 24, 9, 25, 2, 24,…
$ mes_regis <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ anio_regis <dbl> 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017,…
$ dia_nacim <dbl> 99, 31, 19, 20, 11, 8, 15, 22, 15, 1, 26, 18, 13, 28, 16, 4…
$ mes_nacim <dbl> 99, 10, 11, 1, 11, 5, 3, 6, 5, 2, 2, 5, 9, 9, 6, 7, 6, 7, 1…
$ anio_nacim <dbl> 1926, 1921, 1922, 1920, 1964, 1938, 1946, 1937, 1955, 1947,…
$ ocupacion <dbl> 11, 11, 4, 11, 9, 11, 2, 11, 2, 11, 2, 6, 11, 7, 6, 9, 2, 4…
$ escolarida <dbl> 3, 3, 3, 3, 1, 3, 10, 3, 10, 3, 9, 3, 4, 4, 1, 3, 9, 4, 1, …
$ edo_civil <dbl> 3, 5, 3, 3, 2, 5, 5, 5, 5, 5, 5, 5, 3, 5, 5, 2, 5, 5, 9, 5,…
$ presunto <dbl> 8, 8, 8, 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,…
$ ocurr_trab <dbl> 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,…
$ lugar_ocur <dbl> 88, 88, 88, 9, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, …
$ necropsia <dbl> 9, 2, 2, 9, 2, 2, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 2, 9, 2, 9,…
$ asist_medi <dbl> 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ sitio_ocur <dbl> 1, 11, 11, 1, 11, 3, 11, 11, 11, 3, 11, 3, 11, 3, 12, 3, 11…
$ cond_cert <dbl> 2, 3, 3, 1, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 1,…
$ nacionalid <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ derechohab <dbl> 1, 7, 2, 7, 99, 2, 2, 99, 2, 2, 1, 2, 1, 2, 2, 2, 99, 2, 3,…
$ embarazo <dbl> 8, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 8, 8,…
$ rel_emba <dbl> 8, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 8, 8,…
$ horas <dbl> 8, 19, 7, 11, 14, 17, 10, 0, 20, 4, 19, 4, 10, 9, 16, 10, 1…
$ minutos <dbl> 45, 0, 0, 10, 30, 15, 20, 20, 15, 40, 10, 7, 45, 15, 0, 15,…
$ capitulo <dbl> 9, 9, 4, 20, 9, 14, 4, 13, 2, 11, 2, 10, 9, 2, 4, 11, 9, 2,…
$ grupo <dbl> 7, 7, 2, 25, 4, 1, 2, 9, 2, 6, 2, 5, 7, 9, 2, 9, 4, 9, 5, 6…
$ lista1 <chr> "069", "069", "052", "103", "067", "085", "052", "083", "03…
$ gr_lismex <chr> "30", "30", "20", "E51", "28", "38", "20", "37", "09", "35"…
$ vio_fami <dbl> 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,…
$ area_ur <dbl> 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1,…
$ edad_agru <chr> "18", "23", "22", "24", "15", "20", "19", "20", "17", "18",…
$ complicaro <dbl> 8, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 8, 8, 8,…
$ dia_cert <dbl> 99, 99, 99, 27, 1, 1, 19, 20, 7, 18, 19, 20, 1, 20, 28, 20,…
$ mes_cert <dbl> 1, 1, 1, 12, 3, 7, 12, 10, 1, 1, 12, 1, 1, 1, 11, 1, 12, 1,…
$ anio_cert <dbl> 2017, 2017, 2017, 2016, 2016, 2016, 2016, 2016, 2017, 2017,…
$ maternas <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ lengua <dbl> 9, 9, 9, 2, 9, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ cond_act <dbl> 2, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1,…
$ par_agre <dbl> 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,…
$ ent_ocules <dbl> 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,…
$ mun_ocules <chr> "888", "888", "888", "888", "888", "888", "888", "888", "88…
$ loc_ocules <chr> "8888", "8888", "8888", "8888", "8888", "8888", "8888", "88…
$ razon_m <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ dis_re_oax <dbl> 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999, 999,…
Hacer esto es lo mismo que hacerlo con el pipe nativo de R, |>:
mortalidad_2017 |> glimpse() o bien ponerlo dentro del glimpse:
glimpse(mortalidad_2017) El comando ncol nos muestra el número de columnas en la base de datos y tally nos cuenta el número de filas en la base de datos.
mortalidad_2017 %>% ncol() #Columnas[1] 59
mortalidad_2017 %>% tally() #Filas# A tibble: 1 × 1
n
<int>
1 703047
El comando nrow hace lo mismo: conteo de filas. Pero no es tan recomendado como veremos más adelante.
mortalidad_2017 %>% nrow() #Filas[1] 703047
Podemos ver los nombres de las columnas de la base de datos con colnames:
#Mostramos las columnas
mortalidad_2017 %>% colnames() [1] "ent_regis" "mun_regis" "ent_resid" "mun_resid" "tloc_resid"
[6] "loc_resid" "ent_ocurr" "mun_ocurr" "tloc_ocurr" "loc_ocurr"
[11] "causa_def" "lista_mex" "sexo" "edad" "dia_ocurr"
[16] "mes_ocurr" "anio_ocur" "dia_regis" "mes_regis" "anio_regis"
[21] "dia_nacim" "mes_nacim" "anio_nacim" "ocupacion" "escolarida"
[26] "edo_civil" "presunto" "ocurr_trab" "lugar_ocur" "necropsia"
[31] "asist_medi" "sitio_ocur" "cond_cert" "nacionalid" "derechohab"
[36] "embarazo" "rel_emba" "horas" "minutos" "capitulo"
[41] "grupo" "lista1" "gr_lismex" "vio_fami" "area_ur"
[46] "edad_agru" "complicaro" "dia_cert" "mes_cert" "anio_cert"
[51] "maternas" "lengua" "cond_act" "par_agre" "ent_ocules"
[56] "mun_ocules" "loc_ocules" "razon_m" "dis_re_oax"
Una descripción de las variables las puedes encontrar en el archivo diccionario_datos_defunciones_generales_2017.csv. Donde aparece como sigue:
| NOMBRE_CAMPO | LONGITUD | TIPO | NEMÓNICO | CATÁLOGO | RANGO_CLAVES |
|---|---|---|---|---|---|
| Entidad Registro | 2 | N | ent_regis | decateml | 01-32 |
| Municipio Registro | 3 | N | mun_regis | decateml | 001-570 |
| Entidad Residencia | 2 | N | ent_resid | decateml | 01-35, 99 |
| Municipio Residencia | 3 | N | mun_resid | decateml | 001-570, 999 |
| Tamaño Localidad Residencia | 2 | N | tloc_resid | detamloc | 01-17,99 |
| Clave Localidad de Residencia | 4 | N | loc_resid | decateml | 0001 - 6999, 9999 |
| Entidad Ocurrencia | 2 | N | ent_ocurr | decateml | 01-35,99 |
| Municipio Ocurrencia | 3 | N | mun_ocurr | decateml | 001-570,999 |
| Tamaño de Localidad Ocurrencia | 2 | N | tloc_ocurr | detamloc | 01-17,99 |
| Clave Localidad de Ocurrencia | 4 | N | loc_ocur | decateml | 0001 - 6999, 9999 |
| Causa Defunción (Lista Detallada) | 4 | A | causa_def | decatcausa | NA |
| Causa Defunción (Lista Mexicana) | 3 | A | lista_mex | delistamex | NA |
| Sexo | 1 | N | sexo | desexo | 1-2,9 |
| Edad | 4 | N | edad | deedad | 1001-1023, 1097,1098, 2001-2029,2098, 3001-3011,3098, 4001-4120, 4998 |
| Día Defunción | 2 | N | dia_ocurr | dedias | 01-31,99 |
| Mes Defunción | 2 | N | mes_ocurr | demeses | 01-12, 99 |
| Año Defunción | 4 | N | anio_ocur | deaño | 1900-Año estadística, 9999 |
| Día Registro | 2 | N | dia_regis | dedias | 01-31,99 |
| Mes Registro | 2 | N | mes_regis | demeses | 01-12 |
| Año Registro | 4 | N | anio_regis | deaño | Año estadística |
| Día Nacimiento | 2 | N | dia_nacim | dedias | 01-31,99 |
| Mes Nacimiento | 2 | N | mes_nacim | demeses | 01-12,99 |
| Año Nacimiento | 4 | N | anio_nacim | deaño | 1900-Año estadística, 9999 |
| Ocupación | 2 | N | ocupacion | deocupa | 1 - 11, 97, 98, 99 |
| Escolaridad | 2 | N | escolarida | deesco | 1-10, 88, 99 |
| Estado Conyugal | 1 | N | edo_civil | deedocony | 1-6, 8, 9 |
| Presunto | 1 | N | presunto | depresunto | 1-5, 8 |
| Ocurrió Trabajo | 1 | N | ocurr_trab | deocutrab | 1-2, 8, 9 |
| Sitio donde Ocurrio la Lesión | 2 | N | lugar_ocur | desitiolesion | 0 - 9, 88 |
| Necropsia | 1 | N | necropsia | denecrop | 1-2, 9 |
| Asistencia Médica | 1 | N | asist_medi | deasismed | 1-2,9 |
| Sitio donde Ocurrio la Defunción | 2 | N | sitio_ocur | desitiodefun | 1-12,99 |
| Certificada Por | 1 | N | cond_cert | deccertif | 1-5, 8, 9 |
| Nacionalidad | 1 | N | nacionalid | denacion | 1-2, 9 |
| Derechohabiencia | 2 | N | derechohab | dederech | 1-9, 99 |
| Condición de embarazo | 1 | N | embarazo | decondemba | 1-6, 8, 9 |
| Causas relacionadas con embarazo | 1 | N | rel_emba | derelemba | 1,2, 8, 9 |
| Hora de la defunción | 2 | N | horas | dehoradef | 00-23, 99 |
| Minuto de la defunción | 2 | N | minutos | demindef | 00-59, 99 |
| Capitulo | 2 | N | capitulo | decapgpo | NA |
| Grupo | 2 | N | grupo | decapgpo | NA |
| Lista1 | 3 | A | lista1 | delista1 | NA |
| Grupos lista mexicana | 3 | A | gr_lismex | degpolisme | NA |
| Violencia Familiar | 1 | N | vio_fami | deviofam | 1, 2, 8, 9 |
| Area Urbano Rural | 1 | N | area_ur | deurbrur | 1, 2, 9 |
| Edad agrupada | 2 | C | edad_agru | deedadagrup | 01-30 |
| Complicaron el embarazo | 1 | N | complicaro | decomplicaemba | 1, 2, 8, 9 |
| Día de Certificación | 2 | N | dia_cert | dedias | 01-31,99 |
| Mes de Certificación | 2 | N | mes_cert | demeses | 01-12, 99 |
| Año de Certificación | 4 | N | anio_cert | deaño | AE* -1, AE*, 9999 |
| Maternas | 4 | C | maternas | decatcausa | NA |
| Lengua indígena | 1 | N | lengua | delengindi | 1, 2, 8, 9 |
| Condición de Actividad económica | 1 | N | cond_act | decondact | 1, 2, 8, 9 |
| Parentesco del presunto agresor | 2 | N | par_agre | deparenagresor | 1 - 72, 88, 99 |
| Entidad Ocurrencia de la lesión | 2 | N | ent_ocules | decateml | 01-35,88, 99 |
| Municipio Ocurrencia de la lesión | 3 | N | mun_ocules | decateml | 001-570, 888, 999 |
| Clave Localidad de Ocurrencia de la lesión | 4 | N | loc_ocules | decateml | 0001 - 6999, 8888 9999 |
| Razón Materna | 1 | N | razon_m | derazonm | 1 |
| Distritos de Oaxaca | 3 | N | dis_re_oax | decateml | 901-930, 999 |
Una vez tenemos una base de datos podemos analizar sus entradas usando el nombre de la base y corchetes. Por ejemplo:
#Obtengo todo el registro de la columna sexo
mortalidad_2017[ ,"sexo"]# A tibble: 6 × 1
sexo
<dbl>
1 2
2 2
3 1
4 2
5 2
6 1
También podemos usar el número de columna:
#Me regresa las observaciones de la columna 2
#dada por mun_regis
#| eval: false
mortalidad_2017[ ,2]# A tibble: 703,047 × 1
mun_regis
<chr>
1 001
2 009
3 001
4 006
5 001
6 001
7 001
8 001
9 001
10 001
# … with 703,037 more rows
#Me regresa las observaciones de la columna 2
#dada por mun_regis
head(mortalidad_2017[ , 2])También podemos acceder a la base por renglones. Por ejemplo, así vemos el tercer renglón:
mortalidad_2017[3, ]# A tibble: 1 × 6
ent_regis mun_regis ent_resid mun_resid tloc_resid loc_resid
<chr> <chr> <chr> <chr> <dbl> <chr>
1 01 001 01 001 15 0001
Finalmente, podemos combinar la primer entrada de la columna lista1 se vería así:
mortalidad_2017[1, "lista1"]# A tibble: 1 × 1
lista1
<chr>
1 069
En resumen, la notación siempre es de la siguiente forma:
\[
\text{datos}[\underbrace{f}_{fila},\overbrace{c}^{columna}]
\] Esta notación notación matricial es estándar en el mundo de computación y de las matemáticas. Siempre siempre primero es fila luego columna.
Otra forma de seleccionar una columna es con $ seguido del nombre de la columna. Por ejemplo:
mortalidad_2017$edo_civil[1] 3 5 3 3 2 5
Por ciertos errores en los que se pueden incurrir al crear funciones, se recomienda que se utilice siempre la notación de doble corchete y se evite el signo de $. Pero ambos métodos funcionan. Sólo recuerda que la notación es así:
\[
\text{datos}\$\text{Columna}[\underbrace{f}_{\text{Fila}}]
\] Una última opción es con select:
mortalidad_2017 %>% select(edo_civil)# A tibble: 6 × 1
edo_civil
<dbl>
1 3
2 5
3 3
4 3
5 2
6 5
la cual nos regresa la columna de estado civil. El comando select es bastante útil si, por ejemplo, no recordamos exactamente el nombre de la columna. Por ejemplo, si sólo recordamos que tiene la palabra "civil" pero no exactamente cómo se escribe podemos usar contains:
#Podemos usar contains si recordamos que dice civil
#pero no el nombre completo
mortalidad_2017 %>% select(contains("civil"))# A tibble: 703,047 × 1
edo_civil
<dbl>
1 3
2 5
3 3
4 3
5 2
6 5
7 5
8 5
9 5
10 5
# … with 703,037 more rows
Podemos preguntarnos, de igual manera por la 7a entrada de edo_civil haciendo:
mortalidad_2017$edo_civil[7][1] 5
Lo cual es similar (mas no equivalente) a la forma anterior:
mortalidad_2017[7, "edo_civil"]# A tibble: 1 × 1
edo_civil
<dbl>
1 5
En el caso del tidyverse el equivalente a select, para elegir fila, el equivalente se conoce como slice:
#Selecciona la fila 11
mortalidad_2017 %>% slice(11)# A tibble: 1 × 59
ent_regis mun_regis ent_resid mun_re…¹ tloc_…² loc_r…³ ent_o…⁴ mun_o…⁵ tloc_…⁶
<chr> <chr> <chr> <chr> <dbl> <chr> <chr> <chr> <dbl>
1 01 001 01 001 15 0001 01 001 15
# … with 50 more variables: loc_ocurr <chr>, causa_def <chr>, lista_mex <chr>,
# sexo <dbl>, edad <dbl>, dia_ocurr <dbl>, mes_ocurr <dbl>, anio_ocur <dbl>,
# dia_regis <dbl>, mes_regis <dbl>, anio_regis <dbl>, dia_nacim <dbl>,
# mes_nacim <dbl>, anio_nacim <dbl>, ocupacion <dbl>, escolarida <dbl>,
# edo_civil <dbl>, presunto <dbl>, ocurr_trab <dbl>, lugar_ocur <dbl>,
# necropsia <dbl>, asist_medi <dbl>, sitio_ocur <dbl>, cond_cert <dbl>,
# nacionalid <dbl>, derechohab <dbl>, embarazo <dbl>, rel_emba <dbl>, …
Podemos combinar múltiples argumentos con %>%1:
1 En el mismo renglón debe estar el %>% que el último comando para no generar error
mortalidad_2017 %>%
select(sexo, edo_civil) %>% #Selecciona edo civil y sexo
slice(11:20) #Selecciona filas 11 a 20# A tibble: 10 × 2
sexo edo_civil
<dbl> <dbl>
1 1 5
2 1 5
3 2 3
4 1 5
5 1 5
6 2 2
7 1 5
8 1 5
9 2 9
10 1 5
Esta forma de combinación con pipes (%>% ó |>) será bastante útil más adelante.
Para seleccionar múltiples filas o columnas, como vimos en el ejemplo anterior, hay que crear un vector usando c2. Un vector es una lista ordenada de variables del mismo tipo. Por ejemplo:
2 Se llama c por concatenate (concatenar).
mi_vector <- c(1, 141, 12)Es un vector de 3 entradas. De hecho, la forma de acceder a sus entradas es la misma que la de los tibbles con un agregado: ¡sólo hay filas, no hay columnas!
Intuitivamente podemos pensar un vector como una columna de una base de datos. En ella todas las variables son del mismo tipo.
Para acceder a las entradas de un vector es de la siguiente forma:
#Para un vector sólo pongo el número de entrada ¡no hay columna!
mi_vector[2] #entrada 2[1] 141
En general la notación es la siguiente:
\[ \text{vector}[\underbrace{i}_{\text{Entrada}}] \]
Un vector que ya nos encontramos antes es el de columnas. Podemos, por ejemplo, ver el nombre de la cuarta columna de la base combinando lo que sabemos de vectores con el comando colnames:
#Regresa el nombre de la 5a columna
colnames(mortalidad_2017)[5][1] "tloc_resid"
La pregunta también puede hacerse al revés: podemos pedirle a R que nos conteste cuál es el número de columna para una columna dada. Por ejemplo, ¿cuál es el número de columna para edad?
#Preguntamos a R cuál de las columnas se llama edad
which(colnames(mortalidad_2017) == "edad")[1] 14
Nota que si preguntamos por una columna que no existe, R nos regresa lo siguiente:
#Preguntamos a R cuál de las columnas se llama edad
which(colnames(mortalidad_2017) == "Paraguas")integer(0)
Esto significa que no hay ninguna columna con ese nombre. Finalmente, nota que el which puede regresar múltiples resultados si las cosas se repiten. Por ejemplo, el siguiente comando nos regresa las múltiples entradas del vector nombres_de_amigos donde hay un amigo que tiene el nombre de Alejandro:
#Creo un vector con nombres de mis amigos
nombres_de_amigos <- c("Alejandro", "Beatriz", "Alejandro", "Carla")
#Pregunto por cuáles entradas contienen a Alejandro
which(nombres_de_amigos == "Alejandro")[1] 1 3
Finalmente, podemos usar vectores para seleccionar múltiples columnas y filas, por ejemplo si deseo seleccionar, de la base, las columnas de sexo y edad:
mortalidad_2017[ , c("sexo", "edad")]# A tibble: 6 × 2
sexo edad
<dbl> <dbl>
1 2 4068
2 2 4090
3 1 4088
4 2 4096
5 2 4051
6 1 4078
Finalmente si quiero seleccionar sólo algunos renglones puedo ponerlos en un vector:
#Selecciona sólo el renglón 1 y el 7
mortalidad_2017[c(1, 7), c("sexo", "edad")]# A tibble: 2 × 2
sexo edad
<dbl> <dbl>
1 2 4068
2 1 4070
Mientras que el : selecciona del renglón 1 al renglón 7
#Selecciona del 1 al 7
mortalidad_2017[1:7, c("sexo", "edad")]# A tibble: 7 × 2
sexo edad
<dbl> <dbl>
1 2 4068
2 2 4090
3 1 4088
4 2 4096
5 2 4051
6 1 4078
7 1 4070
El equivalente usando select y slice es:
mortalidad_2017 %>%
select(sexo, edad) %>%
slice(1:7)# A tibble: 7 × 2
sexo edad
<dbl> <dbl>
1 2 4068
2 2 4090
3 1 4088
4 2 4096
5 2 4051
6 1 4078
7 1 4070
#Selecciona renglones del 2 al 7
#y las columnas 5 a 9
mortalidad_2017[2:7, 5:9]# A tibble: 6 × 5
tloc_resid loc_resid ent_ocurr mun_ocurr tloc_ocurr
<dbl> <chr> <chr> <chr> <dbl>
1 1 0016 01 009 1
2 15 0001 01 001 15
3 8 0001 01 006 8
4 15 0001 01 001 15
5 15 0001 01 001 15
6 15 0001 01 001 15
Los : también funcionan para seleccionar rangos de columnas a través del select:
#Selecciona todas las columnas desde loc_resid hasta tloc_ocurr
mortalidad_2017 %>%
select(loc_resid:tloc_ocurr) # A tibble: 703,047 × 4
loc_resid ent_ocurr mun_ocurr tloc_ocurr
<chr> <chr> <chr> <dbl>
1 0001 01 001 15
2 0016 01 009 1
3 0001 01 001 15
4 0001 01 006 8
5 0001 01 001 15
6 0001 01 001 15
7 0001 01 001 15
8 0001 01 001 15
9 0001 01 001 15
10 1025 01 001 15
# … with 703,037 more rows
5 Ejercicios
- Lee la hoja
MCV2del archivounicef_vacunas.xlsxel cual contiene información sobre vacunasMCV2dada por la UNICEF. Responde las siguientes preguntas:
- Determina los nombres de todas las columnas de las variables usando
R(no vale ver el archivo). - Determina el número de renglones y el número de filas totales de la base.
- Encuentra cuál fila contiene a México en la columna de
country. - Regresa todas las mediciones para la fila de Colombia.
- Regresa la 5a columna.
- Obtén el nombre de la columna
9. - Obtén la entrada en la columna
5y fila14.
- Considera la base de datos creada por el siguiente código:
base_inventada <- tibble(Tiempo = rexp(100),
Enfermo = rbinom(100, 83, 1/3))Determina qué ocasionó los resultados siguientes:
- ¿Por qué da error?
#Primer error
base_inventada[ , 200]Error in `base_inventada[, 200]`:
! Can't subset columns past the end.
ℹ Location 200 doesn't exist.
ℹ There are only 2 columns.
- ¿Por qué da
NA?
#NA
base_inventada[1000, ]# A tibble: 1 × 2
Tiempo Enfermo
<dbl> <int>
1 NA NA
- ¿Por qué dice esto?
#Primer error
base_inventada[0,0]# A tibble: 0 × 0
- ¿Por qué da error`?
base_inventada[2, c("tiempo")]Error in `base_inventada[2, c("tiempo")]`:
! Can't subset columns that don't exist.
✖ Column `tiempo` doesn't exist.
- Arregla este código para que no dé error
base.inventada %>% select("Tiempo")
%>% slice(2)6 Limpieza de la base de datos
6.1 Seleccionar columnas con select
De la base de mortalidad comencemos por quedarnos sólo con las columnas ent_regis,mun_regis, sexo, edad, dia_ocurr, mes_ocurr, anio_ocurr y causa_def. Para ello seleccionamos múltiples columnas mediante un vector con el nombre de las columnas a seleccionar.
R trabaja con una copia de la base de datos. Todo lo que hacemos aquí nunca va a cambiar la base de datos a menos que explícitamente se lo pidamos. ¡Es genial porque nos evitamos el riesgo de perder información!
#Reescribimos la variable sólo con las columnas que nos
#interesan
mortalidad_2017 <- mortalidad_2017 %>%
select(ent_regis, mun_regis, sexo, edad, dia_ocurr, mes_ocurr, anio_ocur, causa_def)Ojo que aquí estoy sobreescribiendo la copia de R de la base de mortalidad_2017. Es decir, estoy editando la base mortalidad_2017 y guardándola en sí misma (para que se queden los cambios). También lo pude haber guardado en otra variable:
#Reescribimos la variable sólo con las columnas que nos
#interesan
#| eval: false
mortalidad_2017_pedazo <- mortalidad_2017 %>%
select(ent_regis, mun_regis, sexo, edad, dia_ocurr, mes_ocurr, anio_ocur, causa_def)El punto importante es guardarla porque de otra forma los cambios no son permanentes. Por ejemplo el siguiente comando de seleccionar sólo sexo y edad no edita la base pues no se asigna a la base:
#Reescribimos la variable sólo con las columnas que nos
#interesan
mortalidad_2017 %>%
select(sexo, edad)# A tibble: 703,047 × 2
sexo edad
<dbl> <dbl>
1 2 4068
2 2 4090
3 1 4088
4 2 4096
5 2 4051
6 1 4078
7 1 4070
8 2 4079
9 2 4061
10 1 4069
# … with 703,037 more rows
6.2 Renombrar columnas con rename
Utilicemos rename para mejorar los nombres de las variables. El comando es:
rename(`Nuevo nombre` = `Viejo nombre`)Por ejemplo:
mortalidad_2017 <- mortalidad_2017 %>%
rename(`Entidad` = `ent_regis`)También podemos renombrar varias columnas de golpe aplicando varios rename concatenados por el %>% o bien poniendo muchas igualdades dentro del rename:
mortalidad_2017 <- mortalidad_2017 %>%
rename(`Sexo` = `sexo`) %>%
rename(`Edad` = `edad`) %>%
rename(`Día` = `dia_ocurr`,
`Mes` = `mes_ocurr`,
`Año` = `anio_ocur`,
`Causa` = `causa_def`,
`Municipio`= `mun_regis`)6.3 Filtrar filas con filter
Podemos reducir nuestra base quedándonos, por ejemplo, con aquellas observaciones que ocurrieron en el Año 2017. Nota que el número de filas cambia de:
#Antes de filtrar
mortalidad_2017 %>% tally()# A tibble: 1 × 1
n
<int>
1 703047
#Filtro
mortalidad_2017 <- mortalidad_2017 %>%
filter(Año == 2017)#Después de filtrar
mortalidad_2017 %>%
tally()# A tibble: 1 × 1
n
<int>
1 688468
El filter opera poniéndole una condición adentro. Aquí te copio las instrucciones más comunes:
| Nombre | Instrucción | Significado |
|---|---|---|
| Igualdad | x == y |
x es igual a y |
| Mayor/menor | x > y |
x es menor que y |
| Mayor/menor igual | x >= y |
x es mayor o igual que y |
| Diferente | x != y |
x es distinto de y (\(x \neq y\)) |
| Pertenencia | x %in% y |
x está en y si y es un vector c |
| O | A | B |
al menos una de las dos condiciones A ó B es verdadera |
| Y | A & B |
ambas A y B son verdaderas |
| No | !A |
lo contrario a la condición A |
| Búsqueda de palabras | str_detect(Columna, "palabra") |
Devuelve aquellos casos donde la columna Columna contiene la palabra "palabra" |
| Identifica datos faltantes | is.na |
is.na(Columna) |
Podemos jugar más con el filter por ejemplo devolviendo aquellas defunciones en un mes después de febrero (mes 2):
mortalidad_2017 %>%
filter(Mes > 2)# A tibble: 568,041 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 1 4079 22 3 2017 C61X
2 01 001 2 4080 4 4 2017 M623
3 01 001 1 4075 6 4 2017 E149
4 01 001 2 1097 27 3 2017 P832
5 01 001 2 4075 1 5 2017 E46X
6 01 001 1 4085 30 4 2017 I500
7 01 011 1 4092 17 11 2017 F102
8 01 003 1 4061 2 5 2017 C189
9 01 003 1 4024 21 4 2017 C959
10 01 003 2 4073 24 4 2017 E142
# … with 568,031 more rows
Aquellas defunciones cuyo mes es antes de febrero (incluyendo febrero):
mortalidad_2017 %>%
filter(Mes <= 2)# A tibble: 120,427 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4061 7 1 2017 C259
2 01 001 1 4069 18 1 2017 K631
3 01 001 1 4091 20 1 2017 J441
4 01 001 2 4076 1 1 2017 I698
5 01 001 1 4070 20 1 2017 C61X
6 01 001 2 4048 20 1 2017 K803
7 01 001 1 4062 12 1 2017 C61X
8 01 001 1 4089 13 1 2017 N390
9 01 001 2 4042 8 1 2017 E117
10 01 001 2 4085 14 1 2017 I120
# … with 120,417 more rows
Aquellas defunciones cuyos días estén entre el día 7 y el 9
mortalidad_2017 %>%
filter(Día %in% c(7,8,9)) #o bien 7:9 en lugar del c# A tibble: 67,650 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4061 7 1 2017 C259
2 01 001 2 4042 8 1 2017 E117
3 01 001 1 4073 8 2 2017 J440
4 01 001 1 4088 7 2 2017 I802
5 01 001 1 4067 9 1 2017 E119
6 01 001 1 4019 7 1 2017 Q070
7 01 001 2 4062 9 1 2017 I678
8 01 007 1 4057 8 5 2017 K703
9 01 001 2 4052 7 1 2017 K274
10 01 007 1 4059 9 3 2017 J180
# … with 67,640 more rows
Aquellas defunciones cuyo día no esté entre el día 7 y el 9
mortalidad_2017 %>%
filter(!(Día %in% c(7,8,9))) #o bien 7:9 en lugar del c# A tibble: 620,818 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 1 4069 18 1 2017 K631
2 01 001 1 4091 20 1 2017 J441
3 01 001 2 4076 1 1 2017 I698
4 01 001 1 4070 20 1 2017 C61X
5 01 001 2 4048 20 1 2017 K803
6 01 001 1 4062 12 1 2017 C61X
7 01 001 1 4089 13 1 2017 N390
8 01 001 2 4085 14 1 2017 I120
9 01 001 2 4042 19 1 2017 K729
10 01 001 2 4067 3 1 2017 I120
# … with 620,808 more rows
Aquellas defunciones en enero el día 14:
mortalidad_2017 %>%
filter(Día == 14 & Mes == 1) #o bien 7:9 en lugar del c# A tibble: 2,035 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4085 14 1 2017 I120
2 01 001 1 4083 14 1 2017 I698
3 01 001 1 4089 14 1 2017 R54X
4 01 001 1 4078 14 1 2017 E119
5 01 002 1 4062 14 1 2017 E142
6 01 001 1 4076 14 1 2017 I64X
7 01 001 2 4085 14 1 2017 I210
8 01 009 2 4078 14 1 2017 I219
9 01 001 2 4087 14 1 2017 I119
10 01 007 1 4056 14 1 2017 E119
# … with 2,025 more rows
O bien:
mortalidad_2017 %>%
filter(Día == 14) %>%
filter(Mes == 1) # A tibble: 2,035 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4085 14 1 2017 I120
2 01 001 1 4083 14 1 2017 I698
3 01 001 1 4089 14 1 2017 R54X
4 01 001 1 4078 14 1 2017 E119
5 01 002 1 4062 14 1 2017 E142
6 01 001 1 4076 14 1 2017 I64X
7 01 001 2 4085 14 1 2017 I210
8 01 009 2 4078 14 1 2017 I219
9 01 001 2 4087 14 1 2017 I119
10 01 007 1 4056 14 1 2017 E119
# … with 2,025 more rows
Aquellas defunciones donde el mes sea enero o diciembre:
mortalidad_2017 %>%
filter(Mes == 1 | Mes == 12)# A tibble: 121,073 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4061 7 1 2017 C259
2 01 001 1 4069 18 1 2017 K631
3 01 001 1 4091 20 1 2017 J441
4 01 001 2 4076 1 1 2017 I698
5 01 001 1 4070 20 1 2017 C61X
6 01 001 2 4048 20 1 2017 K803
7 01 001 1 4062 12 1 2017 C61X
8 01 001 1 4089 13 1 2017 N390
9 01 001 2 4042 8 1 2017 E117
10 01 001 2 4085 14 1 2017 I120
# … with 121,063 more rows
Aquellas defunciones donde el mes sea distinto de octubre:
mortalidad_2017 %>%
filter(Mes != 10)# A tibble: 630,907 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4061 7 1 2017 C259
2 01 001 1 4069 18 1 2017 K631
3 01 001 1 4091 20 1 2017 J441
4 01 001 2 4076 1 1 2017 I698
5 01 001 1 4070 20 1 2017 C61X
6 01 001 2 4048 20 1 2017 K803
7 01 001 1 4062 12 1 2017 C61X
8 01 001 1 4089 13 1 2017 N390
9 01 001 2 4042 8 1 2017 E117
10 01 001 2 4085 14 1 2017 I120
# … with 630,897 more rows
Aquellas defunciones cuya causa contenga el caracter C5:
mortalidad_2017 %>%
filter(str_detect(Causa,"C5"))# A tibble: 14,701 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 2 4049 6 1 2017 C509
2 01 001 2 4071 23 1 2017 C509
3 01 001 2 4053 20 1 2017 C56X
4 01 001 2 4066 18 2 2017 C56X
5 01 001 2 4029 2 1 2017 C509
6 01 001 2 4063 5 12 2017 C56X
7 01 001 2 4059 6 1 2017 C56X
8 01 001 2 4047 31 1 2017 C539
9 01 001 2 4064 27 1 2017 C56X
10 01 001 2 4058 31 1 2017 C56X
# … with 14,691 more rows
Ejercicio
En la base unicef_vacunas.xlsx abre la hoja HEPBB con información de la cobertura de dicha vacuna. Determina:
- ¿Cuántos países están registrados para la región
ROSA? - ¿Cuántos países en
2020lograron una cobertura mayor al 90%? - Dentro de la región de Latinoamérica y el Caribe (
LACR) ¿cuántos países lograron una cobertura menor a 80%? - ¿Cuál fue la cobertura de México, Colombia y Uruguay en
2020? - ¿Cuántos países no contienen datos para 1990?
6.4 Estadísticos de resumen con summarise
La función de summarise sirve para colapsar columnas en números. Por ejemplo: obtener el promedio (mean) de una columna, obtener el máximo (max), un cuantil (quantile), una suma (sum) o un conteo (n). Por ejemplo para obtener el promedio de la columna Edad basta con usar mean adentro de summarise:
mortalidad_2017 %>%
summarise(`Edad promedio` = mean(Edad))# A tibble: 1 × 1
`Edad promedio`
<dbl>
1 4000.
Nota que aquí la edad sale altísima y esto es porque el INEGI empieza a contar los años a partir del 4000 siendo 4001 un año, 4002 dos años etc. Por otro lado es necesario eliminar a aquellos con edad de 4998 pues corresponde a Edad no especificada. Para ello filtramos (filter) y luego calculamos la media (mean) de edad menos 4000:
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Edad promedio` = mean(Edad - 4000))# A tibble: 1 × 1
`Edad promedio`
<dbl>
1 65.5
Esto nos dice que en el 2017 el promedio de edad de las defunciones de más de un año fue de 65 años.
Observa que ese promedio es lo mismo que haber sumado todas las edades con sum y luego dividido entre el total de observaciones n:
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Suma de edades` = sum(Edad - 4000))# A tibble: 1 × 1
`Suma de edades`
<dbl>
1 43216448
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Total de observaciones` = n())# A tibble: 1 × 1
`Total de observaciones`
<int>
1 660152
pues:
43216448/660152[1] 65.46439
Podemos también obtener el máximo de edad con max (el mínimo sería con min)
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Edad promedio` = max(Edad - 4000))# A tibble: 1 × 1
`Edad promedio`
<dbl>
1 120
O bien la mediana o algún cuantil:
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Edad mediana` = median(Edad - 4000))# A tibble: 1 × 1
`Edad mediana`
<dbl>
1 69
#Cuantil 0.25 = primer cuartil
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Edad (25%)` = quantile(Edad - 4000, 0.25))# A tibble: 1 × 1
`Edad (25%)`
<dbl>
1 53
de hecho podemos aplicar el summarise de todos a la vez:
#Cuantil 0.25 = primer cuartil
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
summarise(`Edad promedio` = mean(Edad - 4000),
`Edad mediana` = median(Edad - 4000),
`Edad máxima` = max(Edad - 4000))# A tibble: 1 × 3
`Edad promedio` `Edad mediana` `Edad máxima`
<dbl> <dbl> <dbl>
1 65.5 69 120
6.5 Operaciones en grupos usando group_by
Supongamos que es de nuestro interés calcular la media de edad de la defunción pero no para todo el país sino por entidad. Una opción es ir filtrando 32 veces para cada entidad como sigue (por ejemplo en este caso obteniendo todo Baja California que es la entidad 02)
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
filter(Entidad == "02") %>%
summarise(`Edad promedio` = mean(Edad - 4000))# A tibble: 1 × 1
`Edad promedio`
<dbl>
1 59.7
Repetir este proceso 32 veces es bastante ineficiente. Para ello el group_by nos sirve pues el group_by indica sobre qué columna debe repetirse la operación. En nuestro caso sería la de Entidad y podría ponerse:
mortalidad_2017 %>%
filter(Edad >= 4000 & Edad != 4998) %>%
group_by(Entidad) %>%
summarise(`Edad promedio` = mean(Edad - 4000))# A tibble: 32 × 2
Entidad `Edad promedio`
<chr> <dbl>
1 01 65.1
2 02 59.7
3 03 58.3
4 04 66.1
5 05 66.0
6 06 62.0
7 07 62.9
8 08 62.0
9 09 66.9
10 10 66.4
# … with 22 more rows
Podemos, por ejemplo, también calcular el número total de registros por cada Entidad como sigue:
mortalidad_2017 %>%
group_by(Entidad) %>%
tally()# A tibble: 32 × 2
Entidad n
<chr> <int>
1 01 5986
2 02 20572
3 03 3949
4 04 4477
5 05 16137
6 06 4871
7 07 26530
8 08 23468
9 09 75310
10 10 8490
# … with 22 more rows
Se puede agrupar por más de una columna separando por comas. Por ejemplo el siguiente conteo de hombres y mujeres por entidad
conteo_sexo_entidad <- mortalidad_2017 %>%
group_by(Entidad, Sexo) %>%
tally()# A tibble: 96 × 3
# Groups: Entidad [32]
Entidad Sexo n
<chr> <dbl> <int>
1 01 1 3315
2 01 2 2669
3 01 9 2
4 02 1 13135
5 02 2 7420
6 02 9 17
7 03 1 2617
8 03 2 1327
9 03 9 5
10 04 1 2526
# … with 86 more rows
Una vez hayamos terminado de operar por la variable agrupada y decidamos seguir usando la base es necesario desagrupar primero. Por ejemplo si queremos seguir ocupando la base conteo_sexo_entidad para ahora calcular el total de defunciones a nivel nacional usando una suma sum el programa no sabe que queremos dejar de agrupar:
conteo_sexo_entidad %>%
summarise(Todas = sum(n))# A tibble: 32 × 2
Entidad Todas
<chr> <int>
1 01 5986
2 02 20572
3 03 3949
4 04 4477
5 05 16137
6 06 4871
7 07 26530
8 08 23468
9 09 75310
10 10 8490
# … with 22 more rows
De hecho en la misma impresión de la base conteo_sexo_entidad puedes ver que dice # Groups: Entidad [32]. Esto porque se quedó agrupada. Para eliminar los grupos y poder trabajar con toda la base basta con usar ungroup:
conteo_sexo_entidad %>%
ungroup() %>%
summarise(Todas = sum(n))# A tibble: 1 × 1
Todas
<int>
1 688468
6.6 Cambio de columnas usando mutate
La función de mutate sirve para cambiar columnas en función de otras columnas o bien generar columnas nuevas. Por ejemplo podemos generar una nueva columna que se llame Dos y que consista sólo del número 2:
mortalidad_2017 %>%
mutate(`Dos` = 2)# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa Dos
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <dbl>
1 01 001 2 4061 7 1 2017 C259 2
2 01 001 1 4069 18 1 2017 K631 2
3 01 001 1 4091 20 1 2017 J441 2
4 01 001 2 4076 1 1 2017 I698 2
5 01 001 1 4070 20 1 2017 C61X 2
6 01 001 2 4048 20 1 2017 K803 2
7 01 001 1 4062 12 1 2017 C61X 2
8 01 001 1 4089 13 1 2017 N390 2
9 01 001 2 4042 8 1 2017 E117 2
10 01 001 2 4085 14 1 2017 I120 2
# … with 688,458 more rows
También podemos generar una columna que asigne el número de fila:
mortalidad_2017 %>%
mutate(`Fila` = 1:n())# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa Fila
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <int>
1 01 001 2 4061 7 1 2017 C259 1
2 01 001 1 4069 18 1 2017 K631 2
3 01 001 1 4091 20 1 2017 J441 3
4 01 001 2 4076 1 1 2017 I698 4
5 01 001 1 4070 20 1 2017 C61X 5
6 01 001 2 4048 20 1 2017 K803 6
7 01 001 1 4062 12 1 2017 C61X 7
8 01 001 1 4089 13 1 2017 N390 8
9 01 001 2 4042 8 1 2017 E117 9
10 01 001 2 4085 14 1 2017 I120 10
# … with 688,458 more rows
Por último podemos generar una columna que a la edad le reste 4000:
mortalidad_2017 %>%
mutate(`Edad corregida` = `Edad` - 4000)# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa `Edad corregida`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <dbl>
1 01 001 2 4061 7 1 2017 C259 61
2 01 001 1 4069 18 1 2017 K631 69
3 01 001 1 4091 20 1 2017 J441 91
4 01 001 2 4076 1 1 2017 I698 76
5 01 001 1 4070 20 1 2017 C61X 70
6 01 001 2 4048 20 1 2017 K803 48
7 01 001 1 4062 12 1 2017 C61X 62
8 01 001 1 4089 13 1 2017 N390 89
9 01 001 2 4042 8 1 2017 E117 42
10 01 001 2 4085 14 1 2017 I120 85
# … with 688,458 more rows
Cambio de columnas de manera condicional usando if_else y case_when dentro de mutate
El if_else y el case_when sirven para realizar cambios en columnas (mutate) en función de condiciones de la columna. La redacción del if_else es como sigue:
if_else(CONDICIÓN, VALOR SI VERDADERO, VALOR SI FALSO)por ejemplo podemos construir una columna que se llame Sexo categoría y que diga Hombre, Mujer en los casos de 1 y 2 respectivamente:
mortalidad_2017 %>%
filter(Sexo %in% c(1,2)) %>% #eliminamos los desconocidos
mutate(`Sexo categoría` = if_else(Sexo == 1, "Hombre", "Mujer"))# A tibble: 688,104 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa `Sexo categoría`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 01 001 2 4061 7 1 2017 C259 Mujer
2 01 001 1 4069 18 1 2017 K631 Hombre
3 01 001 1 4091 20 1 2017 J441 Hombre
4 01 001 2 4076 1 1 2017 I698 Mujer
5 01 001 1 4070 20 1 2017 C61X Hombre
6 01 001 2 4048 20 1 2017 K803 Mujer
7 01 001 1 4062 12 1 2017 C61X Hombre
8 01 001 1 4089 13 1 2017 N390 Hombre
9 01 001 2 4042 8 1 2017 E117 Mujer
10 01 001 2 4085 14 1 2017 I120 Mujer
# … with 688,094 more rows
O bien podemos crear una columna de edad nueva que sea 0 si la edad es menor que 4000 y Edad - 4000 en otro caso. Esto porque en la codificación del INEGI los números menores a 4000 se usan para codificar horas, días y meses.
mortalidad_2017 %>%
mutate(`Edad corregida` = if_else(Edad >= 4000, Edad - 4000, 0))# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa `Edad corregida`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <dbl>
1 01 001 2 4061 7 1 2017 C259 61
2 01 001 1 4069 18 1 2017 K631 69
3 01 001 1 4091 20 1 2017 J441 91
4 01 001 2 4076 1 1 2017 I698 76
5 01 001 1 4070 20 1 2017 C61X 70
6 01 001 2 4048 20 1 2017 K803 48
7 01 001 1 4062 12 1 2017 C61X 62
8 01 001 1 4089 13 1 2017 N390 89
9 01 001 2 4042 8 1 2017 E117 42
10 01 001 2 4085 14 1 2017 I120 85
# … with 688,458 more rows
También podemos, por ejemplo, obtener una columna que diga Tuberculosis si la causa de defunción fue tuberculosis (códigos A15-A19) o bien Otra en caso contrario:
mortalidad_2017 %>%
mutate(TB = if_else(str_detect(Causa,"A15") | str_detect(Causa,"A16") |
str_detect(Causa,"A17") | str_detect(Causa,"A18") |
str_detect(Causa,"A19"), "Tuberculosis", "Otra"))# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa TB
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 01 001 2 4061 7 1 2017 C259 Otra
2 01 001 1 4069 18 1 2017 K631 Otra
3 01 001 1 4091 20 1 2017 J441 Otra
4 01 001 2 4076 1 1 2017 I698 Otra
5 01 001 1 4070 20 1 2017 C61X Otra
6 01 001 2 4048 20 1 2017 K803 Otra
7 01 001 1 4062 12 1 2017 C61X Otra
8 01 001 1 4089 13 1 2017 N390 Otra
9 01 001 2 4042 8 1 2017 E117 Otra
10 01 001 2 4085 14 1 2017 I120 Otra
# … with 688,458 more rows
Además del if_else (que sirve para dos condiciones) existe el case_when que sirve para múltiples. La redacción siempre es:
case_when(
CONDICION1 ~ VALOR1,
CONDICION2 ~ VALOR2,
CONDICION3 ~ VALOR3,
TRUE ~ VALOR_DEFAULT #Este se puede omitir y el default es NA
)Por ejemplo, la vez anterior eliminamos aquellos que tenían sexo faltante. Podemos mejor dejarlos como missing: NA
mortalidad_2017 %>%
mutate(`Sexo corregido` = case_when(
`Sexo` == 1 ~ "Hombre",
`Sexo` == 2 ~ "Mujer",
TRUE ~ NA_character_
))# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa `Sexo corregido`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 01 001 2 4061 7 1 2017 C259 Mujer
2 01 001 1 4069 18 1 2017 K631 Hombre
3 01 001 1 4091 20 1 2017 J441 Hombre
4 01 001 2 4076 1 1 2017 I698 Mujer
5 01 001 1 4070 20 1 2017 C61X Hombre
6 01 001 2 4048 20 1 2017 K803 Mujer
7 01 001 1 4062 12 1 2017 C61X Hombre
8 01 001 1 4089 13 1 2017 N390 Hombre
9 01 001 2 4042 8 1 2017 E117 Mujer
10 01 001 2 4085 14 1 2017 I120 Mujer
# … with 688,458 more rows
Nota que pusimos NA_character_ para indicar el faltante. Esto es porque existen diferentes tipos de valores faltantes según la variable que tenemos: NA_real_ para números, NA_integer_ para números enteros y NA_character_ para palabras/caracteres.
Como nota no hace falta poner la última condición para que R sepa que aquellos que no son "Hombre" o "Mujer" son faltantes:
#Es lo mismo
mortalidad_2017 %>%
mutate(`Sexo corregido` = case_when(
`Sexo` == 1 ~ "Hombre",
`Sexo` == 2 ~ "Mujer",
))# A tibble: 688,468 × 9
Entidad Municipio Sexo Edad Día Mes Año Causa `Sexo corregido`
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
1 01 001 2 4061 7 1 2017 C259 Mujer
2 01 001 1 4069 18 1 2017 K631 Hombre
3 01 001 1 4091 20 1 2017 J441 Hombre
4 01 001 2 4076 1 1 2017 I698 Mujer
5 01 001 1 4070 20 1 2017 C61X Hombre
6 01 001 2 4048 20 1 2017 K803 Mujer
7 01 001 1 4062 12 1 2017 C61X Hombre
8 01 001 1 4089 13 1 2017 N390 Hombre
9 01 001 2 4042 8 1 2017 E117 Mujer
10 01 001 2 4085 14 1 2017 I120 Mujer
# … with 688,458 more rows
En un mutate podemos sobreescribir nombres de variables (es decir asignarle a Sexo un nuevo valor de Sexo). Esto sólo se sobreescribe en R y no la original.
#Es lo mismo
mortalidad_2017 %>%
mutate(`Sexo` = case_when(
`Sexo` == 1 ~ "Hombre",
`Sexo` == 2 ~ "Mujer",
))# A tibble: 688,468 × 8
Entidad Municipio Sexo Edad Día Mes Año Causa
<chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr>
1 01 001 Mujer 4061 7 1 2017 C259
2 01 001 Hombre 4069 18 1 2017 K631
3 01 001 Hombre 4091 20 1 2017 J441
4 01 001 Mujer 4076 1 1 2017 I698
5 01 001 Hombre 4070 20 1 2017 C61X
6 01 001 Mujer 4048 20 1 2017 K803
7 01 001 Hombre 4062 12 1 2017 C61X
8 01 001 Hombre 4089 13 1 2017 N390
9 01 001 Mujer 4042 8 1 2017 E117
10 01 001 Mujer 4085 14 1 2017 I120
# … with 688,458 more rows
Finalmente podemos combinar operaciones que ya hemos visto antes como tally y group_by por poner un ejemplo:
#Es lo mismo
mortalidad_2017 <- mortalidad_2017 %>%
mutate(`Sexo` = case_when(
`Sexo` == 1 ~ "Hombre",
`Sexo` == 2 ~ "Mujer",
))
#Tabla de sexo
mortalidad_2017 %>%
group_by(`Sexo`) %>%
tally()# A tibble: 3 × 2
Sexo n
<chr> <int>
1 Hombre 386125
2 Mujer 301979
3 <NA> 364
Un ejemplo más complicado de case_when consiste en asignar el tipo de tuberculosis a cada causa:
mortalidad_2017 %>%
mutate(`Tipo tuberculosis` = case_when(
str_detect(`Causa`, "A15") ~ "Tuberculosis respiratoria",
str_detect(`Causa`, "A17") ~ "Tuberculosis del sistema nervioso",
str_detect(`Causa`, "A18") ~ "Tuberculosis de otros órganos",
str_detect(`Causa`, "A19") ~ "Tuberculosis miliar",
TRUE ~ "Sin tuberculosis" #Valor default
)) %>%
filter(`Tipo tuberculosis` != "Sin tuberculosis") %>%
group_by(`Tipo tuberculosis`) %>%
tally()# A tibble: 4 × 2
`Tipo tuberculosis` n
<chr> <int>
1 Tuberculosis de otros órganos 113
2 Tuberculosis del sistema nervioso 89
3 Tuberculosis miliar 116
4 Tuberculosis respiratoria 516
6.7 Unión de otras bases usando _join
_join. Fuente: William Surles (https://rpubs.com/williamsurles/293454)Para unir los nombres de las entidades o de los municipios podríamos hacer un case_when gigante pero eso es poco eficiente dado que el INEGI nos provee de una base de datos (decateml.csv). Leámosla
codigos_entidad <- read_csv("decateml.csv",
locale = locale(encoding = "WINDOWS-1252"))Nota que las entidades son todas aquellas que en cve_mun y cve_loc tienen ceros. Primero hagamos una base de entidades quedándonos sólo con cve_ent y nom_loc:
base_entidades <- codigos_entidad %>%
filter(`cve_loc` == "0000") %>%
filter(`cve_mun` == "000") %>%
select(`cve_ent`, `nom_loc`) %>%
rename(`Entidad federativa` = `nom_loc`)# A tibble: 37 × 2
cve_ent `Entidad federativa`
<chr> <chr>
1 01 Aguascalientes
2 02 Baja California
3 03 Baja California Sur
4 04 Campeche
5 05 Coahuila de Zaragoza
6 06 Colima
7 07 Chiapas
8 08 Chihuahua
9 09 Ciudad de México
10 10 Durango
# … with 27 more rows
Nota que en la base_entidades la columna con la clave de la entidad se llama cve_ent mientras que en la base de mortalidad_2017 se llama Entidad. Para juntar las bases usaremos un left_join. Esta instrucción lo que hace es tomar una base grande y pegarle el diccionario de datos de una base pequeña como sigue:
grande %>% left_join(pequeña)En nuestro caso la base grande es mortalidad_2017 y la base pequeña es base_entidades y las variables en común son: Entidad = cve_ent.
mortalidad_2017 <- mortalidad_2017 %>%
left_join(base_entidades, by = c("Entidad" = "cve_ent"))En el caso del municipio requerimos dos columnas cve_ent (Entidad) y cve_mun (Municipio). Para ello filtramos la base inicial del INEGI:
base_municipios <- codigos_entidad %>%
filter(`cve_loc` == "0000") %>%
filter(`cve_mun` != "000") %>%
select(`cve_ent`, `cve_mun`, `nom_loc`) %>%
rename(`Nombre del municipio` = `nom_loc`)y pegamos a nuestros datos:
mortalidad_2017 <- mortalidad_2017 %>%
left_join(base_municipios,
by = c("Entidad" = "cve_ent",
"Municipio" = "cve_mun"))Podemos entonces mostrar las defunciones para los municipios de Sinaloa:
mortalidad_2017 %>%
filter(`Entidad federativa` == "Sinaloa") %>%
group_by(`Nombre del municipio`) %>%
tally()# A tibble: 18 × 2
`Nombre del municipio` n
<chr> <int>
1 Ahome 2509
2 Angostura 434
3 Badiraguato 105
4 Choix 128
5 Concordia 149
6 Cosalá 63
7 Culiacán 5007
8 El Fuerte 332
9 Elota 130
10 Escuinapa 292
11 Guasave 1407
12 Mazatlán 2943
13 Mocorito 214
14 Navolato 549
15 Rosario 249
16 Salvador Alvarado 354
17 San Ignacio 95
18 Sinaloa 322
6.8 Pivoteo de bases con pivot
La siguiente tabla muestra por sexo y entidad las defunciones. Muchas veces para mejorar el formato buscamos transponer la tabla de tal forma que hombres sea una columna y mujeres otra. Éste no es el formato recomendado para análisis pero a veces sí para presentar. Para poder transformar la tabla podemos pivot_wider:
#Tabla en formato largo (long)
formato_largo <- mortalidad_2017 %>%
group_by(`Entidad federativa`, Sexo) %>%
tally()formato_largo# A tibble: 96 × 3
# Groups: Entidad federativa [32]
`Entidad federativa` Sexo n
<chr> <chr> <int>
1 Aguascalientes Hombre 3315
2 Aguascalientes Mujer 2669
3 Aguascalientes <NA> 2
4 Baja California Hombre 13135
5 Baja California Mujer 7420
6 Baja California <NA> 17
7 Baja California Sur Hombre 2617
8 Baja California Sur Mujer 1327
9 Baja California Sur <NA> 5
10 Campeche Hombre 2526
# … with 86 more rows
Podemos decirle a R que vuelva cada sexo una columna. Para ello hay que especificar las variables que quedan constantes como columnas en id_cols (en este caso Entidad federativa) y las que van a cambiar a columna (names_from) Sexo junto con el valor asignar (values_from) n:
formato_ancho <- formato_largo %>%
pivot_wider(id_cols = `Entidad federativa`,
names_from = `Sexo`,
values_from = `n`)# A tibble: 32 × 4
# Groups: Entidad federativa [32]
`Entidad federativa` Hombre Mujer `NA`
<chr> <int> <int> <int>
1 Aguascalientes 3315 2669 2
2 Baja California 13135 7420 17
3 Baja California Sur 2617 1327 5
4 Campeche 2526 1945 6
5 Chiapas 14474 12051 5
6 Chihuahua 13845 9613 10
7 Ciudad de México 39204 36096 10
8 Coahuila de Zaragoza 8877 7256 4
9 Colima 3049 1820 2
10 Durango 4862 3627 1
# … with 22 more rows
por otro lado si tenemos una base en formato ancho wide y que queremos pasar a largo para su análisis (long) podemos trabajar con pivot_longer seleccionando en cols las columnas a transformar, bajo qué nombre quedan en names_to y bajo qué valor en values_to:
#Y de regreso
formato_ancho %>%
pivot_longer(cols = c(`Mujer`, `Hombre`, `NA`),
names_to = "Sexo",
values_to = "n")# A tibble: 96 × 3
# Groups: Entidad federativa [32]
`Entidad federativa` Sexo n
<chr> <chr> <int>
1 Aguascalientes Mujer 2669
2 Aguascalientes Hombre 3315
3 Aguascalientes NA 2
4 Baja California Mujer 7420
5 Baja California Hombre 13135
6 Baja California NA 17
7 Baja California Sur Mujer 1327
8 Baja California Sur Hombre 2617
9 Baja California Sur NA 5
10 Campeche Mujer 1945
# … with 86 more rows
6.9 Unión de columnas con paste
En nuestra base de datos tenemos el día, mes y año separados en las columnas homónimas. Podemos juntarlas en una sola con paste especificando cómo separarlas con sep:
mortalidad_2017 <- mortalidad_2017 %>%
mutate(Fecha = paste(Día, Mes, Año, sep = "-"))# A tibble: 688,468 × 11
Entidad Municipio Sexo Edad Día Mes Año Causa Entida…¹ Nombr…² Fecha
<chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr> <chr>
1 01 001 Mujer 4061 7 1 2017 C259 Aguasca… Aguasc… 7-1-…
2 01 001 Hombre 4069 18 1 2017 K631 Aguasca… Aguasc… 18-1…
3 01 001 Hombre 4091 20 1 2017 J441 Aguasca… Aguasc… 20-1…
4 01 001 Mujer 4076 1 1 2017 I698 Aguasca… Aguasc… 1-1-…
5 01 001 Hombre 4070 20 1 2017 C61X Aguasca… Aguasc… 20-1…
6 01 001 Mujer 4048 20 1 2017 K803 Aguasca… Aguasc… 20-1…
7 01 001 Hombre 4062 12 1 2017 C61X Aguasca… Aguasc… 12-1…
8 01 001 Hombre 4089 13 1 2017 N390 Aguasca… Aguasc… 13-1…
9 01 001 Mujer 4042 8 1 2017 E117 Aguasca… Aguasc… 8-1-…
10 01 001 Mujer 4085 14 1 2017 I120 Aguasca… Aguasc… 14-1…
# … with 688,458 more rows, and abbreviated variable names
# ¹`Entidad federativa`, ²`Nombre del municipio`
Una vez que están unidas podemos usar la librería lubridate para convertirlas a fecha usando dmy si es día-mes-año o ymd si es año mes día (mdy ¿para qué será?)
library(lubridate)
mortalidad_2017 <- mortalidad_2017 %>%
mutate(Fecha = dmy(Fecha))Una vez es convertida en fecha podemos hacer operaciones con fechas por ejemplo contar cuántos días han pasado desde el 01 de enero del 2017 hasta la fecha:
mortalidad_2017 <- mortalidad_2017 %>%
mutate(`Días desde primero de enero` = Fecha - dmy("01/01/2017"))o bien obtener la semana epidemiológica y el año epidemiológico:
mortalidad_2017 <- mortalidad_2017 %>%
mutate(`Semana epidemiológica` = epiweek(Fecha)) %>%
mutate(`Año epidemiológico` = epiyear(Fecha))6.10 Ejercicio
El ejercicio consiste en usar las siguientes funciones en una base de datos de tu preferencia al menos una vez para responder preguntas de la base de datos. +
filter+group_by+mutate+select+tally+rename+summarise+ Al menos una de las siguientes:left_join,right_join,full_join,inner_join+ Al menos una de las siguientes:pivot_longer,pivot_wider+ Una gráfica conggplotSi no tienes una base de datos puedes usar la que sigue:
En el siguiente hipervínculo puedes encontrar la base de datos de Enfermedades Febriles Exantemáticas más reciente de la Dirección General de Epidemiología (Base de Datos) así como el significado de cada uno de los códigos en Diccionario de Datos. Cada renglón de la base de datos representa un caso distinto de una de estas enfermedades.
Utiliza la base para responder las siguientes preguntas usando al menos una vez (en todo el ejercicio) las funciones:
filtergroup_bymutateselecttallyrenamesummarise- Al menos una de las siguientes:
left_join,right_join,full_join,inner_join - Al menos una de las siguientes:
pivot_longer,pivot_wider - Una gráfica con
ggplot
¿Cuántos casos se han presentado este año?
¿Cuántos presentaron fiebre?
¿Cuántos casos en hombres y cuántos en mujeres?
¿Cuál es la edad promedio de los casos?
De los casos ¿cuántos hombres estaban vacunados y cuántas mujeres vacunadas?. Genera una tabla de 2 por 2 que se vea exactamente así:
Hombres Mujeres Con al menos una vacuna --- --- Sin vacuna --- --- De los casos ¿qué proporción de los hombres estaban vacunados y qué proporción de las mujeres vacunadas?
Utiliza la población del
Censo 2020del INEGI para calcular la incidencia por cada 10000 habitantes por entidad y encontrar la entidad con mayor incidencia.Grafica la incidencia de casos totales por semana epidemiológica usando
ggplot2.
6.11 Para saber más
La mayor recomendación es el libro R4DS (R para ciencia de datos) el cual puede encontrarse gratuitamente en línea: nueva edición (inglés): https://r4ds.had.co.nz/ edición previa (español): https://es.r4ds.hadley.nz/