2 votes

Renommer la liste imbriquée lorsque certains éléments sont nuls

Comment renommer une liste imbriquée qui peut avoir ou non des valeurs en utilisant purrr? J'ai essayé modify_at() mais j'obtiens des erreurs si l'une des listes est vide

library(tidyverse)

my_list <- 
  list(
    list(
      app = list(id = 123, name = "abc"), 
      tag = list(id = 456, name = "def")
    ),
    list(
      app = list(id = 345, name = "xyz"), 
      tag = list(id = 678, name = "abc")   # <--- fonctionne
    # tag = NULL                           # <--- ne fonctionne pas

    )
  ) 

Je veux que app$id et app$name deviennent app_id et app_name et finalement retourner un data frame

| app_id | app_name | tag_id | tag_name |
|--------|----------|--------|----------|
|    123 |      abc |    456 |      def |
|    345 |      xyz |        |          |

Cet exemple fonctionne si les deux applications ont des tags, mais si l'un des tags est null (modifier les commentaires ci-dessus), l'étape modify_at("tag") échoue

my_list |> 
  map(
    ~.x |> 
      modify_at("app", ~set_names(.x, glue("app_{names(.x)}"))) |> 
      modify_at("tag", ~set_names(.x, glue("tag_{names(.x)}"))) |> 
      as.data.frame() |>   # aussi pourquoi as_tibble() ne fonctionne pas ici?
      rename_all(str_replace_all, "\\.", "_")
  ) |> 
  list_rbind()

Plus précisément, ici :

my_list |> 
  map(
    ~modify_at(.x, "tag",  ~set_names(.x, glue("tag_{names(.x)}")))
  )
# Error in `map()`:
#    In index: 2.
# Caused by error in `map()`:
#    In index: 1.
#  With name: tag.
# Caused by error in `set_names()`:
#   ! `x` must be a vector

J'ai trouvé ce problème sur GitHub mais j'ai toujours eu des problèmes

1voto

TarJae Points 9674

Voici une solution avec une fonction personnalisée :

Avec cette fonction, nous gérons les éléments NULL en les remplaçant par une liste d'éléments NA. Nous utilisons ensuite map_vec pour convertir les listes en un data frame.

library(tidyverse)

my_func <- function(list, prefix) {
  if(is.null(list)) {
    list <- list(id = NA, name = NA)
  }
  set_names(list, paste0(prefix, "_", names(list)))
}

my_list |> 
  map(
    ~ list(
      app = my_func(.x$app, "app"),
      tag = my_func(.x$tag, "tag")
    )
  ) |> 
  map_vec(
    ~ bind_cols(.x$app, .x$tag)
  )

app_id app_name tag_id tag_name

1    123 abc         456 def     
2    345 xyz          NA NA

1voto

Jay Points 1879

Vous pouvez faire cela sans avoir besoin de renommer explicitement au préalable. Il vous suffit de lier les listes internes les unes aux autres, puis de lier le résultat et de pivoter.

library(tidyverse)

my_list |>
  map(bind_rows, .id = "élément") |>
  bind_rows(.id = "id_liste") |>
  pivot_wider(noms_depuis = élément, 
              valeurs_de = c(id, nom),
              noms_collage = "{élément}_{.value}",
              noms_variables = "plus lent")

# A tibble: 2 × 5
  id_liste id_app nom_app id_tag nom_tag

1 1          123 abc       456 def     
2 2          345 xyz        NA NA

0voto

Joris Chau Points 1728

Pas une solution purrr, mais cela peut également être fait en utilisant rrapply() (dans le package rrapply):

rrapply::rrapply(
  my_list, 
  condition = Negate(is.null),                ## filtrer les NULL
  how = "bind",                               ## déplier en large data.frame
  options = list(coldepth = 2, namesep = "_") ## options de dépliage supplémentaires
) 
#>   app_id app_name tag_id tag_name
#> 1    123      abc    456      def
#> 2    345      xyz     NA

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X