Jamie
Jamie

Reputation: 1965

How to use map_at properly in a nested list?

I have a nested list of lists that include data tables at the bottom level. My goal is to map a function to a single table to transform the data. I'm currently trying to use purrr:map_depth and purrr::map_at in conjunction to create the plot. The reason I need to use map_at or map_if is the plot function I'm using takes different arguments depending on the table.

example below

library(data.table)
library(purrr)

example = list(
  group1 = list(
    all = data.table(
      x = 1:10,
      y = 10:1),
    not_all = data.table(
      x2 = 11:20,
      y2 = 20:11)
    ),
  group2 = list(
    all = data.table(
      x = 1:10,
      y = 10:1),
    not_all = data.table(
      x2 = 11:20,
      y2 = 20:11)
  ),
  group3 = list(
    all = data.table(
      x = 1:10,
      y = 10:1),
    not_all = data.table(
      x2 = 11:20,
      y2 = 20:11)
  ),
  group4 = list(
    all = data.table(
      x = 1:10,
      y = 10:1),
    not_all = data.table(
      x2 = 11:20,
      y2 = 20:11)
  )
)

I'm planning to use highcharter::data_to_boxplot as the mapping function.

So far I've been unable to extract a single table to map to and don't have a solid grasp of the purrr syntax yet.

map_depth(example, 2, map_at(., "all", data_to_boxplot, variable = x))
# Error: character indexing requires a named object

map_depth(example, 2, ~map_at(., "all", data_to_boxplot, variable = x))
# this prints out the entire list

# would like to try something like this too but can't figure out the piping correctly
map_depth(example, 2) %>%
  map_at(., "all", data_to_boxplot, variable = x)

# Error in as_mapper(.f, ...) : argument ".f" is missing, with no default

Any help would be greatly appreciated!

Upvotes: 3

Views: 428

Answers (1)

Martin Gal
Martin Gal

Reputation: 16998

I think you need a nested map-map_at-construct:

library(data.table)
library(highcharter)
library(purrr)

example %>% 
  map(~.x %>% 
        map_at("all", data_to_boxplot, variable = x))

This returns

$group1
$group1$all
# A tibble: 1 x 4
  name  data       id    type   
  <lgl> <list>     <lgl> <chr>  
1 NA    <list [1]> NA    boxplot

$group1$not_all
    x2 y2
 1: 11 20
 2: 12 19
 3: 13 18
 4: 14 17
 5: 15 16
 6: 16 15
 7: 17 14
 8: 18 13
 9: 19 12
10: 20 11


$group2
$group2$all
# A tibble: 1 x 4
  name  data       id    type   
  <lgl> <list>     <lgl> <chr>  
1 NA    <list [1]> NA    boxplot


$group3
$group3$all
# A tibble: 1 x 4
  name  data       id    type   
  <lgl> <list>     <lgl> <chr>  
1 NA    <list [1]> NA    boxplot


$group4
$group4$all
# A tibble: 1 x 4
  name  data       id    type   
  <lgl> <list>     <lgl> <chr>  
1 NA    <list [1]> NA    boxplot

How does this work?

  • example is a list of lists. map applies a function to each element of this list. These elements are also lists.
  • The custom function used in map is just another map-function, which is applied to an object named all (here at "level 2").

This is equivalent to

example %>% 
  map_depth(1,
            ~.x %>% 
              map_at("all", data_to_boxplot, variable = x),
            )

Take a look at ?map_depth:

map_depth(.x, .depth, .f, ..., .ragged = FALSE)

with .depth defined as "Level of .x to map on. Use a negative value to count up from the lowest level of the list."

  • map_depth(x, 0, fun) is equivalent to fun(x).
  • map_depth(x, 1, fun) is equivalent to x <- map(x, fun).
  • map_depth(x, 2, fun) is equivalent to x <- map(x, ~ map(., fun)).

Upvotes: 4

Related Questions