Используйте ввод функции карты purrr, чтобы создать именованный список как вывод в R

Я использую функцию карты пакета purrr в R, которая выдает в качестве вывода список. Теперь я хотел бы, чтобы на выходе был именованный список на основе ввода. Пример приведен ниже.

input <- c("a", "b", "c")
output <- purrr::map(input, function(x) {paste0("test-", x)})

Отсюда я хотел бы получить доступ к элементам списка, используя:

output$a

Or

output$b

person Michael    schedule 12.05.2017    source источник
comment
По умолчанию это делается base::Map: output2 <- Map(function(x) {paste0("test-", x)}, input).   -  person andycraig    schedule 05.06.2018


Ответы (3)


Нам просто нужно назвать list

names(output) <- input

а затем извлеките элементы на основе имени

output$a
#[1] "test-a"

Если это необходимо сделать с помощью tidyverse

library(tidyverse)
output <- map(input, ~paste0('test-', .)) %>% 
                                setNames(input)
person akrun    schedule 12.05.2017
comment
К вашему сведению, purrr импортирует set_names() из rlang по этой причине. Не то чтобы между _4 _... - person Anders Swanson; 30.06.2018
comment
@AndersSwanson Есть существенные различия, и, в частности, вы можете сделать x %>% set_names() %>% map(fn), что является рекомендуемым решением этой проблемы. - person Lionel Henry; 11.10.2019

Обновить

Теперь, в 2020 году, форма ответа @mihagazvoda описывает правильный подход: просто set_names перед применением map

c("a", "b", "c") %>% 
    purrr::set_names() %>% 
    purrr::map(~paste0('test-', .))

Устаревший ответ

Принятое решение работает, но страдает от повторяющегося аргумента (input), который может вызывать ошибки и прерывать поток при использовании конвейера с %>%.

Альтернативным решением было бы использование оператора %>% с большей мощностью.

1:5 %>% { set_names(map(., ~ .x + 3), .) } %>% print # ... or something else

Это берет аргумент из трубы, но все же лишен красоты. Альтернативой может быть небольшой вспомогательный метод, такой как

map_named = function(x, ...) map(x, ...) %>% set_names(x)

1:5 %>% map_named(~ .x + 1)

Это уже выглядит красивее и элегантнее. И было бы моим предпочтительным решением.

Наконец, мы могли бы даже перезаписать purrr::map в случае, если аргумент является символьным или целочисленным вектором, и в таком случае создать именованный список.

map = function(x, ...){
    if (is.integer(x) | is.character(x)) {
        purrr::map(x, ...) %>% set_names(x)
    }else {
        purrr::map(x, ...) 
    }
}

1 : 5 %>% map(~ .x + 1)

Однако оптимальным решением было бы, если purrr реализовал бы такое поведение из коробки.

person Holger Brandl    schedule 09.07.2019
comment
Это было бы круто! Разве вам не стоит предложить такой пул-реквест? Или у них есть веская причина не поступать таким образом? - person Dan Chaltiel; 11.07.2019
comment
Мне кажется, что purrr сопровождающим это не нравится, потому что это создает различную семантику в зависимости от ввода. Я предполагаю, что они предпочитают более явный подход, например, с параметром, но это также поддержит вариант использования. Кроме того, я полагаю, что в некоторых случаях это нарушит обратную совместимость. Но конечно, почему бы не спросить, см. github.com/tidyverse/purrr/issues/691 - person Holger Brandl; 11.07.2019
comment
Рекомендуемое решение (добавленное годом позже @mihagazvoda) показывает, что действительно существует мурлыкающий способ сделать это. - person vinnief; 12.10.2020
comment
Действительно. Я не хочу удалять ответ, а скорее проголосую за этот ответ на @mihagazvoda. Есть ли способ сделать это с помощью stackoverflow? - person Holger Brandl; 14.10.2020

Рекомендуемое решение:

c("a", "b", "c") %>% 
    purrr::set_names() %>% 
    purrr::map(~paste0('test-', .))
person mihagazvoda    schedule 11.09.2020
comment
Блестяще. Имя задаем заранее. Это также работает с map_dfc и позволяет избежать предупреждений о переименовании для повторяющихся имен, которые я получаю при установке имени после карты. - person vinnief; 12.10.2020