Cargar los paquetes dplyr y tidyr.
library(dplyr)
library(tidyr)
Las bases de datos que se van a utilizar ya vienen precargadas en RStudio: iris, mtcars i Indometh.
?iris
?mtcars
?Indometh
Notar que, en construir nuestro R Markdown, según el tamaño de la tabla que se produce hemos puesto el orden “results = ‘hide’” para aquel chunk. Lo que hace que no se vea el resultado en el documento, siendo por lo tanto necesario executar el chunk por separado, para ver el resultado.
tidyverse
select(mtcars, mpg, cyl, gear) # Selección por nombre
select(mtcars, 1,2,10) # Selección por índice de la columna
R base
Con R base se utiliza rl típico operador “[]” o la función subset() con el argumento select.
mtcars[,c("mpg","cyl","gear")] # Por nombre
mtcars[,c(1,2,10)] # Por índice
subset(mtcars, select = c(1,2, 10)) # Por índice
subset(mtcars, select = c("mpg","cyl","gear")) # Por nombre
tidyverse
En este caso se utiliza la función mutate().
mutate(mtcars, # Crea una variable que calcula el ratio entre mpg i hp.
ratio.mpg.hp = mpg/hp)
R base
En el R base podemos utilizar 2 maneras: la primera utiliza el operador de asignación “<-”, mientras la segunda usa la función transorm() que funciona de manera muy similar a mutate():
mtcars$ratio.mpg.hp <- mtcars$mpg/mtcars$hp
transform(iris, petal.ratio = Petal.Length/Petal.Width)
Selección de observaciones o líneas tidyverse
Con dplyr se utiliza filter().
filter(mtcars, cyl== 8)
R base
Con R base se utiliza el típico operador “[]” o la función subset():
mtcars[mtcars$cyl==8,]
subset(mtcars, cyl==4)
tidyverse
Con dplyr es la típica la función arrange()
arrange(iris, desc(Species), Sepal.Width)
R base
Con R base, pueden utilizarse diferentes variaciones sobre la función order() acompañada o no por rev() (para el inverso), también utilizando with() para evitar usar $ o [].
iris[order(rev(iris$Species), iris$Sepal.Length),]
iris[order(rev(iris[,1]), iris[,5]),]
iris[with(iris, order(rev(Species), Sepal.Length)),]
tidyverse
La función clásica es summarise(). También funciona summarise_at para muchas variables; y también summarise() combinada con across(), una nueva función introducida en dplyr en la actualización de junio 2020.
# Manera clásica
mtcars %>%
summarise(mpg.mean =mean(mpg),
mpg.median=median(mpg),
hp.mean=mean(hp),
hp.median=median(hp))
## mpg.mean mpg.median hp.mean hp.median
## 1 20.09062 19.2 146.6875 123
# Maneras abreviadas si las variables y las funciones a sumarizar son muchas
mtcars %>% # Con summarise_at()
summarise_at (.vars = c("mpg", "hp"),
.funs = c("mean", "median"))
## mpg_mean hp_mean mpg_median hp_median
## 1 20.09062 146.6875 19.2 123
mtcars %>% # Con summarise() y across()
summarise(across(.cols= c(mpg, hp),
.fns= list(Mean= mean, Median= median)))
## mpg_Mean mpg_Median hp_Mean hp_Median
## 1 20.09062 19.2 146.6875 123
R base
Dos de las maneras posibles son: 1) crear un data frame con data.frame(), 2) usar la función aggregate().
# Creando manualmente un data frame
data.frame(mpg.mean = mean(mtcars$mpg),
mpg.median= median(mtcars$mpg),
hp.mean= mean(mtcars$hp),
hp.median= median(mtcars$hp))
## mpg.mean mpg.median hp.mean hp.median
## 1 20.09062 19.2 146.6875 123
aggregate(formula = cbind(mpg, hp) ~ 1, data = mtcars,FUN = function(x){c(mean = mean(x), median = median(x))})
## mpg.mean mpg.median hp.mean hp.median
## 1 20.09062 19.20000 146.6875 123.0000
Considerar estos 3 micro data frames
band_members
## # A tibble: 3 x 2
## name band
## <chr> <chr>
## 1 Mick Stones
## 2 John Beatles
## 3 Paul Beatles
band_instruments
## # A tibble: 3 x 2
## name plays
## <chr> <chr>
## 1 John guitar
## 2 Paul bass
## 3 Keith guitar
band_instruments2
## # A tibble: 3 x 2
## artist plays
## <chr> <chr>
## 1 John guitar
## 2 Paul bass
## 3 Keith guitar
Combinando con tidyverse
# Mantener los nombres que combinan
inner_join(band_members, band_instruments, by = "name")
# Mantener todas la líneas:
full_join(band_members, band_instruments, by = "name")
# Mantener todas la líneas de la primer tabla:
left_join(band_members, band_instruments, by = "name")
# Mantener todas la líneas de la segunda tabla:
right_join(band_members, band_instruments, by = "name")
# Las columnas a combinar pueden tener nombres diferentes:
inner_join(band_members, band_instruments2, by = c("name" = "artist"))
Combinando con R base
# Mantener los nombres que combinan
merge(band_members, band_instruments, by = "name")
## name band plays
## 1 John Beatles guitar
## 2 Paul Beatles bass
# Mantener todas la líneas:
merge(band_members, band_instruments, by = "name", all = TRUE)
## name band plays
## 1 John Beatles guitar
## 2 Keith <NA> guitar
## 3 Mick Stones <NA>
## 4 Paul Beatles bass
# Mantener todas la líneas de la primer tabla:
merge(band_members, band_instruments, by = "name", all.x = TRUE)
## name band plays
## 1 John Beatles guitar
## 2 Mick Stones <NA>
## 3 Paul Beatles bass
# Mantener todas la líneas de la segunda tabla:
merge(band_members, band_instruments, by = "name", all.y = TRUE)
## name band plays
## 1 John Beatles guitar
## 2 Keith <NA> guitar
## 3 Paul Beatles bass
# Las columnas a combinar pueden tener nombres diferentes:
merge(band_members, band_instruments2, by.x = "name", by.y = "artist")
## name band plays
## 1 John Beatles guitar
## 2 Paul Beatles bass
Agrupando con dplyr
La función más típica de dplyr es group_by()
mtcars %>%
group_by(carb, gear) %>%
summarise(mpg.mean = mean(mpg),
mpg.median = median(mpg),
hp.mean = mean(hp),
hp.median = median(hp)) %>%
arrange(gear)%>%
ungroup() # elimina del análisis que sigue
## `summarise()` regrouping output by 'carb' (override with `.groups` argument)
## # A tibble: 11 x 6
## carb gear mpg.mean mpg.median hp.mean hp.median
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 3 20.3 21.4 104 105
## 2 2 3 17.2 17.1 162. 162.
## 3 3 3 16.3 16.4 180 180
## 4 4 3 12.6 13.3 228 230
## 5 1 4 29.1 29.8 72.5 66
## 6 2 4 24.8 23.6 79.5 78.5
## 7 4 4 19.8 20.1 116. 116.
## 8 2 5 28.2 28.2 102 102
## 9 4 5 15.8 15.8 264 264
## 10 6 5 19.7 19.7 175 175
## 11 8 5 15 15 335 335
Agrupando con R base
Con R base existen diferentes maneras de hacer estos tipos de operación. Este es es conocido como solución “split-apply-combine”utilizando la función by().
# Primero trabajar en data.frame por grupo (split-apply)
mtcars_by <- by(mtcars,
INDICES = list(mtcars$carb, mtcars$gear),
FUN = function(x){
data.frame(carb = unique(x$carb),
gear = unique(x$gear),
mpg.mean = mean(x$mpg),
mpg.median = median(x$mpg),
hp.mean = mean(x$hp),
hp.median = median(x$hp))
})
# Combinar el resultado en un data frame
do.call(rbind, mtcars_by)
## carb gear mpg.mean mpg.median hp.mean hp.median
## 1 1 3 20.33333 21.40 104.0 105.0
## 2 2 3 17.15000 17.10 162.5 162.5
## 3 3 3 16.30000 16.40 180.0 180.0
## 4 4 3 12.62000 13.30 228.0 230.0
## 5 1 4 29.10000 29.85 72.5 66.0
## 6 2 4 24.75000 23.60 79.5 78.5
## 7 4 4 19.75000 20.10 116.5 116.5
## 8 2 5 28.20000 28.20 102.0 102.0
## 9 4 5 15.80000 15.80 264.0 264.0
## 10 6 5 19.70000 19.70 175.0 175.0
## 11 8 5 15.00000 15.00 335.0 335.0
Pero también se puede utilizar, de manera más práctica la función aggregate() que hemos visto antes.
aggregate(formula = cbind(mpg, hp) ~ carb + gear,
data = mtcars,
FUN = function(x){
c(mean = mean(x), median = median(x))})
## carb gear mpg.mean mpg.median hp.mean hp.median
## 1 1 3 20.33333 21.40000 104.0 105.0
## 2 2 3 17.15000 17.10000 162.5 162.5
## 3 3 3 16.30000 16.40000 180.0 180.0
## 4 4 3 12.62000 13.30000 228.0 230.0
## 5 1 4 29.10000 29.85000 72.5 66.0
## 6 2 4 24.75000 23.60000 79.5 78.5
## 7 4 4 19.75000 20.10000 116.5 116.5
## 8 2 5 28.20000 28.20000 102.0 102.0
## 9 4 5 15.80000 15.80000 264.0 264.0
## 10 6 5 19.70000 19.70000 175.0 175.0
## 11 8 5 15.00000 15.00000 335.0 335.0
En este caso la ventaja de trabajar con dplyr es bastante clara por la intelegibilidad del código. En cualquier caso la función aggregate() puede ser útil.
Por ejemplo creando una columna “centered” que cuantifica la diferencia de un misuración del promedio calculado por el grupo al que pertenece la observación.
Creación con tidyverse
iris %>%
group_by(Species) %>%
mutate(Sepal.Length.centered = Sepal.Length - mean(Sepal.Length)) %>%
ungroup()
Creación de columnas con R base
En este caso ave() sirve para preparar esos datos para la asignación <- de nuevo en el data.frame principal, como con mutate(). De hecho la sintaxis de esta solución no dista mucho de la de diplyr, mutate().
iris$Sepal.Length.centered <- ave(iris$Sepal.Length,
iris$Species,
FUN = function(x) x - mean(x))
La segunda alternativa utiliza by() separando un vector que después se incorpora en el data frame original.
# Primero trabajar con by()
mtcars_by2 <-with(mtcars,by(mtcars,
cyl,
FUN = function(y){y$mpg.diff <- y$mpg - mean(y$mpg)
return(y)}))
# Después combinar el resultado en un data frame
do.call(rbind, mtcars_by2)
Por ejemplo, estableciendo los máximos por cada grupos. En el caso de mtcars, sería, por ejemplo, filtrar la máxima hp, según cyl, el números de cilindros.
tidyverse
En este caso usando group_by() y filter().
mtcars %>%
group_by(cyl) %>%
filter(hp == max(hp))
## # A tibble: 3 x 12
## # Groups: cyl [3]
## mpg cyl disp hp drat wt qsec vs am gear carb ratio.mpg.hp
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 30.4 4 95.1 113 3.77 1.51 16.9 1 1 5 2 0.269
## 2 19.7 6 145 175 3.62 2.77 15.5 0 1 5 6 0.113
## 3 15 8 301 335 3.54 3.57 14.6 0 1 5 8 0.0448
R base
En este caso, también se utiliza la función by()
max_hp <- with(mtcars, by(mtcars,
cyl,
FUN = function(xm){xm[xm$hp== max(xm$hp),]}))
do.call(rbind,max_hp)
## mpg cyl disp hp drat wt qsec vs am gear carb ratio.mpg.hp
## 4 30.4 4 95.1 113 3.77 1.513 16.9 1 1 5 2 0.26902655
## 6 19.7 6 145.0 175 3.62 2.770 15.5 0 1 5 6 0.11257143
## 8 15.0 8 301.0 335 3.54 3.570 14.6 0 1 5 8 0.04477612
Modelo alargado con tidyverse
Tidyverse tiene disponibles dos funciones para ello. La función pivot_longer substituirà paulatinamente gather(), por ser más intuitiva.
# Método antiguo
gather(iris, key = "trait", value = "measurement", Sepal.Length:Petal.Width)
# Método nuevo con la función pivot_longer()
pivot_longer(iris, cols = Sepal.Length:Petal.Width, names_to = "trait", values_to = "measurement")
Modelo alargado con R base
Con R base se pude trabajar a ello con reshape(). Si la variables a meter en la tabla son muchas es incómoda.
reshape(iris,
varying = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"),
timevar = "trait",
idvar = "id",
v.names = "measurement",
direction = "long")
Modelo ancho con tidyverse
Tidyverse tiene disponibles dos funciones para ello. Análogamente al caso anterior, la función pivot_wider substituirà paulatinamente spread(), por ser más intuitiva.
# 1
spread(Indometh, key = "time", value = "conc")
## Subject 0.25 0.5 0.75 1 1.25 2 3 4 5 6 8
## 1 1 1.50 0.94 0.78 0.48 0.37 0.19 0.12 0.11 0.08 0.07 0.05
## 2 4 1.85 1.39 1.02 0.89 0.59 0.40 0.16 0.11 0.10 0.07 0.07
## 3 2 2.03 1.63 0.71 0.70 0.64 0.36 0.32 0.20 0.25 0.12 0.08
## 4 5 2.05 1.04 0.81 0.39 0.30 0.23 0.13 0.11 0.08 0.10 0.06
## 5 6 2.31 1.44 1.03 0.84 0.64 0.42 0.24 0.17 0.13 0.10 0.09
## 6 3 2.72 1.49 1.16 0.80 0.80 0.39 0.22 0.12 0.11 0.08 0.08
# 2
pivot_wider(Indometh, names_from = "time", values_from = "conc")
## # A tibble: 6 x 12
## Subject `0.25` `0.5` `0.75` `1` `1.25` `2` `3` `4` `5` `6` `8`
## <ord> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 1.5 0.94 0.78 0.48 0.37 0.19 0.12 0.11 0.08 0.07 0.05
## 2 2 2.03 1.63 0.71 0.7 0.64 0.36 0.32 0.2 0.25 0.12 0.08
## 3 3 2.72 1.49 1.16 0.8 0.8 0.39 0.22 0.12 0.11 0.08 0.08
## 4 4 1.85 1.39 1.02 0.89 0.59 0.4 0.16 0.11 0.1 0.07 0.07
## 5 5 2.05 1.04 0.81 0.39 0.3 0.23 0.13 0.11 0.08 0.1 0.06
## 6 6 2.31 1.44 1.03 0.84 0.64 0.42 0.24 0.17 0.13 0.1 0.09
# 3
Indometh %>%
mutate(time = paste("conc", time, sep = "_")) %>%
spread(key = "time", value = "conc")
## Subject conc_0.25 conc_0.5 conc_0.75 conc_1 conc_1.25 conc_2 conc_3 conc_4
## 1 1 1.50 0.94 0.78 0.48 0.37 0.19 0.12 0.11
## 2 4 1.85 1.39 1.02 0.89 0.59 0.40 0.16 0.11
## 3 2 2.03 1.63 0.71 0.70 0.64 0.36 0.32 0.20
## 4 5 2.05 1.04 0.81 0.39 0.30 0.23 0.13 0.11
## 5 6 2.31 1.44 1.03 0.84 0.64 0.42 0.24 0.17
## 6 3 2.72 1.49 1.16 0.80 0.80 0.39 0.22 0.12
## conc_5 conc_6 conc_8
## 1 0.08 0.07 0.05
## 2 0.10 0.07 0.07
## 3 0.25 0.12 0.08
## 4 0.08 0.10 0.06
## 5 0.13 0.10 0.09
## 6 0.11 0.08 0.08
Modelo ancho con R base
reshape(Indometh,
v.names = "conc",
idvar = "Subject",
timevar = "time",
direction = "wide")
## Subject conc.0.25 conc.0.5 conc.0.75 conc.1 conc.1.25 conc.2 conc.3 conc.4
## 1 1 1.50 0.94 0.78 0.48 0.37 0.19 0.12 0.11
## 12 2 2.03 1.63 0.71 0.70 0.64 0.36 0.32 0.20
## 23 3 2.72 1.49 1.16 0.80 0.80 0.39 0.22 0.12
## 34 4 1.85 1.39 1.02 0.89 0.59 0.40 0.16 0.11
## 45 5 2.05 1.04 0.81 0.39 0.30 0.23 0.13 0.11
## 56 6 2.31 1.44 1.03 0.84 0.64 0.42 0.24 0.17
## conc.5 conc.6 conc.8
## 1 0.08 0.07 0.05
## 12 0.25 0.12 0.08
## 23 0.11 0.08 0.08
## 34 0.10 0.07 0.07
## 45 0.08 0.10 0.06
## 56 0.13 0.10 0.09