首页 > 解决方案 > R - 从另一个列表上的操作填充一个空列表

问题描述

我正在编写一个 R 程序来分析树结构。在下面的示例中,树中有 10 个节点,每个节点的祖先(该节点的父节点,以及该节点的父节点的父节点等)都存储在一个名为 Ancestors 的列表中。用户将查询节点名称的向量,我正在尝试创建一个列表,该列表将填充该查询的祖先。列表中的每个项目都将包含每个被调用祖先的查询后代列表。请参阅下面的示例

假设我有以下结构。

在此处输入图像描述 所以祖先列表看起来像这样

Ancestors <- list()
Ancestors$'p1' <- c('p2', 'p3', 'p4', 'p5', 'p8', 'p9', 'p10')
Ancestors$'p2' <- c('p4', 'p5', 'p8', 'p9', 'p10')
Ancestors$'p3' <- c('p4', 'p5', 'p9', 'p10')
Ancestors$'p4' <- c('p5', 'p9', 'p10')
Ancestors$'p5' <- c('p9', 'p10')
Ancestors$'p6' <- c('p4', 'p5', 'p9', 'p10')
Ancestors$'p7' <- c('p5', 'p9', 'p10')
Ancestors$'p8' <- c('p5', 'p9', 'p10')
Ancestors$'p9' <- NA
Ancestors$'p10' <- NA

假设查询是

query <- c('p5', 'p4', 'p1')

那么我想要制作的清单是

# lst <- list()
# 
# lst$'p2'
#   'p1'
# lst$'p3'
#   'p1'
# lst$'p4'
#   'p1'
# lst$'p5'
#   'p1', 'p4'
# lst$'p8'
#   'p1'
# lst$'p9'
#   'p1', 'p4', 'p5'
# lst$'p10'
#   'p1', 'p4', 'p5'

(2,3,4,5,8,9,10) 是查询词的所有祖先。这就是我想做的清单。然后对于每个命名项目,我想列出作为列表项目后代的查询词列表。我很抱歉这个令人困惑的例子。我希望这是有道理的。

这是我到目前为止尝试过的

lst <- list()

lapply(query, function(x) {
  theAncestors <- Ancestors[[x]]
  sapply(theAncestors, function(y) {
    lst[[y]][[1]] <- c(lst[[y]][[1]], x)
  })
})

但这不会填充列表 lst。所发生的只是它打印出来

[[1]]
  p9  p10 
"p5" "p5" 

[[2]]
  p5   p9  p10 
"p4" "p4" "p4" 

[[3]]
  p2   p3   p4   p5   p8   p9  p10 
"p1" "p1" "p1" "p1" "p1" "p1" "p1" 

这与我想要的有点不同。另外,当我尝试输出 lst 时,它仍然是空的。所以这段代码甚至不会影响 lst。那么我怎样才能得到我想要的输出呢?我曾想过使用 for 循环,但我认为这些在 R 中非常慢。我的实际问题可能会有 100 或 1000 个查询词和更多的祖先词。所以 lst 会很长。所以我认为 for 循环可能行不通。

编辑:我想通了。我的代码现在是:

lst <- list()

aLst <- unlist(lapply(query, function(x) {
  theAncestors <- Ancestors[[x]]
  sapply(theAncestors, function(y) {
    lst[[y]][1] <- c(lst[[y]][[1]], x)
  })
}))
aLst <- split(unname(aLst), names(aLst))

这打印出来

$p10
[1] "p5" "p4" "p1"

$p2
[1] "p1"

$p3
[1] "p1"

$p4
[1] "p1"

$p5
[1] "p4" "p1"

$p8
[1] "p1"

$p9
[1] "p5" "p4" "p1"

这就是我想要的

标签: rlistlapply

解决方案


它只是打印的原因是你lapply没有被分配给任何东西。它不填充的原因lst稍微复杂一些,并且与函数范围有关 - 这里有一个非常详细的解释:http: //adv-r.had.co.nz/Environments.html#function-envs

要点是 lst 没有被修改 - 它的副本正在函数中被修改,但它在函数完成调用后被丢弃的环境中被修改。有几种方法可以解决这个问题 - 第一种是使用<<-而不是<-. 这个“深度赋值”运算符看起来比函数范围之外的内容更深,<-并且会修改函数范围之外的内容。

第二个是我认为以不同的方式解决您的问题 - 列出您的Ancestors清单,query您可以首先执行以下操作:

query_members <- Ancestors[query]

query_members

# $`p4`
# [1] "p5"  "p9"  "p10"

# $p5
# [1] "p9"  "p10"

# $p1
# [1] "p2"  "p3"  "p4"  "p5"  "p8"  "p9"  "p10"

子集到您想要的元素。您现在需要在某种意义上“反转”它。首先,获取查询成员的唯一祖先:

query_ancestors <- sort(unique(unlist(query_members)))

query_ancestors

# [1] "p10" "p2"  "p3"  "p4"  "p5"  "p8"  "p9" 

现在你有了一些你可以做的东西lapply,因为它与你想要的输出具有相同的结构。您只需要回答“对于每个祖先,哪个查询成员是后代?”这个问题。

所以你可以写一个小函数,比如:

get_descendants <- function(query_ancestor, query_members) {

  sort(names(Filter(function(x) { query_ancestor %in% x }, query_members)))

}

# test it out
get_descendants("p5", query_members)

# [1] "p1" "p4"

现在我们可以lapply使用我们的query_ancestors

lst <- lapply(query_ancestors, get_descendants, query_members = query_members)
names(lst) <- query_ancestors
lst

# $`p10`
# [1] "p1" "p4" "p5"

# $p2
# [1] "p1"

# $p3
# [1] "p1"

# $p4
# [1] "p1"

# $p5
# [1] "p1" "p4"

# $p8
# [1] "p1"

# $p9
# [1] "p1" "p4" "p5"

把它们放在一起,你可以编写一个很好的函数来包装所有这些,让你专注于查询和祖先列表:

list_ancestors <- function(query, Ancestors) {

  query_members <- Ancestors[query]
  query_ancestors <- sort(unique(unlist(query_members)))

  lst <- lapply(query_ancestors, function(element, members) {
    sort(names(Filter(function(x) element %in% x, members)))
  }, members = query_members)

  names(lst) <- query_ancestors

  lst

}

# so for example with just p7
list_ancestors("p7", Ancestors)

# $`p10`
# [1] "p7"

# $p5
# [1] "p7"

# $p9
# [1] "p7"

希望这可以帮助!


推荐阅读