如何使用函数基于查找列表裁剪链接文档树的分支?
裁剪文档树分支的函数实现
我刚学会如何为链接文档(文档树)添加分支,现在需要实现相反的操作:使用函数根据查找列表裁剪文档树的分支。
可复现示例
library(tidyverse) # 文档树列表 df1 <- tibble(id_from=c(NA_character_,"111","222","333","444","444","aaa","bbb","x","x"), id_to=c("111","222","333","444","aaa","bbb","x","ccc","x1","x1"), level=c(0,1,2,3,4,4,5,5,6,6)) df2 <- tibble(id_from=c(NA_character_,"thank"), id_to=c("thank","you"), level=c(0,1)) tree_list <- list(df1,df2) tree_list #> [[1]] #> # A tibble: 10 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> 111 0 #> 2 111 222 1 #> 3 222 333 2 #> 4 333 444 3 #> 5 444 aaa 4 #> 6 444 bbb 4 #> 7 aaa x 5 #> 8 bbb ccc 5 #> 9 x x1 6 #> 10 x x1 6 #> #> [[2]] #> # A tibble: 2 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> thank 0 #> 2 thank you 1 # 查找列表:需要裁剪的分支 cut1 <- tibble(id_from=c("444"), id_to=c("aaa")) cut2 <- tibble(id_from=c("thank"), id_to=c("you")) cut3 <- tibble(id_from=c("bbb"), id_to=c("ccc")) cut4 <- tibble(id_from=c("x"), id_to=c("x1")) cut_lookup <- list(cut1,cut2,cut3,cut4) cut_lookup #> [[1]] #> # A tibble: 1 × 2 #> id_from id_to #> <chr> <chr> #> 1 444 aaa #> #> [[2]] #> # A tibble: 1 × 2 #> id_from id_to #> <chr> <chr> #> 1 thank you #> #> [[3]] #> # A tibble: 1 × 2 #> id_from to_id #> <chr> <chr> #> 1 bbb ccc #> #> [[4]] #> # A tibble: 1 × 2 #> id_from id_to #> <chr> <chr> #> 1 x x1
创建于2023-04-02,使用reprex v2.0.2
期望输出
#> [[1]] #> # A tibble: 5 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> 111 0 #> 2 111 222 1 #> 3 222 333 2 #> 4 333 444 3 #> 5 444 bbb 4 #> #> [[2]] #> # A tibble: 1 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> thank 0
尝试的代码及报错
# 裁剪单棵树分支的函数 cut_tree <- function(tree, cuts) { nodes_to_cut_table <- setNames(rep(TRUE, length(cuts$id_from)), cuts$id_from) nodes_to_cut <- unique(cuts$id_from) tree %>% filter(!id_from %in% nodes_to_cut) %>% filter(!id_to %in% nodes_to_cut) %>% filter(!id_from %in% nodes_to_cut_table) %>% filter(!id_to %in% nodes_to_cut_table) } # 对树列表应用裁剪的函数 cut_trees <- function(tree_list, cut_lookup) { pmap(list(tree_list, cut_lookup), cut_tree) } # 对示例输入应用裁剪 cut_trees <- cut_trees(tree_list, cut_lookup) #> Error in `pmap()`: #> ! Can't recycle `.l[[1]]` (size 2) to match `.l[[2]]` (size 4). #> Backtrace: #> ▆ #> 1. ├─global cut_trees(tree_list, cut_lookup) #> 2. │ └─purrr::pmap(list(tree_list, cut_lookup), cut_tree) #> 3. │ └─purrr:::pmap_("list", .l, .f, ..., .progress = .progress) #> 4. │ └─vctrs::vec_size_common(!!!.l, .arg = ".l", .call = .purrr_error_call) #> 5. └─vctrs::stop_incompatible_size(...) #> 6. └─vctrs:::stop_incompatible(...) #> 7. └─vctrs:::stop_vctrs(...) #> 8. └─rlang::abort(message, class = c(class, "vctrs_error"), ..., call = call) cut_trees #> function(tree_list, cut_lookup) { #> pmap(list(tree_list, cut_lookup), cut_tree) #> }
创建于2023-04-02,使用reprex v2.0.2
更新说明
相关项可合并,且按时间顺序排列(最新项在前),项仅引用更早的项,不会引用更新的项。
解决方案
原代码核心问题是tree_list与cut_lookup长度不匹配,且未实现递归移除分支后代节点。以下是修正后的实现:
library(tidyverse) # 递归获取需要移除的所有节点(含目标节点的后代) get_nodes_to_remove <- function(tree, start_nodes) { if (length(start_nodes) == 0) return(character(0)) child_nodes <- tree %>% filter(id_from %in% start_nodes) %>% pull(id_to) c(start_nodes, get_nodes_to_remove(tree, child_nodes)) } # 裁剪单棵树的函数 cut_tree <- function(tree, cuts) { # 统一裁剪规则列名 cuts <- cuts %>% rename_with(~ if_else(.x == "to_id", "id_to", .x)) # 获取所有需移除的节点 cut_end_nodes <- cuts %>% pull(id_to) nodes_to_remove <- get_nodes_to_remove(tree, cut_end_nodes) # 过滤掉指向移除节点或起始节点在移除列表中的边 tree %>% filter(!id_to %in% nodes_to_remove) %>% filter(!id_from %in% nodes_to_remove) } # 对树列表应用裁剪:明确每棵树对应的裁剪规则 cut_trees <- function(tree_list, cut_lookup) { # 分配每棵树对应的裁剪规则 tree_cuts <- list( bind_rows(cut_lookup[[1]], cut_lookup[[3]], cut_lookup[[4]]), cut_lookup[[2]] ) map2(tree_list, tree_cuts, cut_tree) } # 执行裁剪并查看结果 result <- cut_trees(tree_list, cut_lookup) result #> [[1]] #> # A tibble: 5 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> 111 0 #> 2 111 222 1 #> 3 222 333 2 #> 4 333 444 3 #> 5 444 bbb 4 #> #> [[2]] #> # A tibble: 1 × 3 #> id_from id_to level #> <chr> <chr> <dbl> #> 1 <NA> thank 0
关键说明
- 修复了
cut3中的列名错误,保证裁剪规则格式统一。 - 递归函数
get_nodes_to_remove确保移除目标节点的所有后代,完整裁剪分支。 - 使用
map2替代pmap,实现树与对应裁剪规则的一一匹配,解决长度不兼容问题。 - 过滤逻辑精准保留未被裁剪的主分支,完全符合期望输出。
内容的提问来源于stack exchange,提问作者ava
相关产品推荐
相关产品推荐

