рекурсивно упростить списки, используя unlist - PullRequest
0 голосов
/ 07 декабря 2018

Рассмотрим случай, подобный следующему:

xml_list <- list(
  a = "7",
  b = list("8"),
  c = list(
    c.a = "7",
    c.b = list("8"), 
    c.c = list("9", "10"),
    c.d = c("11", "12", "13")),
  d = c("a", "b", "c"))

Я ищу способ рекурсивного упрощения этой конструкции, так что unlist вызывается для любого list длины 1.Ожидаемый результат для приведенного выше примера будет выглядеть следующим образом:

list(
  a = "7",
  b = "8",
  c = list(
    c.a = "7",
    c.b = "8", 
    c.c = list("9", "10"),
    c.d = c("11", "12", "13")),
 d = c("a", "b", "c"))

Я баловался с rapply, но это явно работает на list -членах, которые NOT перечисляют себя, поэтомунаписал следующее:

library(magrittr)
clean_up_list <- function(xml_list){
  xml_list %>%
    lapply(
      function(x){
        if(is.list(x)){
          if(length(x) == 1){
            x %<>%
              unlist()
          } else {
            x %<>%
              clean_up_list()
          }
        }
        return(x)
      })
}

Это, однако, я даже не могу проверить, как Error: C stack usage 7969588 is too close to the limit (по крайней мере, в списках, которые я хочу окончательно обработать).

Копать глубже (и обдумав ответ @ Roland), я нашел решение, которое использует purrr -хорошость, выполняет итерацию по глубине списка и NEARLY делает то, что я хочу:

clean_up_list <- function(xml_list)
{
  list_depth <- xml_list %>%
    purrr::vec_depth()
  for(dl in rev(sequence(list_depth)))
  {
    xml_list %<>%
      purrr::modify_depth(
        .depth = dl,
        .ragged = TRUE,
        .f = function(x)
        {
          if(is.list(x) && length(x) == 1 && length(x[[1]]) == 1)
          {
            unlist(x, use.names = FALSE)
          } else {
            x
          }
        })
  }
  return(xml_list)
}

Похоже, что это работает, как и предполагалось, даже для списков глубины, с которыми я имею дело НО элементы, которые раньше были векторами (например, c.d и d в примере), теперь преобразуются в lists, что побеждает цель ... дальнейшее понимание?

Ответы [ 2 ]

0 голосов
/ 17 декабря 2018

С помощью тикета против github хранилища purrr я решил это: с текущей версией разработчика purrr (устанавливается через remotes::install_github('tidyverse/purrr')), purrr-зависимый код в вопросе работает должным образом и больше не «листает» векторы.Таким образом, этот код должен послужить ответом на этот вопрос и стать полностью функциональным с пакетами на основе CRAN после нового года 2018/19.

0 голосов
/ 07 декабря 2018

Я не разбираюсь в этом материале magrittr, но легко создать рекурсивную функцию:

foo <- function(L) lapply(L, function(x) {
  if (is.list(x) && length(x) > 1) return(foo(x))
  if (is.list(x) && length(x) == 1) x[[1]] else x
  })
foo(test_list)

#$`a`
# [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S" "T" "U" "V" "W" "X" "Y" "Z"
#
#$b
#[1] "a"
#
#$c
#$c$`c.1`
#[1] "b"
#
#$c$c.2
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" "t" "u" "v" "w" "x" "y" "z"
#
#$c$c.3
#$c$c.3[[1]]
#[1] "c"
#
#$c$c.3[[2]]
#[1] "d"

Если это выдает ошибку, касающуюся использования стека C, то у вас есть глубоко вложенные списки.Тогда вы не сможете использовать рекурсию, что сделает эту проблему сложной.Затем я бы изменил создание этого списка, если это возможно.Или вы можете попытаться увеличить размер стека C .

...