Я хотел бы преобразовать такой список в тиббл.
lst <- list(
"A"=list(
"Category" = "A",
"Team"=c("x"),
"City" = "N"
),
"C"=list(
"Category" = "C",
"Team"=c(
"x","v","z"
),
"City" = "V"
),
"F"=list(
"Category" = "F",
"Team"=c("o")
),
"K"=list(
"Category" = "K",
"Team"=c(
"f","h","y"
),
"City" = "T"
),
"B"=list(
"Category" = "B",
"City"=c("Q")
)
)
В списке может быть несколько элементов. В этом примере у меня их всего три: Категория, Команда и Город. Категория всегда будет иметь только одно уникальное значение без дублирования, тогда как другие поля могут быть одним, несколькими или отсутствовать.
Мне нужна одна уникальная категория в каждой строке. Любой другой столбец, имеющий несколько значений для категории, должен представлять собой вложенный список.
Это результат, который я ищу:
Category Team City
<chr> <list> <chr>
1 A <chr [1]> N
2 C <chr [3]> V
3 F <chr [1]> NA
4 K <chr [3]> T
5 B <chr [1]> Q
Это было создано вручную пользователем
library(purrr)
library(dplyr)
d <- map_dfr(lst,~as_tibble(.x))
reframe(d,across(Team,list),.by=Category) |>
left_join(distinct(select(d,-Team),Category,.keep_all=TRUE), by = "Category")
Я ищу решение, которое будет работать для любого количества полей/столбцов и любого количества элементов в любом столбце (кроме категории, в которой будет только один элемент).
Одна из многих неудачных попыток:
map_dfr(lst,~as_tibble(.x)) |>
group_by(Category) |>
summarise(
across(everything(), ~ {
if (n_distinct(.) == 1) {
first(.)
} else {
list(.)
}
})
)
Error in `summarise()`:
ℹ In argument: `across(...)`.
Caused by error:
! `Team` must return compatible vectors across groups.
ℹ Result of type <character> for group A: `Category = "A"`.
ℹ Result of type <list> for group C: `Category = "C"`.
Run `rlang::last_trace()` to see where the error occurred.
Сессия
R version 4.4.1 (2024-06-14)
Platform: x86_64-pc-linux-gnu
Ubuntu 22.04.4 LTS
dplyr_1.1.4
tidyr_1.3.1
purrr_1.0.2
Возможно, это решит вашу проблему
lst %>%
dplyr::bind_rows() %>%
tidyr::nest(Team = Team)
# A tibble: 5 × 3
Category City Team
<chr> <chr> <list>
1 A N <tibble [1 × 1]>
2 C V <tibble [3 × 1]>
3 F NA <tibble [1 × 1]>
4 K T <tibble [3 × 1]>
5 B Q <tibble [1 × 1]>
Возможно, что-то вроде:
list_transpose(lst, default = NA, template = unique(unlist(map(lst, names)))) |>
as_tibble() |>
mutate(across(where(is.list), \(l) if (all(lengths(l) == 1)) unlist(l) else l))
# A tibble: 5 × 3 Category Team City <chr> <named list> <chr> 1 A <chr [1]> N 2 C <chr [3]> V 3 F <chr [1]> NA 4 K <chr [3]> T 5 B <lgl [1]> Q
Я надеюсь, что это может сработать хорошо. протестировано на новом элементе с новым столбцом.
lst <- list(
.... PREVIOUS lst LIST ....
my_addition = list(
Category = "Z",
Team = state.name[1:2],
City = letters[1:3],
Zip = state.abb[1:4]
)
)
lst_to_df <- function(lst_item) {
tibble(
Category = lst_item$Category,
!!!map(lst_item[-1], list)
)
}
map_dfr(lst, lst_to_df, .id = "List_Name") # remove `.id` to change from `my_addition` to `Z` as you probably really want
Category Team City Zip
<chr> <list> <list> <list>
1 A <chr [1]> <chr [1]> <NULL>
2 C <chr [3]> <chr [1]> <NULL>
3 F <chr [1]> <NULL> <NULL>
4 K <chr [3]> <chr [1]> <NULL>
5 B <NULL> <chr [1]> <NULL>
6 Z <chr [2]> <chr [3]> <chr [4]>
>
При этом все столбцы всегда будут храниться в виде списков, чего, я думаю, ОП не хочет.
Вот еще одно решение, которое работает для любого количества полей/столбцов и любого количества элементов в любом столбце. Поскольку вы хотите хранить одну строку по уровню категории, каждый столбец соответствует списку с соответствующими различными значениями.
library(rlang)
# Bind lists as tibble
df <- lst |>
bind_rows()
# Get the column name used as key
key <- colnames(df)[1]
# Map over all columns and nest distinct values
map(colnames(df)[-1], function(x){
df |>
select(all_of(c(key, x))) |>
group_by(!!sym(key)) |>
distinct(!!sym(x)) |>
nest(!!sym(x):= x)
}) |>
# Reduce the lists using left_join
reduce(left_join, by = key)
# A tibble: 5 × 3
# Groups: Category [5]
# Category Team City
# <chr> <list> <list>
#1 A <tibble [1 × 1]> <tibble [1 × 1]>
#2 C <tibble [3 × 1]> <tibble [1 × 1]>
#3 F <tibble [1 × 1]> <tibble [1 × 1]>
#4 K <tibble [3 × 1]> <tibble [1 × 1]>
#5 B <tibble [1 × 1]> <tibble [1 × 1]>
Подход с использованием unnest_wider
library(dplyr)
library(tidyr)
tibble(lst) %>% unnest_wider(lst)
# A tibble: 5 × 3
Category Team City
<chr> <list> <chr>
1 A <chr [1]> N
2 C <chr [3]> V
3 F <chr [1]> NA
4 K <chr [3]> T
5 B <NULL> Q
Обратите внимание, что несуществующая команда сохраняется как NULL
.
Я добавил новый элемент списка с другими столбцами (не город/команда), и он не распечатал его. Этот элемент: my_addition = list( Category = "Z", Team = state.name[1:2], City = Letters [1:3], Zip = state.abb[1:4] )