Как создать тиббл из вложенного списка, в котором одна часть списка для некоторых записей вложена немного иначе, чем для других

Мне интересно, как создать тиббл из списка, где один подсписок содержит другой список переменных для некоторых записей, в то время как тот же подсписок содержит только переменные для других записей. Это немного расплывчато, поэтому я надеюсь, что следующий пример, основанный на этом вопросе (Как создать фрейм данных из списка, выбрав, на каком подсписке сосредоточиться) --- поможет:

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     

Поскольку это ваш второй вопрос по теме, хотелось бы узнать, как выглядят ваши необработанные данные. Может быть, есть более простой способ? Я думаю, что collapse::unlist2d() или подобные функции могут помочь, но я не нашел хорошего, то есть жестко запрограммированного, решения.

Friede 28.08.2024 20:47

@Friede, необработанные данные — это данные Scopus из пакета rscopus, в частности функция get_complete_author_info (или другие связанные функции). Если вы перейдете на dev.elsevier.com/scopus.html#!/Author_Search/AuthorSearch, вы можете поиграть с примерами, чтобы получить общее представление о том, как выглядят данные.

bill999 28.08.2024 20:54

Т.е. listexample это пример вывода вашего запроса API?

Friede 28.08.2024 20:58

Нет, это не запрос API (я не хотел использовать фактические данные, поскольку для этого требуется токен), но имеет аналогичный формат, по крайней мере, в некотором смысле.

bill999 28.08.2024 22:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
66
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Кажется, что именованные списки вызывают у вас проблемы, потому что некоторые из них имеют имена, а некоторые нет. Нам просто нужно превратить наши именованные списки в безымянные списки того же формата.

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   

Хороший подход - спасибо!

bill999 29.08.2024 00:09

Такой подход более чем уродлив. Я зайду попозже. Однако я подозреваю, что должен быть краткий способ решения головоломки с аргументами 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>

Спасибо за ответ! Я не знал ни об одной из функций, которые вы использовали, так хорошо, что они были у меня на радаре.

bill999 29.08.2024 00:10
Ответ принят как подходящий

Попытка в этом вопросе на самом деле довольно близка. Строки ниже такие же, как и там, за исключением добавления (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.

bill999 29.08.2024 00:08

Другие вопросы по теме