我正在编写一个R程序来分析树结构。在下面的示例中,树中有10个节点,每个节点的祖先(该节点的父级,以及该节点的父级的父级,等等)都存储在称为“祖先”的列表中。用户将查询节点名称的向量,而我正在尝试创建一个列表,该列表将填充该查询的祖先。列表中的每个项目都将包含所调用的每个祖先的查询后代的列表。请参见下面的示例
假设我具有以下结构。
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"
我想要的是什么
答案 0 :(得分:2)
仅打印的原因是您的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"
希望这会有所帮助!