更安全的purrr :: map2用于名称混乱的列表

时间:2019-04-22 17:41:11

标签: r purrr

这是我之前在代码中编写过故障保护功能的一个问题,但是我想知道是否有更简单的事情我错过了。

有时,我有2个(或更多)包含不同类型信息的列表,这些信息需要与map2之类的功能配合使用-想一个ggplot对象的命名列表和一个library(purrr) evens <- list(a = 2, b = 4, c = 6, d = 8) odds <- list(a = 11, d = 9, c = 7, b = 5) 对象的命名列表用于保存每个文件输出的文件路径。有没有一种内置方法或可以轻松地将其添加到管道工作流程中的方法,以确保列表项按名称而不是按位置进行匹配?

考虑一个简单的例子:

map2

b返回名称与 first 列表相同的列表,并按位置进行迭代。因此无法解决doddsmap2(evens, odds, function(l1, l2) { paste(l1, l2) }) #> $a #> [1] "2 11" #> #> $b #> [1] "4 9" #> #> $c #> [1] "6 7" #> #> $d #> [1] "8 5" map2(odds, evens, function(l1, l2) { paste(l1, l2) }) #> $a #> [1] "11 2" #> #> $d #> [1] "9 4" #> #> $c #> [1] "7 6" #> #> $b #> [1] "5 8" 中切换的事实,并且这两个调用的结果不同:

imap

我过去所做的是改为使用imap(evens, function(l1, name) { paste(l1, odds[[name]]) }) #> $a #> [1] "2 11" #> #> $b #> [1] "4 5" #> #> $c #> [1] "6 7" #> #> $d #> [1] "8 9" 并使用第一个列表的名称来提取另一个列表中的适当项目,但这意味着我的函数中不再有第二个列表参数:

map2(
  evens[order(names(evens))],
  odds[order(names(odds))],
  function(l1, l2) paste(l1, l2)
)
# same output as previous

如果我想在两个列表上都能更均匀地工作,可以按名称对它们进行排序,但这感觉很笨拙:

map

或者更笨拙,列出两个列表,然后在另一个pmap中对它们进行排序,然后将其通过管道传送到list(evens, odds) %>% map(~.[order(names(.))]) %>% pmap(function(l1, l2) paste(l1, l2)) # same output as previous 中,因为它需要一个列表:

imap

理想情况下,我想将map2选项的安全性与RowStyle的整洁度结合起来。

5 个答案:

答案 0 :(得分:4)

我们可以做到

library(tidyverse)
map2(evens, odds[names(evens)], str_c, sep=' ')
#$a
#[1] "2 11"

#$b
#[1] "4 5"

#$c
#[1] "6 7"

#$d
#[1] "8 9"

如果两个list名称都是无序的,请遍历sort之一的names ed list,提取元素并进行连接

map(sort(names(evens)), ~ str_c(evens[[.x]], odds[[.x]], sep= ' '))

或为order创建标识符,然后在order中将list的{​​{1}}元素并与list串联

map2

答案 1 :(得分:4)

bind_rows与名称匹配,因此您可以先bind_rows然后再map(尽管这对列表中的内容施加了其他限制)

library(tidyverse)

bind_rows(evens, odds) %>% 
  map(paste, collapse = ' ')

# $`a`
# [1] "2 11"
# 
# $b
# [1] "4 5"
# 
# $c
# [1] "6 7"
# 
# $d
# [1] "8 9"

答案 2 :(得分:3)

只需编写一个帮助程序函数来清理它

namemap <- function(.x, .y, .f, ...) {
  n <- order(unique(names(.x), names(.y)))
  map2(.x[n], .y[n], .f, ...)
}
namemap(odds, evens, paste)

基本上,purrr中没有任何原始函数会自动为您执行此操作。而且,当这很容易做到时,似乎没有什么意义。

答案 3 :(得分:2)

transpose()似乎正在这样做(按名称匹配)。尽管没有记录 edit: .names arg的解释给出了上下文,并且有示例),但该记录在某些地方似乎不准确( purrr v。0.3.1)。

  

之所以称为转置,是因为x[[1]][[2]]等效于transpose(x)[[2]][[1]]

^似乎不准确,因为在这种情况下,list(evens, odds)[[2]][[4]]5,而transpose(list(evens, odds))[[4]][[2]]9

  

请注意,transpose()是其自身的逆,非常类似于矩阵上的转置操作。您可以通过移调两次来获取原始输入。

并不完全准确,但是我们可以利用它来发挥优势:

list(evens, odds) %>% 
  transpose() %>% 
  transpose()
#> [[1]]
#> [[1]]$a
#> [1] 2
#> 
#> [[1]]$b
#> [1] 4
#> 
#> [[1]]$c
#> [1] 6
#> 
#> [[1]]$d
#> [1] 8
#> 
#> 
#> [[2]]
#> [[2]]$a
#> [1] 11
#> 
#> [[2]]$b
#> [1] 5
#> 
#> [[2]]$c
#> [1] 7
#> 
#> [[2]]$d
#> [1] 9

reprex package(v0.2.1)于2019-04-23创建

OP的第一个示例(“想想一个ggplot对象的命名列表和一个文件路径的命名列表,用于保存每个对象的输出。” )如下所示:

  list(paths, plots) # or list(filename = paths, plot = plots) to match args of ggsave
  transpose() %>%
  walk(lift(ggsave))

OP的第二个示例可能是:

list(evens = evens, odds = odds) %>% # or tibble::lst(evens, odds) but lst() is in the questioning stage
  transpose() %>% 
  map(lift(paste)) # or map(paste, collapse = " ") 
#> $a
#> [1] "2 11"
#> 
#> $b
#> [1] "4 5"
#> 
#> $c
#> [1] "6 7"
#> 
#> $d
#> [1] "8 9"

reprex package(v0.2.1)于2019-04-23创建


注意:我尚未检查是否存在有关此行为的Github问题,也不知道是否有任何可能会改变的可能性,或者是否有其他理由要求更好的控制。 / p>

答案 4 :(得分:1)

如果列表名称只是部分重叠,则可以使用@MrFlick 答案的以下修改。应用的函数必须忽略 NULL 参数:

namedmap2 <- function(.x, .y, .f, ...) {
    set <- unique(c(names(.x), names(.y)))
    lst <- map2(.x[set], .y[set], .f, ...)
    names(lst) <- set
    lst
}