【问题标题】:How to manipulate NULL elements in a nested list?如何操作嵌套列表中的 NULL 元素?
【发布时间】:2016-08-15 06:00:48
【问题描述】:

我有一个包含 NULL 元素的嵌套列表,我想用其他东西替换它们。例如:

l <- list(
  NULL,
  1,
  list(
    2,
    NULL,
    list(
      3,
      NULL
    )
  )
)

我想用 NA 替换 NULL 元素。执行此操作的自然方法是使用rapply 递归循环列表。我试过了:

rapply(l, function(x) NA, classes = "NULL", how = "replace")
rapply(l, function(x) if(is.null(x)) NA else x, how = "replace")

不幸的是,这些方法都不起作用,因为rapply 显然忽略了NULL 元素。

如何操作嵌套列表中的NULL 元素?

【问题讨论】:

  • 我终于放弃了 rapply 并使用something which I can predict how it will work 所以rapply2(l, function(x) if (is.null(x)) NA else x) 可以工作
  • 这里有一个有趣的post 讨论了这个问题,但并没有完全回答你关于如何解决这个问题的问题。
  • 在史蒂夫布;这是正确的。很抱歉@Owen 似乎不再在这里花太多时间了。他的帖子总是很有见地。
  • 基本上每个人都想做同样的事情并重写自己的递归 lapply。恕我直言,有点负担过重;也许这应该存在于基础 R 中。
  • @rawr 我想知道你写这篇评论后是否改变了rapply2() 的行为。

标签: r list


【解决方案1】:

我将选择“使用rapply 的版本不会对NULL 产生奇怪的行为”。这是我能想到的最简单的实现:

simple_rapply <- function(x, fn)
{
  if(is.list(x))
  {
    lapply(x, simple_rapply, fn)
  } else
  {
    fn(x)
  }
}

rawr::rapply2,正如@rawr 在 cmets 中提到的那样,是一种更复杂的尝试。)

现在我可以使用

simple_rapply(l, function(x) if(is.null(x)) NA else x)

