Programación Básica en R Studio
Gestión de Data Frames con el paquete paquete dplyr
Data Frames
Un data frame es una estructura de datos fundamental en estadística y
en el lenguaje R. Esta estructura se organiza de manera que
cada fila corresponde a una observación, mientras que cada columna
representa una variable, característica, medida o atributo de dicha
observación. En R, existe una implementación propia de los
data frames que es, probablemente, la más utilizada.
Librería dplyr
El paquete dplyr, creado por Hadley Wickham de RStudio, es una
versión optimizada y refinada de su paquete plyr. Una de sus principales
aportaciones es que introduce una “gramática” (específicamente, verbos)
para la manipulación de datos y el trabajo con data frames. Esta
gramática permite expresar de manera clara y comprensible las
operaciones que se realizan en un data frame, lo que resulta útil porque
brinda una abstracción para la manipulación de datos que antes no
existía. Además, otro aspecto destacado de dplyr es su alta
velocidad, ya que muchas de sus funciones clave están implementadas en
C++
Gramática dplyr
Algunos de los “verbos” clave proporcionados por el paquete
dplyrsonselect: devuelve un subconjunto de las columnas de un dataframe, utilizando una notación flexiblefilter: extraer un subconjunto de filas de un dataframe basándose en condiciones lógicasarrange: reordenar las filas de undataframerename: renombrar las variables de undataframemutate: añadir nuevas variables/columnas o transformar variables existentessummarise: generar estadísticas de resumen de diferentes variables en eldataframe, posiblemente dentro de los estratos%>%: el operador “pipe” se utiliza para conectar varias acciones verbales en unapipeline
Para ello, realicemos el proceso de instlación y llamado de la
librería dplyr. Recordemos la metáfora de
comprar la escoba y colocarnos a barrer. ¡Vayamos al R
Studio!
select()
Para los ejemplos de esta sección utilizaremos un dataset que contiene información sobre la contaminación del aire y la temperatura de la ciudad de Chicago (ver Air pollution dataset). Todos los datos usados en este curso los podrás descargar en: RDataSets o en su defecto en la carpeta del curso
- Después de descargar el archivo, puede cargar los datos en
Rutilizando la funciónreadRDS(). Esta función proporciona los medios para guardar y restaurar un único objetoRen una conexión. Esta difiere desaveyload, que guardan y restauran uno o más objetos con nombre en un entorno. Son ampliamente utilizadas por el propioR, por ejemplo para almacenar los metadatos de un paquete y para almacenar las bases de datos dehelp.search: la extensión de archivo".rds"es la más utilizada. ¡Vayamos al R Studio!
- Puede ver algunas características básicas del dataset con
las funciones
dim()ystr().
## [1] 6940 8
## 'data.frame': 6940 obs. of 8 variables:
## $ city : chr "chic" "chic" "chic" "chic" ...
## $ tmpd : num 31.5 33 33 29 32 40 34.5 29 26.5 32.5 ...
## $ dptp : num 31.5 29.9 27.4 28.6 28.9 ...
## $ date : Date, format: "1987-01-01" "1987-01-02" ...
## $ pm25tmean2: num NA NA NA NA NA NA NA NA NA NA ...
## $ pm10tmean2: num 34 NA 34.2 47 NA ...
## $ o3tmean2 : num 4.25 3.3 3.33 4.38 4.75 ...
## $ no2tmean2 : num 20 23.2 23.8 30.4 30.3 ...
La función
select()se utiliza para elegir las columnas específicas de un dataframe en las que desea enfocarse. Es común tener un dataframe grande que incluye todos los datos, pero en un análisis particular, es posible que solo se necesite un subconjunto de variables u observaciones. Conselect(), puede extraer fácilmente las columnas que son relevantes para su análisis, simplificando así el trabajo con conjuntos de datos grandes y centrándose en la información más pertinente.Supongamos que queremos tomar sólo las 3 primeras columnas. Hay varias formas de hacerlo. Podríamos, por ejemplo, utilizar índices numéricos. Pero también podemos utilizar los nombres directamente.
## [1] "city" "tmpd" "dptp"
## city tmpd dptp
## 1 chic 31.5 31.500
## 2 chic 33.0 29.875
## 3 chic 33.0 27.375
## 4 chic 29.0 28.625
## 5 chic 32.0 28.875
## 6 chic 40.0 35.125
## city tmpd dptp
## 1 chic 31.5 31.500
## 2 chic 33.0 29.875
## 3 chic 33.0 27.375
## 4 chic 29.0 28.625
## 5 chic 32.0 28.875
## 6 chic 40.0 35.125
- Tenga en cuenta que el signo : normalmente no se puede utilizar con nombres o cadenas, pero dentro de la función select() puede utilizarlo para especificar un rango de nombres de variables. También puede omitir variables con la función select() utilizando el signo negativo
## date pm25tmean2 pm10tmean2 o3tmean2 no2tmean2
## 1 1987-01-01 NA 34.00000 4.250000 19.98810
## 2 1987-01-02 NA NA 3.304348 23.19099
## 3 1987-01-03 NA 34.16667 3.333333 23.81548
## 4 1987-01-04 NA 47.00000 4.375000 30.43452
## 5 1987-01-05 NA NA 4.750000 30.33333
## 6 1987-01-06 NA 48.00000 5.833333 25.77233
- La función
select()también permite una sintaxis especial que permite especificar nombres de variables basados en patrones. Así, por ejemplo, si quisiéramos conservar todas las variables que terminan con un “2”, podríamos hacer
## 'data.frame': 6940 obs. of 4 variables:
## $ pm25tmean2: num NA NA NA NA NA NA NA NA NA NA ...
## $ pm10tmean2: num 34 NA 34.2 47 NA ...
## $ o3tmean2 : num 4.25 3.3 3.33 4.38 4.75 ...
## $ no2tmean2 : num 20 23.2 23.8 30.4 30.3 ...
- O si quisiéramos mantener todas las variables que empiezan por “d”, podríamos hacer
## 'data.frame': 6940 obs. of 2 variables:
## $ dptp: num 31.5 29.9 27.4 28.6 28.9 ...
## $ date: Date, format: "1987-01-01" "1987-01-02" ...
filter()
- La función filter() se utiliza para extraer
subconjuntos de filas de un dataframe. Esta función es similar a la
función subset() existente en R, pero
es bastante más rápida. Supongamos que queremos extraer las filas del
dataframe
PM2.5(la materia particulada 2.5, incluye sustancias químicas orgánicas, polvo, hollín y metales.) que sean superiores a 30 (que es un nivel razonablemente alto), podríamos hacer
## city tmpd dptp date pm25tmean2 pm10tmean2 o3tmean2 no2tmean2
## 1 chic 23 21.9 1998-01-17 38.10 32.46154 3.180556 25.30000
## 2 chic 28 25.8 1998-01-23 33.95 38.69231 1.750000 29.37630
## 3 chic 55 51.3 1998-04-30 39.40 34.00000 10.786232 25.31310
## 4 chic 59 53.7 1998-05-01 35.40 28.50000 14.295125 31.42905
## 5 chic 57 52.0 1998-05-02 33.30 35.00000 20.662879 26.79861
- Podemos colocar una secuencia lógica arbitrariamente
compleja dentro de filter(), por lo que podríamos,
por ejemplo, extraer las filas en las que
PM2.5es mayor que 30 y la temperatura,tmpdes mayor que 80 grados Fahrenheit
## city tmpd dptp date pm25tmean2 pm10tmean2 o3tmean2 no2tmean2
## 1 chic 81 71.2 1998-08-23 39.6000 59.0 45.86364 14.32639
## 2 chic 81 70.4 1998-09-06 31.5000 50.5 50.66250 20.31250
## 3 chic 82 72.2 2001-07-20 32.3000 58.5 33.00380 33.67500
## 4 chic 84 72.9 2001-08-01 43.7000 81.5 45.17736 27.44239
## 5 chic 85 72.6 2001-08-08 38.8375 70.0 37.98047 27.62743
arrange()
La función arrange() se utiliza para reorganizar las
filas de un dataframe en función de una o más variables/columnas,
manteniendo el orden de las demás columnas correspondiente a ese
reordenamiento. En R, reordenar las filas de un dataframe puede ser una
tarea complicada, pero arrange() simplifica este proceso
considerablemente. Esta función permite ordenar los datos en orden
ascendente o descendente utilizando la función desc() en
combinación con arrange(). Por ejemplo, se puede ordenar un
dataframe por la columna de fechas para que la primera fila corresponda
a la observación más antigua y la última a la más reciente. Además,
arrange() es parte del paquete dplyr, lo que significa que
puede integrarse fácilmente en flujos de trabajo de manipulación de
datos más amplios, especialmente cuando se combina con otras funciones
de dplyr mediante el operador pipe %>%. Esto facilita la
creación de pipelines eficientes y legibles para el procesamiento de
datos.
- Las columnas también se pueden ordenar de forma descendente
utilizando el operador especial
desc()
## city tmpd dptp date pm25tmean2 pm10tmean2 o3tmean2 no2tmean2
## 1 chic 35 30.1 2005-12-31 15.00000 23.5 2.531250 13.25000
## 2 chic 36 31.0 2005-12-30 15.05714 19.2 3.034420 22.80556
## 3 chic 35 29.4 2005-12-29 7.45000 23.5 6.794837 19.97222
## 4 chic 37 34.5 2005-12-28 17.75000 27.5 3.260417 19.28563
## 5 chic 40 33.6 2005-12-27 23.56000 27.0 4.468750 23.50000
## 6 chic 35 29.6 2005-12-26 8.40000 8.5 14.041667 16.81944
rename()
Renombrar una variable en un dataframe en R es sorprendentemente difícil de hacer. La función
rename()está diseñada para facilitar este proceso. Aquí puedes ver los nombres de las cinco primeras variables del dataframechicago. en la consola ejecutemos lo siguientecolnames(chicago)y apreciemos los nombres que contiene el dfLa columna
dptpse supone que representa la temperatura del punto de rocío y la columna pm25tmean2 proporciona los datos PM2.5. Sin embargo, estos nombres son incómodos y probablemente deban ser renombrados a algo más sencillos.Dew point: temperatura más alta a la que empieza a condensarse el vapor de agua contenido en el aire, produciendo rocío, neblina, cualquier tipo de nube o, en caso de que la temperatura sea lo suficientemente baja, escarcha.
## city tmpd dewpoint date pm25
## 1 chic 35 30.1 2005-12-31 15.00000
## 2 chic 36 31.0 2005-12-30 15.05714
## 3 chic 35 29.4 2005-12-29 7.45000
Nota: La sintaxis dentro de la función rename() es
tener el nuevo nombre en el lado izquierdo del signo =
y el nombre antiguo en el lado derecho.
mutate()
La función
mutate()existe para calcular las transformaciones de las variables de un dataframe. A menudo, se desea crear nuevas variables derivadas de variables existentes ymutate()proporciona una interfaz limpia para hacerlo. Por ejemplo, con los datos de contaminación atmosférica, a menudo queremos eliminar la tendencia de los datos restando la media de los mismosDe este modo, podemos ver si el nivel de contaminación atmosférica de un día determinado es superior o inferior a la media (en lugar de observar su nivel absoluto). Aquí creamos una variable
pm25detrendque resta la media de la variablepm25.
## city tmpd dewpoint date pm25 pm10tmean2 o3tmean2 no2tmean2
## 1 chic 35 30.1 2005-12-31 15.00000 23.5 2.531250 13.25000
## 2 chic 36 31.0 2005-12-30 15.05714 19.2 3.034420 22.80556
## 3 chic 35 29.4 2005-12-29 7.45000 23.5 6.794837 19.97222
## 4 chic 37 34.5 2005-12-28 17.75000 27.5 3.260417 19.28563
## 5 chic 40 33.6 2005-12-27 23.56000 27.0 4.468750 23.50000
## 6 chic 35 29.6 2005-12-26 8.40000 8.5 14.041667 16.81944
## pm25detrend
## 1 -1.230958
## 2 -1.173815
## 3 -8.780958
## 4 1.519042
## 5 7.329042
## 6 -7.830958
- También existe la función relacionada llamada
transmute(), que hace lo mismo quemutate()pero luego elimina todas las variables no transformadas. En este caso, se remueve la tendencia de las variables PM10 y ozono (O3)
head(transmute(chicago,
pm10detrend = pm10tmean2 - mean(pm10tmean2, na.rm = TRUE),
o3detrend = o3tmean2 - mean(o3tmean2, na.rm = TRUE)))## pm10detrend o3detrend
## 1 -10.395206 -16.904263
## 2 -14.695206 -16.401093
## 3 -10.395206 -12.640676
## 4 -6.395206 -16.175096
## 5 -6.895206 -14.966763
## 6 -25.395206 -5.393846
group_by()
La función
group_by()se utiliza para generar estadísticas de resumen del dataframe dentro de los estatus definidos por una variable. Por ejemplo, en este dataset de contaminación atmosférica, podría querer saber cuál es el nivel medio anual de PM2,5. Entonces el estatus es el año, y eso es algo que podemos derivar de la variable fecha. Junto con la funcióngroup_by()se suele utilizar la funciónsummarize()(o summarise()` para algunas partes del mundo).La operación general aquí es una combinación de dividir un dataframe en piezas separadas definidas por una variable o grupo de variables (
group_by()), y luego aplicar una función de resumen en esos subconjuntos (summarize()). En primer lugar, podemos crear una variable de año utilizandoas.POSIXlt(). Las opciones para días y meses respectivamente, están disponibles usando$mdayymon+1(ver as.POSIXlt). Los valores anuales se almacenan utilizando un valor de índice base de 1900. Así,2015se almacena como 115 ($year= 115).
## city tmpd dewpoint date pm25 pm10tmean2 o3tmean2 no2tmean2
## 1 chic 35 30.1 2005-12-31 15.00000 23.5 2.531250 13.25000
## 2 chic 36 31.0 2005-12-30 15.05714 19.2 3.034420 22.80556
## 3 chic 35 29.4 2005-12-29 7.45000 23.5 6.794837 19.97222
## 4 chic 37 34.5 2005-12-28 17.75000 27.5 3.260417 19.28563
## 5 chic 40 33.6 2005-12-27 23.56000 27.0 4.468750 23.50000
## 6 chic 35 29.6 2005-12-26 8.40000 8.5 14.041667 16.81944
## pm25detrend year
## 1 -1.230958 2005
## 2 -1.173815 2005
## 3 -8.780958 2005
## 4 1.519042 2005
## 5 7.329042 2005
## 6 -7.830958 2005
Ahora podemos crear un dataframe separado que divida el dataset original por año
## # A tibble: 6,940 × 10
## # Groups: year [19]
## city tmpd dewpoint date pm25 pm10tmean2 o3tmean2 no2tmean2
## <chr> <dbl> <dbl> <date> <dbl> <dbl> <dbl> <dbl>
## 1 chic 35 30.1 2005-12-31 15 23.5 2.53 13.2
## 2 chic 36 31 2005-12-30 15.1 19.2 3.03 22.8
## 3 chic 35 29.4 2005-12-29 7.45 23.5 6.79 20.0
## 4 chic 37 34.5 2005-12-28 17.8 27.5 3.26 19.3
## 5 chic 40 33.6 2005-12-27 23.6 27 4.47 23.5
## 6 chic 35 29.6 2005-12-26 8.4 8.5 14.0 16.8
## 7 chic 35 32.1 2005-12-25 6.7 8 14.4 13.8
## 8 chic 37 35.2 2005-12-24 30.8 25.2 1.77 32.0
## 9 chic 41 32.6 2005-12-23 32.9 34.5 6.91 29.1
## 10 chic 22 23.3 2005-12-22 36.6 42.5 5.39 33.7
## # ℹ 6,930 more rows
## # ℹ 2 more variables: pm25detrend <dbl>, year <dbl>
- Por último, calculamos los estadísticos de resumen para cada año
del dataframe con la función
summarize(). La funciónsummarize()devuelve undataframeconyearcomo primera columna, y luego los promedios anuales de pm25, o3 y no2.
summarize(years, pm25 = mean(pm25, na.rm = TRUE),
o3 = max(o3tmean2, na.rm = TRUE),
no2 = median(no2tmean2, na.rm = TRUE))## # A tibble: 19 × 4
## year pm25 o3 no2
## <dbl> <dbl> <dbl> <dbl>
## 1 1987 NaN 63.0 23.5
## 2 1988 NaN 61.7 24.5
## 3 1989 NaN 59.7 26.1
## 4 1990 NaN 52.2 22.6
## 5 1991 NaN 63.1 21.4
## 6 1992 NaN 50.8 24.8
## 7 1993 NaN 44.3 25.8
## 8 1994 NaN 52.2 28.5
## 9 1995 NaN 66.6 27.3
## 10 1996 NaN 58.4 26.4
## 11 1997 NaN 56.5 25.5
## 12 1998 18.3 50.7 24.6
## 13 1999 18.5 57.5 24.7
## 14 2000 16.9 55.8 23.5
## 15 2001 16.9 51.8 25.1
## 16 2002 15.3 54.9 22.7
## 17 2003 15.2 56.2 24.6
## 18 2004 14.6 44.5 23.4
## 19 2005 16.2 58.8 22.6
pipe %>%
- Antes de explicar este operador, tengamos presente que una forma
sencilla de sacar la pipe conjugando las siguientes teclas
(ctrl + shift + m ). El operadorpipeline%>% es muy útil para encadenar múltiples funcionesdplyren una secuencia de operaciones. Nótese que hasta ahora, cada vez que se desea aplicar más de una función, la secuencia quedaba dentro de una secuencia de llamadas a funciones anidadas que es difícil de leer, por ejemplo
third(second(first(x)))
- Este anidamiento no es una forma natural de pensar en una secuencia de operaciones. El operador %>% permite encadenar las operaciones de izquierda a derecha, es decir
first(x) %>%
second %>%
third
Tomemos el ejemplo que acabamos de hacer en la última sección, en el que hemos calculado la media de o3 y no2 dentro de los quintiles de pm25. Allí tuvimos que
Crear una nueva variable pm25.quint
Agrupar el dataframe por esa nueva variable
Calcular la media de o3 y no2 en los subgrupos definidos por pm25.quint
Eso se puede hacer con la siguiente secuencia en una sola expresión de R
library(tidyr)
#variable categórica de pm25 dividida en quintiles
qq <- quantile(chicago$pm25, seq(0, 1, 0.20), na.rm = TRUE)
qq## 0% 20% 40% 60% 80% 100%
## 1.700 8.700 12.375 16.700 22.610 61.500
mutate(chicago, pm25.quint = cut(pm25, qq)) %>%
group_by(pm25.quint) %>%
summarize(o3 = mean(o3tmean2, na.rm = TRUE), no2 = mean(no2tmean2, na.rm = TRUE)) %>%
drop_na()## # A tibble: 5 × 3
## pm25.quint o3 no2
## <fct> <dbl> <dbl>
## 1 (1.7,8.7] 21.7 18.0
## 2 (8.7,12.4] 20.4 22.1
## 3 (12.4,16.7] 20.7 24.4
## 4 (16.7,22.6] 19.9 27.3
## 5 (22.6,61.5] 20.3 29.6
- De esta manera no tenemos que crear un conjunto de variables
temporales en el camino o crear una secuencia masiva anidada de
llamadas a funciones. Observe en el código anterior que el
dataframe
chicagopasa a la primera llamada amutate(), pero después no tiene que pasar el primer argumento agroup_by()o asummarize(). Una vez que se recorre el pipeline con %>%, el primer argumento se toma como la salida del elemento anterior en la pipeline.