У меня такой репрекс:
library(dplyr)
library(purrr)
df1 <- data.frame(
col1 = 1:5,
col2 = 6:10
)
df2 <- df1 %>%
mutate(col3 = 1:5)
ls <- list(
a = list(df1 = df1),
b = list(
df1 = df1,
df2 = df2
),
c = list(
df2 = df2
)
)
Я хочу отфильтровать ls
, чтобы сохранить элементы ls
, содержащие col3
по имени.
Я пробовал использовать Keep, но не могу правильно индексировать нужную глубину.
Ожидаемое решение:
list(
b = list(
df2 = df2
)
,c = list(
df2 = df2
)
)
Это близко:
ls %>%
map(
~keep(.x, ~ "col3" %in% names(.x))
)
С purrr
это оказалось сложнее, чем я думал. Просто указать, что вы хотите сохранить элементы, в имени которых есть "col3"
, но в итоге вы получите список с вложенными NULL
элементами. Вот функция для их удаления:
remove_nulls <- function(l) {
l |>
# Replace nested NULLs with NULL
map(\(x) if (is.null(unlist(x))) NULL else x) |>
# Remove NULLs at 2nd level or below
map(compact) |>
# Remove NULLs at top level
compact()
}
Тогда это всего лишь случай использования purrr::modify_tree() для изменения каждого листа, использования функции предиката для определения листьев как фреймов данных и поиска "col3"
.
out <- modify_tree(
ls,
is_node = negate(is.data.frame),
leaf = \(x) if ("col3" %in% names(x)) x else NULL
) |>
remove_nulls()
identical(out, desired)
# [1] TRUE
@sactyr да, это имеет смысл. Этот вопрос заставляет меня думать, что purrr
не хватает некоторых функций по удалению нулей из списков произвольной глубины.
Я знаю, вы просите purrr
, но вот удобная фраза с rrapply
.
rrapply
основан на базе R rapply
и направлен на рекурсивное применение функций к набору элементов списка. Здесь я использую аргумент condition
, чтобы указать, какие элементы следует оценивать, и how = "prune"
, чтобы удалить все элементы, не соответствующие условию. classes = "data.frame"
указывает, что функция condition
должна применяться на уровне data.frame
(а не, например, на уровнях столбцов data.frames).
library(rrapply)
rrapply(ls, condition = \(x) "col3" %in% names(x), classes = "data.frame", how = "prune")
# $b
# $b$df2
# col1 col2 col3
# 1 1 6 1
# 2 2 7 2
# 3 3 8 3
# 4 4 9 4
# 5 5 10 5
#
#
# $c
# $c$df2
# col1 col2 col3
# 1 1 6 1
# 2 2 7 2
# 3 3 8 3
# 4 4 9 4
# 5 5 10 5
спасибо, да, пока я хотел использовать keep
, я не мог заставить его работать правильно. Я отметил ваш ответ как предпочтительный просто для удобства чтения и краткости.
Спасибо @SamR, я хотел как можно больше остаться с
purrr
, но из двух ответов я выбрал другой из-за читабельности и краткости.