Preparación

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.

La selección de variables

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

La creación de nuevas variables

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)

Ordenación de las tablas según una variable

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)),]

Sumarización de observaciones o líneas

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

Combinación de tablas

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

Operaciones de agrupación

Sumarizando líneas agrupadas

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.

Creación de nuevas columnas con cálculos agrupados

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)

Filtrado de de líneas segun condiciones evaluadas en los grupos

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

Remodelación de los datos

Modelo alargado (long)

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 (wide)

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