【问题标题】:Efficiently remove all NULL values in a list and all sublists有效删除列表和所有子列表中的所有 NULL 值
【发布时间】:2014-09-24 17:14:09
【问题描述】:

考虑以下列表:

> l1 <- list(NULL,1,2,list(NULL,3,list(NULL,4)))
> str(l1)
List of 4
 $ : NULL
 $ : num 1
 $ : num 2
 $ :List of 3
  ..$ : NULL
  ..$ : num 3
  ..$ :List of 2
  .. ..$ : NULL
  .. ..$ : num 4

要从第一级删除NULL 值,只需调用

l1[vapply(l1,is.null,logical(1L))] <- NULL

现在我想删除所有级别的所有NULL 值,我想出了以下代码。

list.clean <- function(.data, fun = is.null, recursive = FALSE) {
  if(recursive) {
    .data <- lapply(.data, function(.item) {
      if(is.list(.item)) list.clean(.item, fun, TRUE)
      else .item
    })
  }
  .data[vapply(.data,fun,logical(1L))] <- NULL
  .data
}

然后打电话

> list.clean(l1, recursive = TRUE)
[[1]]
[1] 1

[[2]]
[1] 2

[[3]]
[[3]][[1]]
[1] 3

[[3]][[2]]
[[3]][[2]][[1]]
[1] 4

虽然它现在有效,但有没有更好或更快的方法?

【问题讨论】:

  • 我知道一种不计算任何东西的快速方法......尽管它可能会被不赞成。 :)

标签: r list null


【解决方案1】:

使用外部包,现在也可以使用rrapply-包中的rrapply(基础rapply 的修订版)来完成。设置how = "prune" 修剪所有不满足condition 参数中定义的函数的列表元素:

library(rrapply)

l1 <- list(NULL,1,2,list(NULL,3,list(NULL,4)))

rrapply(l1, condition = Negate(is.null), how = "prune")
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [[3]][[1]]
#> [1] 3
#> 
#> [[3]][[2]]
#> [[3]][[2]][[1]]
#> [1] 4

我们可以对照 OP 的 list.clean 函数和 G. Grothendieck 的 rmNull 函数对大型列表对象的计算时间进行基准测试:

## benchmark recursion functions
rmNull <- function(x) {
  x <- Filter(Negate(is.null), x)
  lapply(x, function(x) if (is.list(x)) rmNull(x) else x)
}

list.clean <- function(.data, fun = is.null, recursive = FALSE) {
  if(recursive) {
    .data <- lapply(.data, function(.item) {
      if(is.list(.item)) list.clean(.item, fun, TRUE)
      else .item
    })
  }
  .data[vapply(.data,fun,logical(1L))] <- NULL
  .data
}

## recursively create nested list with dmax layers and 50% NULL elements
f <- function(len, d, dmax) {
  x <- vector(mode = "list", length = len)
  for(i in seq_along(x)) {
    if(d + 1 < dmax) {
      x[[i]] <- Recall(len, d + 1, dmax)
    } else {
      x[[i]] <- list(1, NULL)
    }
  }
  return(x)
}

## long shallow list (3 layers, total 5e5 nodes)
x_long <- f(len = 500, d = 1, dmax = 3)

microbenchmark::microbenchmark(
  rmNull = rmNull(x_long),
  list.clean = list.clean(x_long, recursive = TRUE),
  rrapply = rrapply(x_long, condition = Negate(is.null), how = "prune"),
  check = "equal",
  times = 5L
)
#> Unit: milliseconds
#>        expr       min        lq      mean    median        uq       max
#>      rmNull 2381.5536 2535.6871 2559.4045 2546.0375 2571.9462 2761.7982
#>  list.clean 1954.4046 1973.7983 2012.2158 2010.7334 2049.8020 2072.3409
#>     rrapply  288.5784  297.9041  382.3111  301.3147  460.5107  563.2475

## deeply nested list (18 layers, total 2^18 nodes)
x_deep <- f(len = 2, d = 1, dmax = 18)

microbenchmark::microbenchmark(
  rmNull = rmNull(x_deep),
  list.clean = list.clean(x_deep, recursive = TRUE),
  rrapply = rrapply(x_deep, condition = Negate(is.null), how = "prune"),
  check = "equal",
  times = 5L
)
#> Unit: milliseconds
#>        expr       min        lq      mean    median       uq       max
#>      rmNull 2306.5788 2360.2663 2422.2578 2367.9296 2530.201 2546.3135
#>  list.clean 1708.1192 1829.1303 2014.2162 2157.2148 2180.023 2196.5937
#>     rrapply  174.5385  187.9491  271.4967  200.9263  206.739  587.3306

【讨论】:

    【解决方案2】:

    这可以递归完成:

    rmNull <- function(x) {
       x <- Filter(Negate(is.null), x)
       lapply(x, function(x) if (is.list(x)) rmNull(x) else x)
    }
    l2 <- rmNull(l1)
    

    给予:

    > str(l2)
    List of 3
     $ : num 1
     $ : num 2
     $ :List of 2
      ..$ : num 3
      ..$ :List of 1
      .. ..$ : num 4
    

    【讨论】:

    • 你应该使用Recall来编写递归函数。这样,函数的名称就不会被编码到其中,如果您将其分配给其他名称,则会破坏它。 ?Recall。除了它在 *apply 函数中不起作用,它在那里说。所以不要使用它。请勿阅读此评论。
    • 感谢您的解决方案。但性能似乎无法接受:对于一个最多包含 3 个级别的 400,000 个元素的列表,list.clean 花费 0.18 秒,但 rmNull 在我的 PC 上花费 3.6 秒。
    • 请注意,这给出的结果与list(NULL,list(NULL)) 上的list.clean 不同
    • 如果指定recursive = TRUE,则给出相同的答案。
    • 这很好。一个非常具有挑战性的问题。很好的答案。
    猜你喜欢
    • 2017-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-12
    相关资源
    最近更新 更多