В R как отфильтровать списки списков? - PullRequest
6 голосов
/ 02 августа 2011

Согласно руководству, Фильтр работает с векторами, а также со списками, например ::100100

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 <1, и без z $ z3, потому что он пуст. </p>

Ответы [ 3 ]

3 голосов
/ 14 декабря 2012

Я думаю, вы должны использовать:

Filter(function(x){length(x)>0 && x[["b"]] > 1},z)

Предикат (функция, которую вы используете для фильтрации z) применяется к элементам z, а не к их индексам.

1 голос
/ 02 августа 2011

Я никогда не использовал 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 не приставали к numeric(0).И, очевидно, я сделал пример, используя только вашу простую функцию, а не более сложную функцию, которую вы использовали во втором примере.

0 голосов
/ 02 августа 2011

Здесь нет претензий к красоте, и поиск глубины не выполняется:

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'> 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) }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...