R-从另一个列表的操作中填充一个空列表

时间:2019-06-18 18:31:23

标签: r list lapply

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

假设我具有以下结构。

enter image description here 因此,祖先列表看起来像这样

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的查询字词以及更多的祖先字词。所以第一会很长。因此,我认为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"

我想要的是什么

1 个答案:

答案 0 :(得分:2)

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

要点是,lst未被修改-它的一个副本正在函数中进行修改,但是在环境中被修改,该环境在函数完成调用后被丢弃。有几种解决方法-第一种方法是使用<<-而不是<-。这个“深度分配”运算符看起来比<-更深,将修改函数范围之外的内容。

第二个是我认为以不同的方式处理您的问题-您可以首先列出Ancestorsquery

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"

希望这会有所帮助!