Las columnas de las listas

Entender que una lista se puede usar como estructura de datos nos da mucho valor como analistas, dado que nos permite trabajar con dataframes anidados.

Las listas de columnas tienen la definicion implicita del propio dataframe. Al hacerlo implicito hacemos que la lista forme parte del propio data frame. Ejemplo:

data.frame( x = list(1:3, 4:6))
##   x.1.3 x.4.6
## 1     1     4
## 2     2     5
## 3     3     6

Como podemos ver, el objeto que ha salido no genera una columna, sino que genera dos. Si se quiere que se forme en una sola lista, se usara la función I():

data.frame(x = I(list(1:3, 4:6)),
           y = c("1,2,3", "3,4,5"))
##         x     y
## 1 1, 2, 3 1,2,3
## 2 4, 5, 6 3,4,5

Aqui podemos ver como hemos introducido la lista dentro de la propia variable x.

Si usamos tibble, podemos introducir directamente la lista en la variable sin usar la funcion I():

library(tidyverse)
## -- Attaching packages ------------------------------------------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.0.0     v purrr   0.2.5
## v tibble  1.4.2     v dplyr   0.7.6
## v tidyr   0.8.1     v stringr 1.3.1
## v readr   1.1.1     v forcats 0.3.0
## -- Conflicts ---------------------------------------------------------------------------- tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()
tibble(x = list(1:3, 4:6),
       y = c("1,2,3", "3,4,5"))
## # A tibble: 2 x 2
##   x         y    
##   <list>    <chr>
## 1 <int [3]> 1,2,3
## 2 <int [3]> 3,4,5

La tibble entiende directamente que se introduce una lista, igual que una tribble:

tribble(
  ~x,  ~y, 
  1:3, "1,2,3",
  4:6, "4,5,6"
)
## # A tibble: 2 x 2
##   x         y    
##   <list>    <chr>
## 1 <int [3]> 1,2,3
## 2 <int [3]> 4,5,6

Todo este trabajo nos permite guardar la informacion en un solo objeto, para que despues sea mas facil accedet a la informacion. Siempre habra tres partes para encontrar la efectividad de trabajar con un dataframe con una columna en formato de lista:

  1. Crear columna de listas con nest(), summarise() + list() para meter el resumen en una nueva variable

  2. mutate() + map() o map2() o pmap()

  3. Usar la funcion unnest() para volver a los valores iniciales

Podemos usar la funcion enframe() para meter la informacion dentro de un frame:

# tibble::enframe()

Generando columnas de listas

Podemos usar la funcion nest() para hacer agrupaciones dentro del dataset:

library(gapminder)
gapminder %>%
  group_by(country, continent) %>%
  nest() %>%
  head()
## # A tibble: 6 x 3
##   country     continent data             
##   <fct>       <fct>     <list>           
## 1 Afghanistan Asia      <tibble [12 x 4]>
## 2 Albania     Europe    <tibble [12 x 4]>
## 3 Algeria     Africa    <tibble [12 x 4]>
## 4 Angola      Africa    <tibble [12 x 4]>
## 5 Argentina   Americas  <tibble [12 x 4]>
## 6 Australia   Oceania   <tibble [12 x 4]>
# Que seria lo mismo que hacer:
gapminder %>%
  nest(year:gdpPercap) %>%
  head()
## # A tibble: 6 x 3
##   country     continent data             
##   <fct>       <fct>     <list>           
## 1 Afghanistan Asia      <tibble [12 x 4]>
## 2 Albania     Europe    <tibble [12 x 4]>
## 3 Algeria     Africa    <tibble [12 x 4]>
## 4 Angola      Africa    <tibble [12 x 4]>
## 5 Argentina   Americas  <tibble [12 x 4]>
## 6 Australia   Oceania   <tibble [12 x 4]>

Usando otras funciones vectoriales:

df <- tribble(
  ~x,
  "a,b,c",
  "f,g,h",
  "w,x,y,z"
)

Operamos con el dataframe creado:

df %>% mutate(y = str_split(x, ","))
## # A tibble: 3 x 2
##   x       y        
##   <chr>   <list>   
## 1 a,b,c   <chr [3]>
## 2 f,g,h   <chr [3]>
## 3 w,x,y,z <chr [4]>

Hemos generado una columna con los valores separados por comas dentro de esta. Vamos a hacer el unnest():

df %>% mutate(y = str_split(x, ",")) %>%
  unnest()
## # A tibble: 10 x 2
##    x       y    
##    <chr>   <chr>
##  1 a,b,c   a    
##  2 a,b,c   b    
##  3 a,b,c   c    
##  4 f,g,h   f    
##  5 f,g,h   g    
##  6 f,g,h   h    
##  7 w,x,y,z w    
##  8 w,x,y,z x    
##  9 w,x,y,z y    
## 10 w,x,y,z z

Como podemos ver se han desanidado para cada uno de los resultados.

Podemos usar otras funciones que automiaticamente generen listas de objetos.

sim <- tribble(
  ~f,     ~params, 
  "runif",list(min = -1, max = 1),
  "rnorm",list(sd = 3),
  "rpois",list(lambda = 5)
)

sim %>%
  mutate(sims = invoke_map(f, params, n = 10))
## # A tibble: 3 x 3
##   f     params     sims      
##   <chr> <list>     <list>    
## 1 runif <list [2]> <dbl [10]>
## 2 rnorm <list [1]> <dbl [10]>
## 3 rpois <list [1]> <int [10]>

Como aplicamos el summarise con una lista?

probs <- c(0.01, 0.25, 0.5, 0.75, 0.99)

mtcars %>%
  group_by(cyl) %>%
  summarise(
    p = list(probs),
    q = list(quantile(mpg, probs))) %>%
  unnest()
## # A tibble: 15 x 3
##      cyl     p     q
##    <dbl> <dbl> <dbl>
##  1     4  0.01  21.4
##  2     4  0.25  22.8
##  3     4  0.5   26  
##  4     4  0.75  30.4
##  5     4  0.99  33.8
##  6     6  0.01  17.8
##  7     6  0.25  18.6
##  8     6  0.5   19.7
##  9     6  0.75  21  
## 10     6  0.99  21.4
## 11     8  0.01  10.4
## 12     8  0.25  14.4
## 13     8  0.5   15.2
## 14     8  0.75  16.2
## 15     8  0.99  19.1

Que hacemos cuando queremos iterar acerca del contnido y de los elementos de una lista?

x <- list(
  a = 1:6,
  b = 3:4,
  c = 5:8
)
df <- enframe(x)
df
## # A tibble: 3 x 2
##   name  value    
##   <chr> <list>   
## 1 a     <int [6]>
## 2 b     <int [2]>
## 3 c     <int [4]>
df %>%
  mutate(
    smry = map2_chr(name, value, 
                    ~stringr::str_c(.x, ": ", .y[1]))
  )
## # A tibble: 3 x 3
##   name  value     smry 
##   <chr> <list>    <chr>
## 1 a     <int [6]> a: 1 
## 2 b     <int [2]> b: 3 
## 3 c     <int [4]> c: 5