簡體   English   中英

如何在使用R保留結構的同時從嵌套列表創建所有組合?

[英]How to create all combinations from a nested list while preserving the structure using R?

給定一個嵌套列表,如何從其元素創建所有可能的列表,同時保留嵌套列表的結構?

嵌套列表:

l = list(
    a = list(
        b = 1:2
    ),
    c = list(
        d = list(
            e = 3:4,
            f = 5:6
        )
    ),
    g = 7
)

期望的輸出: l的元素的所有可能組合,同時保留結構,例如:

# One possible output:
list(
    a = list(
        b = 1
    ),
    c = list(
        d = list(
            e = 3,
            f = 5
        )
    ),
    g = 7
)

# Another possible output:
list(
    a = list(
        b = 1
    ),
    c = list(
        d = list(
            e = 4,
            f = 5
        )
    ),
    g = 7
)

到目前為止,我的方法是:

  1. 壓扁列表(例如,如本答案中所討論的)
  2. expand.grid()並獲得一個矩陣,其中每一行代表一個唯一的組合
  3. 解析結果矩陣的每一行,並使用正則表達式從names()重構結構

我正在尋找一種不那么繁瑣的方法,因為我無法保證列表元素的名稱不會改變。

utilsrelist函數似乎是為這個任務而設計的:

rl <- as.relistable(l)
r <- expand.grid(data.frame(rl), KEEP.OUT.ATTRS = F)
> head(r, 5)
   b c.d.e c.d.f g
1  1     3     5 7
2  2     3     5 7
3  1     4     5 7
4  2     4     5 7
5  1     3     6 7

它保存了列表( skeleton )的結構。 這意味着現在可以操作嵌套列表中的數據並將其重新分配到結構中( flesh )。 這里是擴展矩陣的第一行。

r <- rep(unname(unlist(r[1,])),each = 2)
l2 <- relist(r, skeleton = rl)
> l2
$a
$a$b
[1] 1 1


$c
$c$d
$c$d$e
[1] 3 3

$c$d$f
[1] 5 5



$g
[1] 7

attr(,"class")
[1] "relistable" "list"  

請注意,由於結構保持不變,我需要提供與原始列表中相同數量的元素。 這就是使用rep重復元素兩次的原因。 我想也可以用NA填充它。

對於每個可能的組合迭代r (擴展):

lapply(1:nrow(r), function(x) 
          relist(rep(unname(unlist(r[x,])),each = 2), skeleton = rl))

結合Ben Nutzer的精彩回答Joris Chau的精彩評論 ,答案將成為一個單線:

apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")) 

它創建一個列表列表,其中包含與expand.grid()返回的行數一樣多的元素。 結果通過str()的輸出更好地可視化:

