首页 > 解决方案 > 对于名称乱序的列表更安全的 purrr::map2

问题描述

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

有时我有 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所以项目和d被切换的事实odds没有得到解决,这两个调用产生了不同的结果:

map2(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

或者更笨重,制作两个列表的列表,将它们分别排列在 another 中map,然后通过管道将其输入,pmap因为它需要一个列表列表:

list(evens, odds) %>%
  map(~.[order(names(.))]) %>%
  pmap(function(l1, l2) paste(l1, l2))
# same output as previous

理想情况下,我想将imap选项的安全性与map2.

标签: rpurrr

解决方案


我们能做的

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名称都是无序的,则遍历其中之一的sorted ,提取两个元素并连接nameslist

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

或者为 , 然后创建一个标识符order,然后将 和 中orderlist元素listmap2

i1 <- order(names(evens)) # not sure if this should be avoided
map2(evens[i1], odds[i1], str_c, sep=" ")

推荐阅读