如何在igraph中为平行边添加边属性(保留平行边)
问题背景
我有一个带平行边的igraph图,邻接矩阵如下:
> as_adjacency_matrix(g) 45 x 45 sparse Matrix of class "dgCMatrix" [[ suppressing 45 column names … … …... ]] [[ suppressing 45 column names … … …... ]] 1 . . . 1 . . . . . . . . . . . . . . 1 1 2 1 2 1 1 . . . . . . . . . . . . . . . . 2 . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 2 . . . . . . . . . . . . . . . . . 3 . . . 1 . . . . . . . . . . . . . . . . . . 1 . 1 . 2 . . . . . . . . . . . . . . . . . 4 1 . 1 . . . . . . . . . . . 2 . . . . . . . . . . . . . 1 1 . . . . . . . . . . . . . . 5 . . . . . . . . . . . . . . 2 . . . . . . . 1 . . . . . . 1 . . . . . . . . . . . . . . . 6 . . . . . . . . . . . . . . . . . . . . . 1 . . . . . . . . 1 . . . . . . . . . . . . . . 7 . . . . . . . . . . . . . . . . . . . . . . . . . 1 . . . . . 1 1 . 1 1 . . . . . . . . . 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . 1 . . . . . . . . . 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 . . 1 . . . . . 1 1 . . . . . . . 10 . . . . . . . . . . . . . . . . . . . . . . . . . 2 . . . . . . . . . 2 . . . . . . . . . 11 . . . . . . . . . . . . . . . . . . . . . . 1 . . . . 1 . . . . . . . . . . . . . . . . .
我需要给图中的边添加属性,同一节点对之间的所有平行边属性值相同,待添加的属性存储在和邻接矩阵维度一致的edge_attr_mat矩阵中:
> edge_attr_mat 1 2 3 4 5 6 7 8 9 10 11 12 1 0.00 0.0 0.0 68299.332 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 2 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 3 0.00 0.0 0.0 102016.916 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 4 68299.33 0.0 102016.9 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 5 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 6 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 7 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 8 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 9 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 10 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 11 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 12 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 13 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 14 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 15 0.00 0.0 0.0 380794.817 154931.3 0.00 0.000 0.000 0.00 0.000 0.00 0.00 16 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 17 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 18 0.00 0.0 0.0 0.000 0.0 0.00 0.000 0.000 0.00 0.000 0.00 0.00 ....
我试过用这个矩阵重新构建带权图:
g<-graph_from_adjacency_matrix(edge_attr_mat, mode=c("undirected"), diag = F, weighted = T)
但这种方法会丢失平行边,同一节点对的平行边被合并成了单条边。想请教有没有不用遍历每个条目,通过函数或矩阵操作就能给所有边(包括平行边)添加属性的方法?
更新:小型测试数据集
为了方便验证,我准备了一个小型的代表性数据集:
g <- make_empty_graph(n = 4) %>% add_edges(c(1,2, 2,4, 3,4, 1,3, 1,4)) %>% set_edge_attr("type", value = "friend") %>% add_edges(c(1,3, 2,4), type = "col") g <- as.undirected(g, mode = "each") V(g)$name <-c ("A", "B", "C", "D") edge_attr_mat<-matrix(data=c(0,1.1,2.12,1.2, 1.1,0,0,2.3, 2.12,0,0,1.6, 1.2,2.3,1.6,0), nrow = 4) # 这是要添加的新边属性,同一节点对的所有平行边属性值相同
解决方案
针对你的需求,我提供两种高效的方法,不用手动遍历每条边,直接通过矩阵索引就能批量给平行边添加属性,同时保留原有图的结构和属性:
方法一:利用节点数字索引匹配属性
因为你的edge_attr_mat是按节点顺序排列的,我们可以先把所有边转换成节点索引的列表,再直接用这个列表去索引属性矩阵:
# 获取所有边的起点和终点数字索引(无向图中边的顺序不影响取值) edge_indices <- as_edgelist(g, names = FALSE) # 从属性矩阵中提取对应的值,同一节点对的平行边会自动取到相同值 new_attr <- edge_attr_mat[edge_indices] # 给图添加新的边属性,比如命名为"similarity" g <- set_edge_attr(g, "similarity", value = new_attr)
验证一下效果:
# 查看A-C之间的两条平行边 E(g)[V(g)$name == "A" %--% V(g)$name == "C"] # 它们的similarity属性都应该是2.12,同时保留原有的type属性
方法二:利用节点自定义名称匹配(更直观)
如果你的节点有自定义名称(比如示例中的"A","B","C","D"),可以先给属性矩阵添加对应的行名和列名,再通过边的节点名称提取值:
# 给属性矩阵添加节点名称,和图中的节点名称对应 rownames(edge_attr_mat) <- V(g)$name colnames(edge_attr_mat) <- V(g)$name # 获取所有边的节点名称对 edge_names <- as_edgelist(g, names = TRUE) # 提取对应属性值 new_attr <- edge_attr_mat[edge_names] # 添加属性到图中 g <- set_edge_attr(g, "similarity", value = new_attr)
这种方法不需要关心节点的数字索引,更适合节点名称有业务含义的场景,同样能保证同一节点对的平行边属性值一致。
方法原理
这两种方法的核心是利用as_edgelist()函数把图中所有边(包括平行边)转换成二维列表,然后直接用这个列表去索引属性矩阵。因为同一节点对的平行边在边列表中会重复出现,所以它们会自动匹配到矩阵中同一个位置的值,完美实现“同一节点对所有平行边属性值相同”的需求,而且效率很高,完全不需要手动遍历每条边。
另外,这两种方法都不会修改原有图的结构,所有平行边和已有的边属性(比如示例中的type)都会被完整保留。
内容的提问来源于stack exchange,提问作者DotPi

