根据手册,Filter适用于矢量,它恰好也适用于列表,例如:
z <- list(a=1, b=2, c=3)
Filter(function(i){
z[[i]] > 1
}, z)
$b
[1] 2
$c
[1] 3
但是,它不适用于列表列表,例如:
z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1), z3=list())
Filter(function(i){
if(length(z[[i]])>0){
if(z[[i]]$b > 1)
TRUE
else
FALSE
}
else
FALSE
}, z)
Error in z[[i]] : invalid subscript type 'list'
在不使用嵌套循环的情况下过滤列表列表的最佳方法是什么?它也可以是列表清单......
(我尝试使用嵌套lapply,但无法使其工作。)
第二个例子中的编辑,这是我想要获得的:
list(z1=list(a=1,b=2,c=3))
即没有z $ z2,因为z $ z2 $ b&lt; 1,没有z $ z3,因为它是空的。
答案 0 :(得分:3)
我认为你应该使用:
Filter(function(x){length(x)>0 && x[["b"]] > 1},z)
谓词(用于过滤z的函数)适用于z的元素,而不是它们的索引。
答案 1 :(得分:1)
我在你的问题之前从未使用Filter
,所以这对早上的第一件事来说是一个很好的练习:)
至少有一些事情正在使你绊倒(我想)。
让我们从你的第一个简单的匿名函数开始,但让它独立,这样更容易阅读:
f <- function(i){
z[[i]] > 1
}
它应该向你跳出这个函数接受一个参数i
,但它在函数中调用z
。这不是很好的“功能性”编程:)
首先将该功能更改为:
f <- function(i){
i > 1
}
您会看到Filter
实际上会针对列表列表运行:
z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1))
Filter( f, z)
但它返回:
> Filter( f, z)
$z2
$z2$a
[1] 1
$z2$b
[1] 1
$z2$c
[1] 1
$<NA>
NULL
这不是你想要的。老实说,我不能理解为什么它会返回结果,也许有人可以向我解释。
当他说应该有一个递归的解决方案时,@ DWin正在咆哮着正确的树。我在递归函数上进行了第一次尝试,但你需要改进它:fancyFilter <- function(f, x){
if ( is.list( x[[1]] ) ) #only testing the first element... bad practice
lapply( x, fancyFilter, f=f ) #recursion FTW!!
else
return( lapply(x, Filter, f=f ) )
}
fancyFilter
查看传递给它的x
的第一个元素,如果该元素是一个列表,它会在列表的每个元素上递归调用fancyFilter
。但是如果元素#2不是列表呢?这是你应该测试的东西,并梳理出它对你来说是否重要。但是fancyFilter
的结果似乎与你所追求的一样:
> fancyFilter(f, z)
$z1
$z1$a
numeric(0)
$z1$b
[1] 2
$z1$c
[1] 3
$z2
$z2$a
numeric(0)
$z2$b
numeric(0)
$z2$c
numeric(0)
您可能需要添加一些逻辑来清理输出,以便FALSE
结果不会被{0}}骚扰。而且,很明显,我只使用了您的简单函数,而不是您在第二个示例中使用的更复杂的函数。
答案 2 :(得分:0)
这里没有对美的要求,也没有进行深度搜索:
z2 <- lapply(z, function(x){ if( "b" %in% names(x) && x[["b"]] >1 ) x else {} } )
z2[unlist(lapply(z2, is.null))] <- NULL
> z2
$z1
$z1$a
[1] 1
$z1$b
[1] 2
$z1$c
[1] 3
编辑:此代码将遍历列表并组合具有'b'&gt;的节点1.正确标记节点需要一些工作。首先是一个嵌套更深的列表:
z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1), z3=list(),
z4 = list(z5=list(a=5,b=6,c=7), z6=list(a=7,b=8,c=9)))
checkbGT1 <- function(ll){ root <- list()
for(i in seq_along(ll) ) {if ("b" %in% names(ll[[i]]) && ll[[i]]$b >1) {
root <- c(root, ll[[i]])
}else{
if( length(ll[[i]]) && is.list(ll[[i]]) )
{ root <- c(root, list(checkbGT1( ll[[i]] ))) }
}
}
return(root) }
答案 3 :(得分:0)
按键过滤子列表。 阅读有助于我的答案。
zall<-list(z1=list(list(key=1,b=2,c=3),list(key=2,b=3,c=4)))
zall
#> $z1
#> $z1[[1]]
#> $z1[[1]]$key
#> [1] 1
#>
#> $z1[[1]]$b
#> [1] 2
#>
#> $z1[[1]]$c
#> [1] 3
#>
#>
#> $z1[[2]]
#> $z1[[2]]$key
#> [1] 2
#>
#> $z1[[2]]$b
#> [1] 3
#>
#> $z1[[2]]$c
#> [1] 4
lapply(zall$z1, function(x){ x[intersect(names(x),"key")] } )
#> [[1]]
#> [[1]]$key
#> [1] 1
#>
#>
#> [[2]]
#> [[2]]$key
#> [1] 2
lapply(zall$z1, function(x){ x[setdiff(names(x),"key")] } )
#> [[1]]
#> [[1]]$b
#> [1] 2
#>
#> [[1]]$c
#> [1] 3
#>
#>
#> [[2]]
#> [[2]]$b
#> [1] 3
#>
#> [[2]]$c
#> [1] 4
答案 4 :(得分:0)
此问题的现代整洁解决方案是:
library(tidyverse)
z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1), z3=list())
然后简单地:
tibble(disc = z, Names = names(z)) %>%
hoist(disc, c = "c") %>%
filter(c == 3) %>%
unnest_wider(disc) %>%
split(.$Names) %>% map(select, -Names) %>%
map(as.list)
请注意,这现在非常灵活,可以轻松地进行其他过滤,例如如果a == 1