Я нашел этот вопрос, но, похоже, он мне не подходит.
Задача проста. У меня есть список, подобный следующему, полученный из фрейма данных, и я хочу setNames
для подэлементов:
mydf <- data.frame(Patient=rep(paste0('Pat', 1:3), 2), Cycle=rep(paste0('C', 1:2), each=3))
mylist <- sapply(mydf, unique)
Список выглядит так:
> mylist
$Patient
[1] "Pat1" "Pat2" "Pat3"
$Cycle
[1] "C1" "C2"
Единственное, что я хочу сделать, это поставить setNames
для подэлементов, чтобы они включали имя элемента, в результате чего получился окончательный список:
> mylist
$Patient
Patient-Pat1 Patient-Pat2 Patient-Pat3
"Pat1" "Pat2" "Pat3"
$Cycle
Cycle-C1 Cycle-C2
"C1" "C2"
Теперь я мог бы пройтись по элементам один за другим, вот так:
mylist[[1]] <- setNames(mylist[[1]], paste(names(mylist)[1], mylist[[1]], sep='-'))
mylist[[2]] <- setNames(mylist[[2]], paste(names(mylist)[2], mylist[[2]], sep='-'))
...
Но я этого не хочу, так как список может иметь переменное количество элементов и подэлементов с разными именами (это ушло бы в функцию, принимающую разные исходные фреймы данных).
Какой способ лучше всего это сделать, желательно с помощью функций apply
?
Моя лучшая попытка:
sapply(mylist, setNames, paste(names(mylist), mylist, sep='-'))
lapply()
Самое простое — перебирать имена списков. Мы можем передать именованный вектор в lapply()
, чтобы в выходном списке были нужные имена.
lapply(
setNames(names(mylist), names(mylist)),
\(nm, l = mylist) setNames(l[[nm]], sprintf("%s-%s", nm, l[[nm]]))
)
# $Patient
# Patient-Pat1 Patient-Pat2 Patient-Pat3
# "Pat1" "Pat2" "Pat3"
# $Cycle
# Cycle-C1 Cycle-C2
# "C1" "C2"
Вам нужен другой подход, если ваш список может иметь более одного вложенного уровня, например:
mylist$nested <- list(
list("nest1", "nest2")
)
Здесь вы можете использовать rrapply::rrapply(), который имеет аргумент .xparents
, который оценивается как имя родителя текущего элемента списка.
Я перенесу приведенное ниже изображение в json, потому что оно печатается лучше:
rrapply::rrapply(
mylist,
condition = Negate(is.list), # apply only to leafs (and not nodes)
f = \(x, .xparents) setNames(x, sprintf("%s-%s", .xparents[1], x))
) |>
rjson::toJSON()
Выход:
{
"Patient": {
"Patient-Pat1": "Pat1",
"Patient-Pat2": "Pat2",
"Patient-Pat3": "Pat3"
},
"Cycle": {
"Cycle-C1": "C1",
"Cycle-C2": "C2"
},
"nested": [
[
{
"nested-nest1": "nest1"
},
{
"nested-nest2": "nest2"
}
]
]
}
sapply(names(mylist), \(x) setNames(mylist[[x]], paste(x, mylist[[x]], sep = "-")), USE.NAMES = TRUE)
#> $Patient
#> Patient-Pat1 Patient-Pat2 Patient-Pat3
#> "Pat1" "Pat2" "Pat3"
#>
#> $Cycle
#> Cycle-C1 Cycle-C2
#> "C1" "C2"
Мы можем использовать Map
.
> Map(setNames, mylist, Map(paste, names(mylist), mylist, sep='-'))
$Patient
Patient-Pat1 Patient-Pat2 Patient-Pat3
"Pat1" "Pat2" "Pat3"
$Cycle
Cycle-C1 Cycle-C2
"C1" "C2"
Еще один с purrr
и map2
.
.
Отличия:
-- Нет set_name
-- Нет map(map(..))
или []
new_list <- map2(
mylist, names(mylist),
\(x, y) purrr::set_names(x, \(xx) paste0(y, "-", xx)))
Выход:
> new_list
$Patient
Patient-Pat1 Patient-Pat2 Patient-Pat3
"Pat1" "Pat2" "Pat3"
$Cycle
Cycle-C1 Cycle-C2
"C1" "C2"
Буду признателен за комментарий по поводу отрицательного голоса. Это всего лишь вариант с двумя деталями, который некоторым может понравиться.
Извините, я, должно быть, по ошибке проголосовал против при прокрутке на телефоне. Я отредактировал, поэтому могу отменить это.
Я собираюсь использовать это, потому что это синтаксис, который я понимаю лучше всего, и все это базово. Спасибо!