【讨论】:

    【解决方案2】:

    这是 William Dunlap 在 2010 年在 Rhelp 上提出这个问题时的建议:

    replaceInList <- function (x, FUN, ...) 
      {
          if (is.list(x)) {
              for (i in seq_along(x)) {
                  x[i] <- list(replaceInList(x[[i]], FUN, ...))
              }
              x
          }
          else FUN(x, ...)
      }
     replaceInList(l, function(x)if(is.null(x))NA else x)
    

    【讨论】:

      【解决方案3】:

      这是一个 hack,但就 hack 而言,我觉得我对它有点满意。

      lna <- eval(parse(text = gsub("NULL", "NA", deparse(l))))
      
      str(lna)
      #> List of 3
      #> $ : logi NA
      #> $ : num 1
      #> $ :List of 3
      #> ..$ : num 2
      #> ..$ : logi NA
      #> ..$ :List of 2
      #> .. ..$ : num 3
      #> .. ..$ : logi NA
      

      更新:

      如果由于某种原因您需要 "NULL" 作为列表中的字符条目(极端情况,很多吗?)您仍然可以使用上述技巧,因为它替换了字符串的内容,而不是引号,因此它只需要另一个步骤

      l2 <- list(
        NULL,
        1,
        list(
          2,
          "NULL",
          list(
            3,
            NULL
          )
        )
      )
      
      lna2   <- eval(parse(text = gsub("NULL", "NA", deparse(l2))))
      lna2_2 <- eval(parse(text = gsub('\\"NA\\"', '\"NULL\"', deparse(lna2))))
      
      str(lna2_2)
      #> List of 3
      #> $ : logi NA
      #> $ : num 1
      #> $ :List of 3
      #> ..$ : num 2
      #> ..$ : chr "NULL"
      #> ..$ :List of 2
      #> .. ..$ : num 3
      #> .. ..$ : logi NA 
      

      【讨论】:

      • 这是一个非常好的 hack,但我担心 eval 中的黑魔法。所以这个例子很好,但是一个名为"NULL"的字符串会混淆这个。
      • 当然。这与您的用例有关吗?开始时这种做法很糟糕吗?
      • 看到这种将 R 转换为宏处理器的努力有点有趣。不是“坏”,只是有趣。
      • 尝试使用字符串“NULL”会发生什么,引号没有被触及,所以实际上你只需要第二步......编辑。
      • @JonathanCarroll 另一个邪恶的极端案例:如果元素的名称之一是"NA",第二个版本就会中断。 (该名称将转换为 "NULL"
      【解决方案4】:

      我将替换内容包裹在 sapply 中,这使得它对我来说更具可读性/可理解性,尽管不太通用。

       replace_null <- function(x) {
        lapply(x, function(x) {
          if (is.list(x)){
            replace_null(x)
            } else{
              if(is.null(x)) NA else(x)
            } 
          })
      }
      
      replace_null(l)
      

      【讨论】:

      • simplify = FALSE 代替sapply 只需使用lapply。你也应该避免ifelse
      • @sayaa 使用ifelse 而不是ifelse 意味着您将遇到非向量输入的问题。尝试将 data.frame 或公式作为列表的一个元素。
      • 我明白了;并且无论如何它都没有提供向量化的好处,因为这些列表元素之一不是c(1,NULL,1),因为向量只会删除该元素。我想写if(is.null(x)) NA else(x) 对我来说似乎更笨拙。我还是会编辑
      【解决方案5】:

      这也可以通过rrapply-package 中的rrapply() 来完成。以下是我们可以用NA 值替换嵌套列表中的NULL 元素的几种不同方法:

      library(rrapply)
      
      l <- list(
          NULL,
          1,
          list(
              2,
              NULL,
              list(
                  3,
                  NULL
              )
          )
      )
      
      ## replace NULL by NA using only f
      rrapply(l, f = function(x) if(is.null(x)) NA else x, how = "replace")
      #> [[1]]
      #> [1] NA
      #> 
      #> [[2]]
      #> [1] 1
      #> 
      #> [[3]]
      #> [[3]][[1]]
      #> [1] 2
      #> 
      #> [[3]][[2]]
      #> [1] NA
      #> 
      #> [[3]][[3]]
      #> [[3]][[3]][[1]]
      #> [1] 3
      #> 
      #> [[3]][[3]][[2]]
      #> [1] NA
      
      ## replace NULL by NA using condition argument
      rrapply(l, condition = is.null, f = function(x) NA, how = "replace")
      #> [[1]]
      #> [1] NA
      #> 
      #> [[2]]
      #> [1] 1
      #> 
      #> [[3]]
      #> [[3]][[1]]
      #> [1] 2
      #> 
      #> [[3]][[2]]
      #> [1] NA
      #> 
      #> [[3]][[3]]
      #> [[3]][[3]][[1]]
      #> [1] 3
      #> 
      #> [[3]][[3]][[2]]
      #> [1] NA
      
      ## replace NULL by NA using condition and deflt arguments 
      rrapply(l, condition = Negate(is.null), deflt = NA, how = "list")
      #> [[1]]
      #> [1] NA
      #> 
      #> [[2]]
      #> [1] 1
      #> 
      #> [[3]]
      #> [[3]][[1]]
      #> [1] 2
      #> 
      #> [[3]][[2]]
      #> [1] NA
      #> 
      #> [[3]][[3]]
      #> [[3]][[3]][[1]]
      #> [1] 3
      #> 
      #> [[3]][[3]][[2]]
      #> [1] NA
      

      我们还可以通过设置 how = "prune" 来从列表中删除 NULL 元素:

      ## keep only non-NULL elements
      rrapply(l, condition = Negate(is.null), how = "prune")
      #> [[1]]
      #> [1] 1
      #> 
      #> [[2]]
      #> [[2]][[1]]
      #> [1] 2
      #> 
      #> [[2]][[2]]
      #> [[2]][[2]][[1]]
      #> [1] 3
      

      【讨论】:

        猜你喜欢
        • 2021-01-20
        • 1970-01-01
        • 1970-01-01
        • 2019-07-13
        • 2019-11-28
        • 2022-01-23
        • 2021-06-16
        • 1970-01-01
        • 2022-01-19
        相关资源
        最近更新 更多