Abigail
Abigail

Reputation: 470

Error when applying function to list using lmap in R

I have a list of data frames based on running the following:

library(tidyverse)

candidates <- c("A1", "A2", "A3", "B1", "B2")

votes <- function(aa, ab, ba, bb, can) {
  aprob <- c(aa, aa, aa, ab, ab)
  bprob <- c(ba, ba, ba, bb, bb)
  avotes <- t(replicate(25, (sample(can, replace = FALSE, prob = aprob))))
  bvotes <- t(replicate(20, (sample(can, replace = FALSE, prob = bprob))))
  avotes <- as_tibble(avotes)
  bvotes <- as_tibble(bvotes)
  all_votes <- bind_rows(avotes, bvotes)
}

z <- map(1:5, ~ votes(aa = .6, ab = .4, ba = .4, bb = .6, candidates))

I am then trying to apply a function to each data frame in the list using lmap:

count <- function(x) {
  all_votes <- x %>% 
    mutate(rn = row_number()) %>%
    pivot_longer(cols = contains("V")) %>% 
    mutate(name = str_remove_all(name, "V")) %>% 
    pivot_wider(names_from = value, values_from = name) %>% 
    select(-rn) %>% 
    mutate_all(as.integer)
}

a <- lmap(z, ~ count)

And I get this error:

Error in lmap_at(.x, seq_along(.x), .f, ...) : is.list(res) is not TRUE

If I pluck one of the data frames from the list and run the count function it works fine, so I know I'm doing something wrong with lmap. Since lmap is meant to apply a function to a list and return a list I assumed that was what I wanted to use here. I have no idea what the error is trying to tell me. I’d appreciate some help on what I’m doing wrong and how to fix it. Entirely possible there is an stupid mistake staring at me and I'm just not seeing it.

I know I could solve this by putting the code from the second function into the first function but I need to separate it out. And I still want to know what I’m doing wrong so I don’t repeat it in the future.

Upvotes: 1

Views: 281

Answers (1)

akrun
akrun

Reputation: 887541

In the code, OP's used lambda expression, and not applying the function on the list element. Based on the function created, it is more suitable for map

a1 <- map(z, count)

or with lambda expression

a1 <- map(z, ~ count(.x))

Regarding the lmap, as specified in the documentation

map(), lmap_at() and lmap_if() are similar to map(), map_at() and map_if(), with the difference that they operate exclusively on functions that take and return a list (or data frame). Thus, instead of mapping the elements of a list (as in .x[[i]]), they apply a function .f to each subset of size 1 of that list (as in .x[i]). We call those elements list-elements).

Thus, subset is still a list. So, we may need to extract the list element with [[ as the function 'count' is looking for a data.frame as input and not a list

a2 <- lmap(z, ~ count(.x[[1]]))

Note that there is a difference in the structure of output from map and lmap as lmap returns list as output, it is a list of 25 vectors, while map is a list of 5 tibble i.e. if we check the output of the first list element of 'a1' and the first five elements of 'a2', it is the same, similarly second element of 'a1' and next five (6:10) of 'a2',...

all.equal(unclass(a1[[1]]), a2[1:5], check.attributes = FALSE)
#[1] TRUE

Upvotes: 3

Related Questions