[[
, [[<-
, sb2_rec, and sb2_recin,
can perform recursive subset operations on a nested list.
Such recursive subset operations only operate on a single element.
Performing recursive subset operations on multiple elements is not vectorized,
and requires a (potentially slow) loop.
The lst_untree()
function takes a nested tree-like list,
and turns it into a recursive matrix (a matrix of list-elements),
allowing vectorized subset operations to be performed on the nested list. lst_untree()
can also simply flatten the list, making it a non-nested list.
See the Examples section to understand how the list will be arranged and named.
The lst_nlists()
counts the total number of recursive list-elements inside a list.
Arguments
- x
a tree-like nested list.
- margin
a single integer, indicating how the result should be arranged:
margin = 0
produces a simple flattened recursive vector (i.e. list) without dimensions.margin = 1
produces a recursive matrix (i.e. a matrix of list-elements),
withlength(x)
rows andn
columns,
wheren = sapply(x,
lst_nlists) |> max()
.
Empty elements will be filled withlist(NULL)
.margin = 2
produces a recursive matrix (i.e. a matrix of list-elements),
withlength(x)
columns andn
rows,
wheren = sapply(x,
lst_nlists) |> max()
.
Empty elements will be filled withlist(NULL)
.
- use.names
Boolean, indicating if the result should be named.
See section "use.names" for more information.
Value
For lst_untree()
:
A non-nested (dimensional) list.
Note that if margin = 1
or margin = 2
, lst_untree()
returns a recursive matrix
(i.e. a recursive array with 2 dimensions),
not a data.frame.
To turn a nested list into a data.frame instead, one option would be to use:
rrapply(x, how = "melt")
For lst_nlists()
:
A single integer,
giving the total number of recursive list-elements in the given list.
use.names
margin = 0
and use.names = TRUE
If margin = 0
and use.names = TRUE
,
every element in the flattened list will be named.
Names of nested elements, such as x[["A"]][["B"]][["C"]]
,
will become "A.B.C"
,
as that is the behaviour of the rapply function
(which lst_untree()
calls internally).
It is therefore advised not to use dots ("."
) in your list names,
and use underscores ("_"
) instead,
before calling lst_untree()
.
See the rrapply::
rrapply function
for renaming
(and other forms of transforming)
recursive subsets of lists. margin = 1
and use.names = TRUE
If margin == 1
and use.names = TRUE
,
the rows of resulting recursive matrix will be equal to names(x)
,
but recursive names will not be assigned. margin = 2
and use.names = TRUE
If margin == 2
and use.names = TRUE
,
the columns of resulting recursive matrix will be equal to names(x)
,
but recursive names will not be assigned. use.names = FALSE
If use.names = FALSE
, the result will not have any names assigned at all.
Examples
# show-casing how the list-elements are arranged and named ====
x <- list(
A = list(
A = list(A = "AAA", B = "AAB"),
A = list(A = "AA2A", B = "AA2B"),
B = list(A = "ABA", B = "ABB"),
C = letters
),
Y = list(
Z = list(Z = "YZZ", Y = "YZY"),
Y = list(Z = "YYZ", Y = "YYY"),
X = "YX"
)
)
# un-tree column-wise:
sapply(x, lst_nlists) |> max() # number of rows `y` will have
#> [1] 7
y <- lst_untree(x, margin = 2L, use.names = TRUE)
dim(y)
#> [1] 7 2
print(y)
#> A Y
#> [1,] "AAA" "YZZ"
#> [2,] "AAB" "YZY"
#> [3,] "AA2A" "YYZ"
#> [4,] "AA2B" "YYY"
#> [5,] "ABA" "YX"
#> [6,] "ABB" NULL
#> [7,] character,26 NULL
sb2_x(y, n(1:3, 1:2), 1:ndims(y)) # vectorized selection of multiple recursive elements
#> A Y
#> [1,] "AAA" "YZZ"
#> [2,] "AAB" "YZY"
#> [3,] "AA2A" "YYZ"
# un-tree row-wise:
sapply(x, lst_nlists) |> max() # number of columns `y` will have
#> [1] 7
y <- lst_untree(x, margin = 1L, use.names = TRUE)
dim(y)
#> [1] 2 7
print(y)
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7]
#> A "AAA" "AAB" "AA2A" "AA2B" "ABA" "ABB" character,26
#> Y "YZZ" "YZY" "YYZ" "YYY" "YX" NULL NULL
sb2_x(y, n(1:2, 1:3), 1:ndims(y)) # vectorized selection of multiple recursive elements
#> [,1] [,2] [,3]
#> A "AAA" "AAB" "AA2A"
#> Y "YZZ" "YZY" "YYZ"
# simple flattened list:
y <- lst_untree(x, margin = 0, use.names = TRUE)
print(y)
#> $A.A.A
#> [1] "AAA"
#>
#> $A.A.B
#> [1] "AAB"
#>
#> $A.A.A
#> [1] "AA2A"
#>
#> $A.A.B
#> [1] "AA2B"
#>
#> $A.B.A
#> [1] "ABA"
#>
#> $A.B.B
#> [1] "ABB"
#>
#> $A.C
#> [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
#> [20] "t" "u" "v" "w" "x" "y" "z"
#>
#> $Y.Z.Z
#> [1] "YZZ"
#>
#> $Y.Z.Y
#> [1] "YZY"
#>
#> $Y.Y.Z
#> [1] "YYZ"
#>
#> $Y.Y.Y
#> [1] "YYY"
#>
#> $Y.X
#> [1] "YX"
#>
y[["Y.Z.Y"]]
#> [1] "YZY"
x[[c("Y", "Z", "Y")]] # equivalent in the original list
#> [1] "YZY"
################################################################################
# showcasing that only list-elements are recursively flattened ====
# i.e. atomic vectors in recursive subsets remain atomic
x <- lapply(1:10, \(x)list(sample(letters), sample(1:10)))
sapply(x, lst_nlists) |> max()
#> [1] 2
y <- lst_untree(x, margin = 1)
dim(y)
#> [1] 10 2
print(y)
#> [,1] [,2]
#> [1,] character,26 integer,10
#> [2,] character,26 integer,10
#> [3,] character,26 integer,10
#> [4,] character,26 integer,10
#> [5,] character,26 integer,10
#> [6,] character,26 integer,10
#> [7,] character,26 integer,10
#> [8,] character,26 integer,10
#> [9,] character,26 integer,10
#> [10,] character,26 integer,10
lst_untree(x, margin = 1)
#> [,1] [,2]
#> [1,] character,26 integer,10
#> [2,] character,26 integer,10
#> [3,] character,26 integer,10
#> [4,] character,26 integer,10
#> [5,] character,26 integer,10
#> [6,] character,26 integer,10
#> [7,] character,26 integer,10
#> [8,] character,26 integer,10
#> [9,] character,26 integer,10
#> [10,] character,26 integer,10
################################################################################
# showcasing vectorized sub-setting ====
x <- lapply(1:10, \(x) list(
list(sample(letters[1:10]), sample(LETTERS[1:10])),
list(sample(month.abb), sample(month.name)),
list(sample(1:10), rnorm(10))
))
y <- lst_untree(x, 1)
# getting the first recursive elements in the second level/depth in base R:
for(i in seq_along(x)) {
x[[c(i, c(1L, 1L))]] |> print() # for-loop, slow
}
#> [1] "d" "h" "j" "e" "i" "c" "a" "g" "b" "f"
#> [1] "j" "i" "f" "d" "b" "a" "e" "g" "h" "c"
#> [1] "a" "b" "j" "c" "f" "h" "i" "d" "g" "e"
#> [1] "g" "j" "i" "f" "e" "b" "d" "h" "c" "a"
#> [1] "h" "g" "f" "c" "d" "i" "a" "j" "b" "e"
#> [1] "g" "f" "b" "i" "a" "c" "d" "e" "j" "h"
#> [1] "e" "d" "c" "a" "f" "b" "g" "h" "i" "j"
#> [1] "h" "i" "f" "c" "a" "e" "d" "j" "g" "b"
#> [1] "h" "a" "d" "i" "g" "e" "b" "j" "c" "f"
#> [1] "e" "d" "i" "b" "a" "c" "j" "h" "g" "f"
# the same, but vectorized using the untree'd list:
sb2_x(y, n(1:nrow(y), 1L), 1:ndims(y)) |> drop() |> print() # vectorized, fast
#> [[1]]
#> [1] "d" "h" "j" "e" "i" "c" "a" "g" "b" "f"
#>
#> [[2]]
#> [1] "j" "i" "f" "d" "b" "a" "e" "g" "h" "c"
#>
#> [[3]]
#> [1] "a" "b" "j" "c" "f" "h" "i" "d" "g" "e"
#>
#> [[4]]
#> [1] "g" "j" "i" "f" "e" "b" "d" "h" "c" "a"
#>
#> [[5]]
#> [1] "h" "g" "f" "c" "d" "i" "a" "j" "b" "e"
#>
#> [[6]]
#> [1] "g" "f" "b" "i" "a" "c" "d" "e" "j" "h"
#>
#> [[7]]
#> [1] "e" "d" "c" "a" "f" "b" "g" "h" "i" "j"
#>
#> [[8]]
#> [1] "h" "i" "f" "c" "a" "e" "d" "j" "g" "b"
#>
#> [[9]]
#> [1] "h" "a" "d" "i" "g" "e" "b" "j" "c" "f"
#>
#> [[10]]
#> [1] "e" "d" "i" "b" "a" "c" "j" "h" "g" "f"
#>