str(apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")))
 List of 16 $ :List of 3 ..$ a:List of 1 .. ..$ b: num 1 ..$ c:List of 1 .. ..$ d:List of 2 .. .. ..$ e: num 3 .. .. ..$ f: num 5 ..$ g: num 7 $ :List of 3 ..$ a:List of 1 .. ..$ b: num 2 ..$ c:List of 1 .. ..$ d:List of 2 .. .. ..$ e: num 3 .. .. ..$ f: num 5 ..$ g: num 7 ... ... ... $ :List of 3 ..$ a:List of 1 .. ..$ b: num 2 ..$ c:List of 1 .. ..$ d:List of 2 .. .. ..$ e: num 4 .. .. ..$ f: num 6 ..$ g: num 7 

不相等的子列表長度

這是一種方法 - 在Uwe和Ben的答案上延伸 - 這也適用於任意子列表長度。 而不是在data.frame(l)上調用expand.grid ,首先將l展平為單級列表,然后在其上調用expand.grid

## skeleton
skel <- rapply(l, head, n = 1L, how = "list")

## flatten to single level list
l.flat <- vector("list", length = length(unlist(skel)))
i <- 0L

invisible(
    rapply(l, function(x) {
          i <<- i + 1L
          l.flat[[i]] <<- x
        })
)

## expand all list combinations 
l.expand <- apply(expand.grid(l.flat), 1L, relist, skeleton = skel)

str(l.expand)
#> List of 12
#>  $ :List of 3
#>   ..$ a:List of 1
#>   .. ..$ b: num 1
#>   ..$ c:List of 1
#>   .. ..$ d:List of 2
#>   .. .. ..$ e: num 3
#>   .. .. ..$ f: num 5
#>   ..$ g: num 7
#>  ...
#>  ...
#>  $ :List of 3
#>   ..$ a:List of 1
#>   .. ..$ b: num 2
#>   ..$ c:List of 1
#>   .. ..$ d:List of 2
#>   .. .. ..$ e: num 4
#>   .. .. ..$ f: num 7
#>   ..$ g: num 7

數據

我略微修改了數據結構,因此子列表組件ef的長度不等。

l <- list(
    a = list(
        b = 1:2
    ),
    c = list(
        d = list(
            e = 3:4,
            f = 5:7
        )
    ),
    g = 7
)

## calling data.frame on l does not work
data.frame(l)
#> Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : arguments imply differing number of rows: 2, 3

綜合Ben NutzerJoris Chau的精彩答案,我們可以從嵌套列表中創建所有可能的組合,無論某些子列表組件的長度是否不等。

放在一起作為一個功能:

list.combine <- function(input) {
    # Create list skeleton.
    skeleton <- rapply(input, head, n = 1, how = "list")

    # Create storage for the flattened list.
    flattened = list()

    # Flatten the list.
    invisible(rapply(input, function(x) {
        flattened <<- c(flattened, list(x))
    }))

    # Create all possible combinations from list elements.
    combinations <- expand.grid(flattened, stringsAsFactors = FALSE)

    # Create list for storing the output.
    output <- apply(combinations, 1, relist, skeleton = skeleton)

    return(output)
}

注意:如果子列表組件中存在字符類型,則所有內容都將強制轉換為字符。 例如:

# Input list.
l <- list(
    a = "string",
    b = list(
        c = 1:2,
        d = 3
    )
)

# Applying the function.
o <- list.combine(l)

# View the list:
str(o)

# List of 2
#  $ :List of 2
#   ..$ a: chr "string"
#   ..$ b:List of 2
#   .. ..$ c: chr "1"
#   .. ..$ d: chr "3"
#  $ :List of 2
#   ..$ a: chr "string"
#   ..$ b:List of 2
#   .. ..$ c: chr "2"
#   .. ..$ d: chr "3"

One-- --way圍繞這是relist的循環,這將維持在該數據中1x1數據幀。 df[, 1]訪問數據幀將給出原始類型的長度為1的向量作為輸入列表中的元素。 例如:

更新了list.combine()

list.combine <- function(input) {
    # Create list skeleton.
    skeleton <- rapply(input, head, n = 1, how = "list")

    # Create storage for the flattened list.
    flattened = list()

    # Flatten the list.
    invisible(rapply(input, function(x) {
        flattened <<- c(flattened, list(x))
    }))

    # Create all possible combinations from list elements.
    combinations <- expand.grid(flattened, stringsAsFactors = FALSE)

    # Create list for storing the output.
    output <- list()

    # Relist and preserve original data type.
    for (i in 1:nrow(combinations)) {
        output[[i]] <- retain.element.type(relist(flesh = combinations[i, ], skeleton = skeleton))
    }

    return(output)
}

然后retain.element.type()

retain.element.type <- function(input.list) {
    for (name in names(input.list)) {
        # If the element is a list, recall the function.
        if(inherits(input.list[[name]], "list")) {
            input.list[[name]] <- Recall(input.list[[name]])

        # Else, get the first element and preserve the type.
        } else {
            input.list[[name]] <- input.list[[name]][, 1]
        }
    }
    return(input.list)
}

例:

# Input list.
l <- list(
    a = "string",
    b = list(
        c = 1:2,
        d = 3
    )
)

# Applying the updated function to preserve the data type.
o <- list.combine(l)

# View the list:
str(o)

# List of 2
#  $ :List of 2
#   ..$ a: chr "string"
#   ..$ b:List of 2
#   .. ..$ c: int 1
#   .. ..$ d: num 3
#  $ :List of 2
#   ..$ a: chr "string"
#   ..$ b:List of 2
#   .. ..$ c: int 2
#   .. ..$ d: num 3

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM