2017-12-02 109 views
8

Jest kilka pytań na ten temat, ale nie widzę żadnego ogólnego rozwiązania: Mam głęboko rekursywną listę i chcę spłaszczyć ją do pojedynczej listy wszystkich elementów nie znajdujących się na liście. zawarte.Spłaszczyć listę rekursywną

Na przykład, wziąć tego pod listę:

d = list(
    list(
    list(
     iris[sample(1:150,3),], 
     iris[sample(1:150,3),] 
    ), 
    list(
     list(
     iris[sample(1:150,3),], 
     list(
      iris[sample(1:150,3),], 
      iris[sample(1:150,3),] 
     ) 
    ) 
    ) 
) 
) 

i przekształcić go w ten sposób:

list(iris[sample(1:150,3),], 
    iris[sample(1:150,3),], 
    iris[sample(1:150,3),], 
    iris[sample(1:150,3),], 
    iris[sample(1:150,3),]) 

Próbowałem kilka z następujących czynności w oparciu o inne rozwiązania:

purrr::flatten(d) 
plyr::llply(d, unlist) 
lapply(d, unlist, use.names=FALSE) 

Żaden nie osiąga pożądanego rezultatu, który w przykładzie ma pojedynczą długość listy 5, a wszystkie pozycje to data.frame. Wszelkie sugestie doceniane.

+3

_technically_ w 'data.frame' jest w' list' ;-) – hrbrmstr

+0

że wspomniane, że b zawiera pierwotne/c '* apply' (w tym 'rapply') w związku z tym, dlaczego nie są one skuteczne w rekurencyjnym wyodrębnianiu danych.frame-y, tak jak to – hrbrmstr

+0

@hrbrmstr _practically_ it's not;) – geotheory

Odpowiedz

8

Jest to ogólna funkcja spłaszczenia przy użyciu tylko zasady R:

flatten <- function(x) { 
    if (!inherits(x, "list")) return(list(x)) 
    else return(unlist(c(lapply(x, flatten)), recursive = FALSE)) 
} 

Wynik:

flatten(d) 
#[[1]] 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#44   5.0   3.5   1.6   0.6  setosa 
#138   6.4   3.1   5.5   1.8 virginica 
#87   6.7   3.1   4.7   1.5 versicolor 
# 
#[[2]] 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#19   5.7   3.8   1.7   0.3  setosa 
#1   5.1   3.5   1.4   0.2  setosa 
#71   5.9   3.2   4.8   1.8 versicolor 
# 
#[[3]] 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#31   4.8   3.1   1.6   0.2  setosa 
#98   6.2   2.9   4.3   1.3 versicolor 
#134   6.3   2.8   5.1   1.5 virginica 
# 
#[[4]] 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#140   6.9   3.1   5.4   2.1 virginica 
#119   7.7   2.6   6.9   2.3 virginica 
#57   6.3   3.3   4.7   1.6 versicolor 
# 
#[[5]] 
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#73   6.3   2.5   4.9   1.5 versicolor 
#54   5.5   2.3   4.0   1.3 versicolor 
#146   6.7   3.0   5.2   2.3 virginica 

Podobnie:

x <- list(list("A"), list(list("A"), list("A"))) 
flatten(x) 
#[[1]] 
#[1] "A" 
# 
#[[2]] 
#[1] "A" 
# 
#[[3]] 
#[1] "A" 

x <- list(list(1), list(list(2), list(3))) 
flatten(x) 
#[[1]] 
#[1] 1 
# 
#[[2]] 
#[1] 2 
# 
#[[3]] 
#[1] 3 

Wydaje się nieco zaokrąglone, aby dodać więcej list, gdy celem jest ich usunięcie, ale trasa list/unlist jest jedynym niezawodnym sposobem łączenia list z różnymi numerami elementów.

3

swoje dane:

d <- list(
    list(
    list(
     iris[sample(1:150,3),], 
     iris[sample(1:150,3),] 
    ), 
    list(
     list(
     iris[sample(1:150,3),], 
     list(
      iris[sample(1:150,3),], 
      iris[sample(1:150,3),] 
     ) 
    ) 
    ) 
) 
) 

