首页 > 解决方案 > 在 R 中使用 lmap 将函数应用到列表时出错

问题描述

我有一个基于运行以下内容的数据框列表:

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

然后我尝试使用 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)

我得到这个错误:

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

如果我从列表中提取其中一个数据框并运行 count 函数,它工作正常,所以我知道我在使用 lmap 时做错了。由于 lmap 旨在将函数应用于列表并返回一个列表,我认为这就是我想在这里使用的。我不知道错误试图告诉我什么。对于我做错了什么以及如何解决它,我将不胜感激。完全可能有一个愚蠢的错误盯着我看,而我只是没有看到。

我知道我可以通过将第二个函数中的代码放入第一个函数中来解决这个问题,但我需要将它分开。而且我仍然想知道我做错了什么,所以我以后不会重蹈覆辙。

标签: rpurrr

解决方案


在代码中,OP 使用了 lambda 表达式,而不是在列表元素上应用函数。基于创建的功能,更适合map

a1 <- map(z, count)

或使用 lambda 表达式

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

关于lmap, 如文档中所述

map()、lmap_at() 和 lmap_if() 与 map()、map_at() 和 map_if() 类似,不同之处在于它们只对获取和返回列表(或数据帧)的函数进行操作。因此,它们不是映射列表的元素(如 .x[[i]]),而是将函数 .f 应用于该列表的每个大小为 1 的子集(如 .x[i])。我们称这些元素为列表元素)。

因此,子集仍然是一个list。因此,我们可能需要提取列表元素,[[因为函数“count”正在寻找 adata.frame作为输入而不是 alist

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

map请注意,输出 from和lmapas lmapreturn as output的结构有所不同list,它是list25 个向量的 a,而mapalist是 5 个 tibble 即如果我们检查 'a1' 的第一个列表元素的输出和前五个'a2' 的元素,同样,'a1' 的第二个元素和 'a2' 的下五个(6:10),...

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

推荐阅读