Мне интересно, как создать тиббл из списка, где один подсписок содержит другой список переменных для некоторых записей, в то время как тот же подсписок содержит только переменные для других записей. Это немного расплывчато, поэтому я надеюсь, что следующий пример, основанный на этом вопросе (Как создать фрейм данных из списка, выбрав, на каком подсписке сосредоточиться) --- поможет:
library(tidyverse)
listexample = list(books = list(list(
title = "Book 1",
entry = "entry 1",
publisher = "Books Unlimited",
authors = list(
list(name = "bob", location = "north dakota",
subject = list(
list(area = "Fiction", abbrev = "FI"),
list(area = "Mystery", abbrev = "MY"))),
list(name = "susan", location = "california",
subject = list(area = "Fiction", abbrev = "FI")),
list(name = "tim",
subject = list(
list(area = "Fiction", abbrev = "FI"),
list(area = "Mystery", abbrev = "MY"),
list(area = "Suspense", abbrev = "SU")))),
isbn = "1358",
universities = list(
list(univ = "univ1"),
list(univ = "univ2"))
),
list(
title = "Book 2",
entry = "entry 2",
publisher = "Books Unified",
authors = list(
list(name = "tom", location = "north dakota",
subject = list(
list(area = "Fiction", abbrev = "FI"),
list(area = "Mystery", abbrev = "MY"))),
list(name = "sally", location = "california",
subject = list(
list(area = "Nonfiction", abbrev = "NF"),
list(area = "Biography", abbrev = "BIO"))),
list(name = "erica", location = "berlin",
subject = list(area = "Fiction", abbrev = "FI"))),
isbn = "1258",
universities = list(
list(univ = "univ5"),
list(univ = "univ2"),
list(univ = "univ99"),
list(univ = "univ2"),
list(univ = "univ3"))
)
),
misc = list(name = "Jim Smith", location = "Alaska"))
(Чтобы сделать пример более понятным, представьте себе subject
автора как тему, на которую он обычно пишет, даже если она отличается от темы конкретной книги.)
Проблема в том, что в списке subject
у некоторых авторов (Боб, Тим, Том и Салли) есть два (или три) area
(каждый из которых содержится в отдельном подсписке), а у других авторов (Сьюзен и Эрика) — только один. area
(не вынесен в отдельный подсписок).
После изменения ответа на вопрос, указанный выше, я пытаюсь:
listexample %>%
.$books %>%
tibblify %>%
select(title, authors) %>%
unnest(authors) %>%
unnest_wider(subject, names_sep = "a")
Это производит:
# A tibble: 6 × 8
title name location subjecta1 subjecta2 subjectaarea subjectaabbrev subjecta3
<chr> <chr> <chr> <list> <list> <chr> <chr> <list>
1 Book 1 bob north dakota <named list [2]> <named list [2]> NA NA <NULL>
2 Book 1 susan california <NULL> <NULL> Fiction FI <NULL>
3 Book 1 tim NA <named list [2]> <named list [2]> NA NA <named list [2]>
4 Book 2 tom north dakota <named list [2]> <named list [2]> NA NA <NULL>
5 Book 2 sally california <named list [2]> <named list [2]> NA NA <NULL>
6 Book 2 erica berlin <NULL> <NULL> Fiction FI <NULL>
Проблема в том, что в расширении записи Сьюзан и Эрики отображаются в столбцах, отличных от записей других людей. Допустим, я вообще не хочу включать столбец сокращений. Как мне сделать так, чтобы было три столбца subject1area
, subject2area
и subject3area
, чтобы результат выглядел так:
title name location subject1area subject2area subject3area
<chr> <chr> <chr> <chr> <chr> <chr>
1 Book 1 bob north dakota Fiction Mystery NA
2 Book 1 susan california Fiction NA NA
3 Book 1 tim NA Fiction Mystery Suspense
4 Book 2 tom north dakota Fiction Mystery NA
5 Book 2 sally california Nonfiction Biography NA
6 Book 2 erica berlin Fiction NA NA
@Friede, необработанные данные — это данные Scopus из пакета rscopus
, в частности функция get_complete_author_info
(или другие связанные функции). Если вы перейдете на dev.elsevier.com/scopus.html#!/Author_Search/AuthorSearch, вы можете поиграть с примерами, чтобы получить общее представление о том, как выглядят данные.
Т.е. listexample
это пример вывода вашего запроса API?
Нет, это не запрос API (я не хотел использовать фактические данные, поскольку для этого требуется токен), но имеет аналогичный формат, по крайней мере, в некотором смысле.
Кажется, что именованные списки вызывают у вас проблемы, потому что некоторые из них имеют имена, а некоторые нет. Нам просто нужно превратить наши именованные списки в безымянные списки того же формата.
tibble(books = listexample$books) %>%
unnest_wider(books) %>%
select(title, authors) %>%
unnest_longer(authors) %>%
unnest_wider(authors) %>%
# if it is named let's get the area
mutate(subject =
map_if (
subject,
~ !is.null(names(.x)),
~ list(pluck(.x, "area"))
)
) %>%
# unnest like normal, setting NA as default
unnest_wider(
subject,
names_sep = "area",
transform = ~ pluck(.x, 1, .default = NA)
)
#> # A tibble: 6 × 6
#> title name location subjectarea1 subjectarea2 subjectarea3
#> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 Book 1 bob north dakota Fiction Mystery NA
#> 2 Book 1 susan california Fiction NA NA
#> 3 Book 1 tim NA Fiction Mystery Suspense
#> 4 Book 2 tom north dakota Fiction Mystery NA
#> 5 Book 2 sally california Nonfiction Biography NA
#> 6 Book 2 erica berlin Fiction NA NA
Хороший подход - спасибо!
Такой подход более чем уродлив. Я зайду попозже. Однако я подозреваю, что должен быть краткий способ решения головоломки с аргументами tidyr::pivot_wider()
, который также позволяет нам избавиться от sapply/lapply
:
cbind(title = rep(sapply(o <- listexample[[1]], \(i) i$title),
sapply(o, \(i) lengths(i["authors"]))),
lapply(lapply(o, \(i) i[names(i) %in% c("authors")]), \(i) {
rrapply::rrapply(i, how = "melt") |>
dplyr::select(L2, L3, L4, L5, value) |>
tidyr::pivot_wider(id_cols = L2, names_from = L3:L5,
values_from = value) } ) |>
dplyr::bind_rows() |>
select(name_NA_NA, location_NA_NA,subject_1_area,
subject_area_NA, subject_2_area,subject_3_area)) |>
tidyr::unite(subject_1_area, c(subject_1_area, subject_area_NA),
na.rm = TRUE, remove = TRUE) |>
dplyr::rename(name = name_NA_NA, location = location_NA_NA)
предоставление
title name location subject_1_area subject_2_area subject_3_area
1 Book 1 bob north dakota Fiction Mystery <NA>
2 Book 1 susan california Fiction <NA> <NA>
3 Book 1 tim <NA> Fiction Mystery Suspense
4 Book 2 tom north dakota Fiction Mystery <NA>
5 Book 2 sally california Nonfiction Biography <NA>
6 Book 2 erica berlin Fiction <NA> <NA>
Спасибо за ответ! Я не знал ни об одной из функций, которые вы использовали, так хорошо, что они были у меня на радаре.
Попытка в этом вопросе на самом деле довольно близка. Строки ниже такие же, как и там, за исключением добавления (1) строки mutate
, которая упорядочивает столбец различной глубины subject
, чтобы его можно было обрабатывать равномерно с помощью unnest_wider
, и (2) rename_with
строки, которая гарантирует, что имена столбцов такие же, как в вопросе. .
listexample %>%
.$book %>%
tibblify %>%
select(title, authors) %>%
unnest(authors) %>%
mutate(subject = lapply(subject, \(x) bind_rows(x)[["area"]])) %>%
unnest_wider(subject, names_sep = "a") %>%
rename_with(~ sub("subjecta(\\d+)", "subject\\1area", .x))
предоставление
# A tibble: 6 × 6
title name location subject1area subject2area subject3area
<chr> <chr> <chr> <chr> <chr> <chr>
1 Book 1 bob north dakota Fiction Mystery <NA>
2 Book 1 susan california Fiction <NA> <NA>
3 Book 1 tim <NA> Fiction Mystery Suspense
4 Book 2 tom north dakota Fiction Mystery <NA>
5 Book 2 sally california Nonfiction Biography <NA>
6 Book 2 erica berlin Fiction <NA> <NA>
Ах, умно, что сделал шаг l_apply
/bind_rows
.
Поскольку это ваш второй вопрос по теме, хотелось бы узнать, как выглядят ваши необработанные данные. Может быть, есть более простой способ? Я думаю, что
collapse::unlist2d()
или подобные функции могут помочь, но я не нашел хорошего, то есть жестко запрограммированного, решения.