更安静的purrr :: map2用于名称乱序的列表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了更安静的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
或者仍然笨拙地,制作两个列表的列表,在另一个map
中对它们进行排序,然后将其输入pmap
,因为它需要一个列表列表:
list(evens, odds) %>%
map(~.[order(names(.))]) %>%
pmap(function(l1, l2) paste(l1, l2))
# same output as previous
理想情况下,我想将imap
选项的安全性与map2
的清洁度结合起来。
只需编写一个辅助函数来清理它
namemap <- function(.x, .y, .f, ...)
n <- order(unique(names(.x), names(.y)))
map2(.x[n], .y[n], .f, ...)
namemap(odds, evens, paste)
基本上purrr
中没有原语会自动为你做这件事。当这很容易做到时,似乎没有多大意义。
我们可以做的
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
中的list
元素并与map2
连接
i1 <- order(names(evens)) # not sure if this should be avoided
map2(evens[i1], odds[i1], str_c, sep=" ")
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"
transpose()
似乎正在这样做(按名称匹配)。虽然
它没有记录
(编辑:.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创建于2019-04-23(v0.2.1)
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创建于2019-04-23(v0.2.1)
注意:我没有检查是否存在关于此行为的Github问题,也不知道这是否有可能会发生变化,或者获得额外的参数以进行更精细的控制。
以上是关于更安静的purrr :: map2用于名称乱序的列表的主要内容,如果未能解决你的问题,请参考以下文章