Capítulo 6 Loops (purrr) y bibliografía (rticles)

6.1 Paquetes necesarios para este capítulo

Para este capítulo necesitas tener instalado el paquete tidyverse.

Probablemente uno de los puntos que marca la diferencia entre ser un usuario de un lenguaje de programación y un alguién que realmente programa. Es el momento en que una persona aprende a hacer loops. Los loops son una acción repetitiva en la cual una misma acción es realizada por el computador ahorrandonos mucho tiempo de escribir código y muchas veces tiempo de computación tambien.

Existen varias formas de como realizar loops en R, los for loops, la familia de los apply y más recientemente el uso del paquete purrr (Henry and Wickham 2018) presente en el tidyverse. En este capítulo nos enfocaremos principalmente en el uso de este paquete, pero también explicaremos levemente el caso de los for loops.

Dado que este libro es un apoyo para el curso BIO4022, esta clase puede también ser seguida en este link. El video de la clase se encontrará disponible en este link.

6.2 Generando una receta

Como hacer un loop, es una repetición de un código multiples veces, generalmente lo que más nos combiene es generar la receta tomando en cuenta el primer elemento y luego repetirlo en un loop.

6.2.1 Dioxido de nitrógeno en Madrid

Supongamos que queremos estudiar la concentración de dióxido de Nitrógeno en madrid en distintas estaciones, la base de datos puede ser encontrada en el siguiente link. Dentro de esta base de datos tenemos una carpeta con la calidad de aire de estaciones en Madrid, con un archivo para cada año. Supongamos que se quiere hacer lo siguiente, limitandose a las estaciones de Cuatro Caminos, El Pardo, Escuelas Aguirre, Moratalaz y Tres Olivos, calcular los promedios de \(NO_2\) para cada mes y cada año en estas estaciones.

6.2.1.1 Generando la receta

Esto lo podemos hacer con un loop, pero antes generemos la receta tomando en cuenta solo el 2017.

Para esto hacemos lo siguiente:

  • Tomemos la base de datos de calidad de aire de Madrid
  • Leeamos el año 2017
  • Limitemonos a las estaciones de Cuatro Caminos, El Pardo, Escuelas Aguirre, Moratalaz y Tres Olivos
  • Agreguemos una columna con el año y una con el mes
  • Calculemos los promedios de \(NO_2\) para cada mes
  • Eliminemos las columnas innecesarias para estudiar el efecto del \(NO_2\) en Madrid

Vamos paso a paso

6.2.1.1.1 leyendo la base de datos

El primer paso es leer la base de datos, para esto usamos el tidyverse y cargamos además lubridate por si tenemos que trabajar con las fechas. En la tabla ?? vemos los resultados del código a continuación.

6.2.1.1.2 Limitemonos a las estaciones seleccionadas

Revisando el archivo stations.csv, podemos ver que el código de estaciones que estudiaremos son 28079036, 28079008,28079058, 28079060 y 28079038, por lo que lo ponemos en un filter. El resultado de esto lo podemos ver en la tabla ??

6.2.1.1.3 Agreguemos aparte el mes, el año y el nombre de la estación

Usando mutate y las funciones monthy year de lubridate podemos agregar el més y el año para cada observación, además usando left_joint, podemos agreagar el nombre de las estaciones usando la base de datos stations.csv. El resultado de esto lo podemos ver en la tabla ??

Finalmente, agrupamos sacamos el promedio por mes y sacamos las columnas sobrantes al mismo tiempo, como vemos en la tabla ??

## Parsed with column specification:
## cols(
##   id = col_integer(),
##   name = col_character(),
##   address = col_character(),
##   lon = col_double(),
##   lat = col_double(),
##   elevation = col_integer()
## )
## Parsed with column specification:
## cols(
##   date = col_datetime(format = ""),
##   BEN = col_double(),
##   CH4 = col_character(),
##   CO = col_double(),
##   EBE = col_double(),
##   NMHC = col_double(),
##   NO = col_double(),
##   NO_2 = col_double(),
##   NOx = col_character(),
##   O_3 = col_double(),
##   PM10 = col_double(),
##   PM25 = col_double(),
##   SO_2 = col_double(),
##   TCH = col_double(),
##   TOL = col_double(),
##   station = col_integer()
## )
## Joining, by = "station"
6.2.1.1.4 Últimos detalles

