1

How can you access the name of a list element within a function if you pass not the whole list but only the list element (dataframe)?

I have a named list of dataframes, e.g.

files <- list(BRX = -0.72, BRY = -0.72, BRZ = -0.156, BTX = -0.002, BTY = -0.002, 
    BTZ = -0.0034)

Later in the code, I will use a single list element as input for a plot function. This plot function shall also print the list element's name. How can I access it?

I have the following solution - it works but is a bit cumbersome:

map2(files, names(files),
     function(file, filename) {
       data.table::setattr(file, "filename", filename)
     })

Later, I can retrieve the filename as attribute within the plot function by:

plotfunction(list_element, ...) {
...    
filename <- attr(input, "filename")
...
+ ggtitle(filename)
...
}

Is there a more elegant alternative solution, either by a different way to access the list element name, or by setting the filename attribute differently?

Agile Bean
  • 6,437
  • 1
  • 45
  • 53

1 Answers1

0

One straightforward approach might be to pass the data to the plot function plot_fun as a list (using single brackets [), instead of as a list element (using double brackets [[). In this way, the list element's name will directly be available inside the plot function:

## dummy list of datasets
data_ls <- list(`Dataset 1` = data.frame(x = 1:10, y = 1:10), `Dataset 2` = data.frame(x = 1:10, y = 2 * (1:10)))

## dummy plot function
plot_fun <- function(data_el, ...) {
  plot(data_el[[1]], ...) + 
      title(names(data_el))
}

plot_fun(data_ls["Dataset 1"], type = "l")

plot_fun(data_ls[2], type = "l")


Edit: to call plot_fun for each list element in data_ls, we could modify plot_fun to accept a data and name argument, and then call lapply, Map, mapply or purrr's walk2 or map2 (walk2 is preferable, since plot_fun is called for its side-effects).

## modified dummy plot function
plot_fun <- function(data, name, ...) {
  plot(data, ...) + 
      title(name)
}

## using lapply
lapply(seq_along(data_ls), function(i) plot_fun(data_ls[[i]], names(data_ls)[i], type = "l"))

## or with Map
Map(plot_fun, data = data_ls, name = names(data_ls), type = "l")

## or with purrr
purrr::walk2(.x = data_ls, .y = names(data_ls), .f = plot_fun, type = "l")
Joris C.
  • 5,721
  • 3
  • 12
  • 27
  • very smart - never thought about this! Unfortunately, I sometimes also pass the list to map by `data_ls %>% map(plot_it(...))` in addition to the single list element pass. But your idea is definitely a creative approach! – Agile Bean Sep 22 '19 at 09:56
  • @AgileBean: in this case, it might make sense to use `Map`, `mapply`, `purrr::walk2` or `purrr::map2` (see the edited response) – Joris C. Sep 22 '19 at 10:37
  • Thanks @Joris Chau. I see - your `Map` or `walk2` work similarly to my `map2` solution in that they use the `names()` of the list, but instead of setting a filename attribute, call the plot function directly. So this could be combined with your previous idea if the plot function checks whether the input is a list with a single list element, cool. – Agile Bean Sep 22 '19 at 11:23
  • Is there any other way of remembering the filename in the list element other than setting an attribute? – Agile Bean Sep 22 '19 at 11:24
  • I don't think so, this is pretty much related to all questions similar to e.g. this one (how to access names inside `lapply`): https://stackoverflow.com/questions/9950144/access-lapply-index-names-inside-fun – Joris C. Sep 22 '19 at 11:31
  • I see, so that confirms I haven't overlooked any other method, good. – Agile Bean Sep 22 '19 at 11:38