【问题标题】:simplify lists recursively using unlist使用 unlist 递归地简化列表
【发布时间】:2018-12-07 08:45:26
【问题描述】:

考虑这样一个案例:

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"))

我正在寻找的是一种如何递归简化此构造的方法,以便在任何长度为 1 的 list 上调用 unlist。上述示例的预期结果如下所示:

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-成员本身是列表,所以写了以下内容:

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-goodness 的解决方案,反向迭代列表深度并且几乎 做我想做的事:

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)
}

这似乎可以按预期工作,即使对于我正在处理的深度列表 BUT 曾经是向量的元素(如示例中的 c.dd)现在都已转换到lists,这违背了目的……还有什么进一步的见解吗?

【问题讨论】:

    标签: r list purrr simplify


    【解决方案1】:

    我不明白这个 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 堆栈使用的错误,那么您的列表是深度嵌套的。那时你不能使用递归,这会使这成为一个具有挑战性的问题。如果可能的话,我会修改这个列表的创建。或者你可以尝试increase the C stack size

    【讨论】:

    • 谢谢。这实质上是我自己的函数在真实(更深的)列表中尝试并因相同的 C 堆栈问题而失败。
    • 见我回答的最后一部分。我想你可能有 xy 问题。
    【解决方案2】:

    借助ticketpurrrgithub 存储库的帮助,我解决了这个问题:使用当前开发人员版本的purrr(可通过remotes::install_github('tidyverse/purrr') 安装),purrr-dpendent 代码在该问题按预期工作,不再“列出”向量。因此,该代码应作为问题的答案,并在 2018/19 新年之后与CRAN-borne 软件包一起使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 1970-01-01
      • 2020-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多