Vemos que hay algunos valores del 2018, esto parece raro, ya que leimos los archivos del 2017. Al revisar mas con summarize, vemos que en realidad son tan solo unas pocas observaciones las que generan esta anomalía debido a algunas medidas del 1 de enero del 2018.

Para eliminarlas agregamos el siguiente código.

## Parsed with column specification:
## cols(
##   id = col_integer(),
##   name = col_character(),
##   address = col_character(),
##   lon = col_double(),
##   lat = col_double(),
##   elevation = col_integer()
## )
## Parsed with column specification:
## cols(
##   date = col_datetime(format = ""),
##   BEN = col_double(),
##   CH4 = col_character(),
##   CO = col_double(),
##   EBE = col_double(),
##   NMHC = col_double(),
##   NO = col_double(),
##   NO_2 = col_double(),
##   NOx = col_character(),
##   O_3 = col_double(),
##   PM10 = col_double(),
##   PM25 = col_double(),
##   SO_2 = col_double(),
##   TCH = col_double(),
##   TOL = col_double(),
##   station = col_integer()
## )
## Joining, by = "station"

Esto nos dá al fin la receta final que usaremos en el loop.

6.3 Empezando el loop

En este capíulo usaremos principalmente la función map del paquete purrr para generar loops, en esta función los dos argumentos generales que necesitamos es un vector o lista (argumento .x) de los elementos que pasarán por una función, y una funcion (argumento .f) que se aplicará a toda esta lista. Es importante establecer que el resultado de map siempre será una lista.

6.3.1 Volvamos a nuestra receta

Veamos el código que usamos para el año 2017

La primera parte del código es la lectura del archivo

Para hacer esto por todos los archivos de la base de datos requeriríamos de una lista o vector con los nombres de cada uno de los archivos. ¡Si solo hubiera una función en R que nos permitiera leer los archivos de una carpeta! La función list.files hace eso.

Entonces el código que vemos abajo genera un vector con todos los nombres de los archivos que queremos incorporar:

##  [1] "csvs_per_year/madrid_2001.csv" "csvs_per_year/madrid_2002.csv"
##  [3] "csvs_per_year/madrid_2003.csv" "csvs_per_year/madrid_2004.csv"
##  [5] "csvs_per_year/madrid_2005.csv" "csvs_per_year/madrid_2006.csv"
##  [7] "csvs_per_year/madrid_2007.csv" "csvs_per_year/madrid_2008.csv"
##  [9] "csvs_per_year/madrid_2009.csv" "csvs_per_year/madrid_2010.csv"
## [11] "csvs_per_year/madrid_2011.csv" "csvs_per_year/madrid_2012.csv"
## [13] "csvs_per_year/madrid_2013.csv" "csvs_per_year/madrid_2014.csv"
## [15] "csvs_per_year/madrid_2015.csv" "csvs_per_year/madrid_2016.csv"
## [17] "csvs_per_year/madrid_2017.csv" "csvs_per_year/madrid_2018.csv"

Entonces poner dentro de map, un vector con el nombre de los archivos (Archivos), y una función para leer los archivos (read_csv). Esto es el siguiente código

Genera una lista donde cada elemento es un data frame de un año de mediciones.

Cuando se agregan otras funciones mas complejas en un loop usando map. Como por ejemplo filter, ponemos un simbolo ~ dentro de map, y un .x dentro de filter para representar a cada dataframe que usaremos.

De esta forma podemos seguir la receta creada anteriormente sin ningún problema.

Pero en este momento tenemos una lista con 17 data frames, en vez de un gran data frame con todos los datos. Para esto debenos unir esta lista usando la función reduce, lo cual nos genera el siguiente código y la tabla ??

Referencias

Henry, Lionel, and Hadley Wickham. 2018. Purrr: Functional Programming Tools. https://CRAN.R-project.org/package=purrr.