pierwsze, surowy, ale skuteczna funkcja:

f <- function(x) { 

    if (is.null(x)) return 
    n <- length(x) 

    if (length(n) == 0) return 

    for (i in 1:n) { 

    if (is.data.frame(x[[i]])) { 
     res <<- append(res, list(x[[i]])) 
    } else { 
     if (is.list(x[[i]])) { 
     f(x[[i]]) 
     } 
    } 

    } 

} 

Surowy ale skuteczna zmienna globalna towarzyszące:

res <- list() 

f(d) 

Wyniki:

res 
## [[1]] 
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
## 37   5.5   3.5   1.3   0.2 setosa 
## 16   5.7   4.4   1.5   0.4 setosa 
## 8   5.0   3.4   1.5   0.2 setosa 
## 
## [[2]] 
##  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
## 10   4.9   3.1   1.5   0.1  setosa 
## 141   6.7   3.1   5.6   2.4 virginica 
## 86   6.0   3.4   4.5   1.6 versicolor 
## 
## [[3]] 
##  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
## 134   6.3   2.8   5.1   1.5 virginica 
## 40   5.1   3.4   1.5   0.2 setosa 
## 3   4.7   3.2   1.3   0.2 setosa 
## 
## [[4]] 
##  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
## 33   5.2   4.1   1.5   0.1  setosa 
## 132   7.9   3.8   6.4   2.0 virginica 
## 76   6.6   3.0   4.4   1.4 versicolor 
## 
## [[5]] 
##  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
## 10   4.9   3.1   1.5   0.1 setosa 
## 16   5.7   4.4   1.5   0.4 setosa 
## 135   6.1   2.6   5.6   1.4 virginica 

mniej surowego (nie zmienna globalna) roztworu:

f2 <- function(x) { 

    res <- list() 

    if (is.null(x)) return 
    n <- length(x) 

    if (length(n) == 0) return 

    for (i in 1:n) { 
    if (is.data.frame(x[[i]])) { 
     res <- append(res, list(x[[i]])) 
    } else { 
     if (is.list(x[[i]])) res <- append(res, f2(x[[i]])) 
    } 
    } 

    return(res) 

} 

f2(d) 
## same output 
+0

Dzięki. Dla bardziej ogólnego rozwiązania zmieniłbym test 'is.data.frame' na'! Class (x [[i]]) [1] == 'list''. Nieco zaskakujące jest to, że nie ma w tym uporządkowanego rozwiązania (np. 'Broom' /' purrr'). – geotheory

3

rlang::squash dość magicznym:

set.seed(47) 

d = list(list(list(iris[sample(1:150,3),], 
        iris[sample(1:150,3),]), 
       list(list(iris[sample(1:150,3),], 
         list(iris[sample(1:150,3),], 
          iris[sample(1:150,3),]) 
      )) 
)) 

rlang::squash(d) 
#> [[1]] 
#>  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#> 147   6.3   2.5   5.0   1.9 virginica 
#> 56   5.7   2.8   4.5   1.3 versicolor 
#> 113   6.8   3.0   5.5   2.1 virginica 
#> 
#> [[2]] 
#>  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#> 124   6.3   2.7   4.9   1.8 virginica 
#> 86   6.0   3.4   4.5   1.6 versicolor 
#> 103   7.1   3.0   5.9   2.1 virginica 
#> 
#> [[3]] 
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#> 59   6.6   2.9   4.6   1.3 versicolor 
#> 70   5.6   2.5   3.9   1.1 versicolor 
#> 81   5.5   2.4   3.8   1.1 versicolor 
#> 
#> [[4]] 
#>  Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#> 139   6.0   3.0   4.8   1.8 virginica 
#> 21   5.4   3.4   1.7   0.2 setosa 
#> 104   6.3   2.9   5.6   1.8 virginica 
#> 
#> [[5]] 
#> Sepal.Length Sepal.Width Petal.Length Petal.Width Species 
#> 25   4.8   3.4   1.9   0.2  setosa 
#> 90   5.5   2.5   4.0   1.3 versicolor 
#> 75   6.4   2.9   4.3   1.3 versicolor