如何在R中同时对三个字段进行网络分析
问题描述
我需要在R中同时对三个字段执行网络分析,以下是我的示例数据,最后一列为预期输出desired_output:
df <- data.frame( stringsAsFactors = FALSE, id_1 = c("ABC","ABC","BCD", "CDE","DEF","EFG","GHI","HIJ","IJK","JKL", "GHI","KLM","LMN","MNO","NOP"), id_2 = c("1A","2A","3A", "1A","4A","5A","6A","8A","9A","10A","7A", "12A","13A","14A","15A"), id_3 = c("Z3","Z2","Z1", "Z4","Z1","Z5","Z5","Z6","Z7","Z8","Z6","Z8", "Z9","Z9","Z1"), Name = c("StackOverflow1", "StackOverflow2","StackOverflow3","StackOverflow4", "StackOverflow5","StackOverflow6", "StackOverflow7","StackOverflow8","StackOverflow9", "StackOverflow10","StackOverflow11","StackOverflow12", "StackOverflow13","StackOverflow14","StackOverflow15"), desired_output = c(1L,1L,2L,1L,2L, 3L,3L,3L,4L,5L,3L,5L,6L,6L,2L) ) df #> id_1 id_2 id_3 Name desired_output #> 1 ABC 1A Z3 StackOverflow1 1 #> 2 ABC 2A Z2 StackOverflow2 1 #> 3 BCD 3A Z1 StackOverflow3 2 #> 4 CDE 1A Z4 StackOverflow4 1 #> 5 DEF 4A Z1 StackOverflow5 2 #> 6 EFG 5A Z5 StackOverflow6 3 #> 7 GHI 6A Z5 StackOverflow7 3 #> 8 HIJ 8A Z6 StackOverflow8 3 #> 9 IJK 9A Z7 StackOverflow9 4 #> 10 JKL 10A Z8 StackOverflow10 5 #> 11 GHI 7A Z6 StackOverflow11 3 #> 12 KLM 12A Z8 StackOverflow12 5 #> 13 LMN 13A Z9 StackOverflow13 6 #> 14 MNO 14A Z9 StackOverflow14 6 #> 15 NOP 15A Z1 StackOverflow15 2
目前我可以使用igraph包同时对两个字段做网络分析,但无法直接扩展到三个字段的场景,当前采用两次迭代的方法实现了需求,但认为该方案还有优化空间,现有实现代码如下:
library(igraph) library(tidyverse) graph.data.frame(df) %>% components() %>% pluck(membership) %>% stack() %>% set_names(c('GRP', 'id_1')) %>% right_join(df %>% mutate(id_1 = as.factor(id_1)), by = c('id_1')) %>% select(GRP, id_3) %>% graph.data.frame() %>% components() %>% pluck(membership) %>% stack() %>% set_names(c('GRP', 'id_3')) %>% right_join(df %>% mutate(id_3 = as.factor(id_3)), by = c('id_3')) #> GRP id_3 id_1 id_2 Name desired_output #> 1 1 Z3 ABC 1A StackOverflow1 1 #> 2 1 Z2 ABC 2A StackOverflow2 1 #> 3 2 Z1 BCD 3A StackOverflow3 2 #> 4 2 Z1 DEF 4A StackOverflow5 2 #> 5 2 Z1 NOP 15A StackOverflow15 2 #> 6 1 Z4 CDE 1A StackOverflow4 1 #> 7 3 Z5 EFG 5A StackOverflow6 3 #> 8 3 Z5 GHI 6A StackOverflow7 3 #> 9 3 Z6 HIJ 8A StackOverflow8 3 #> 10 3 Z6 GHI 7A StackOverflow11 3 #> 11 4 Z7 IJK 9A StackOverflow9 4 #> 12 5 Z8 JKL 10A StackOverflow10 5 #> 13 5 Z8 KLM 12A StackOverflow12 5 #> 14 6 Z9 LMN 13A StackOverflow13 6 #> 15 6 Z9 MNO 14A StackOverflow14 6
优化方案
核心思路是直接将每行三个字段的两两关联关系统一生成边列表,单次建图计算连通分量即可完成匹配,无需多次迭代,同时天然支持更多关联字段的扩展。
library(igraph) library(tidyverse) # 生成三个字段的两两无向边,去重避免冗余 edge_list <- df %>% select(id_1, id_2, id_3) %>% rowwise() %>% summarise( from = c(id_1, id_1, id_2), to = c(id_2, id_3, id_3), .groups = "drop" ) %>% distinct() # 构建无向图,计算连通分量 g <- graph_from_data_frame(edge_list, directed = FALSE) group_membership <- components(g)$membership # 将分组结果匹配回原数据集 df_result <- df %>% rowwise() %>% mutate(GRP = group_membership[unique(c(id_1, id_2, id_3))][1]) %>% ungroup()
输出结果与预期的desired_output完全一致,相比多次迭代的方案逻辑更简洁,性能更稳定,也可以直接扩展到3个以上字段的关联分组场景。
内容的提问来源于stack exchange,提问作者AnilGoyal
相关产品推荐
相关产品推荐

