如何用Python或R绘制固定层级的多部图
多层层级图绘制实现方案
问题背景
我有两个带层级关联的邻接矩阵:
- 矩阵a:行是a类节点,列是b类节点,表示a类节点指向b类节点
- 矩阵b:行是b类节点(与a的列对应),列是c类节点,表示b类节点指向c类节点
给出的Python列表形式邻接矩阵:
import numpy as np import pandas as pd import networkx as nx a_net= [[1,1,0],[0,0,0]] b_net = [[1,0],[1,1],[0,0]]
转换为DataFrame的代码:
data_a = pd.DataFrame(a_net) data_a.index = ['a1','a2'] data_a.columns = ['b1','b2','b3'] data_b = pd.DataFrame(b_net) data_b.index = data_a.columns data_b.columns = ['c1','c2']
我自行编写了合并为单个邻接矩阵的代码:
# 合并为单个邻接矩阵 for i in range(1,len(data_a.columns)+1): data_b['b'+str(i)] = [0]*len(data_b) data_a['c1'] = [0]*len(data_a) data_a['c2'] = [0]*len(data_a) adj = pd.concat([data_a,data_b]) adj['a1'] = [0]*len(adj) adj['a2'] = [0]*len(adj) c1c2 = pd.DataFrame([[0,0,0,0,0,0,0],[0,0,0,0,0,0,0]]) c1c2.index = ['c1','c2'] c1c2.columns = adj.columns adj = pd.concat([adj,c1c2]) adj
需求:绘制多层图,要求a开头节点处于第一层,b开头节点处于第二层,c开头节点处于第三层,同时显示无连接的节点(如a2、b3)。可提供Python或R语言实现方法,也可通过以下代码导出表格到R:
data_a.to_csv("a_net.csv") data_b.to_csv("b_net.csv")
Python实现方案
1. 构建多层有向图
无需手动合并邻接矩阵,直接通过NetworkX构建图结构,添加所有节点和有效边:
import numpy as np import pandas as pd import networkx as nx import matplotlib.pyplot as plt # 初始化有向图 G = nx.DiGraph() # 添加所有节点,并标记层级 a_nodes = ['a1', 'a2'] b_nodes = ['b1', 'b2', 'b3'] c_nodes = ['c1', 'c2'] G.add_nodes_from(a_nodes, layer=0) G.add_nodes_from(b_nodes, layer=1) G.add_nodes_from(c_nodes, layer=2) # 添加a->b的边 for a in data_a.index: for b in data_a.columns: if data_a.loc[a, b] == 1: G.add_edge(a, b) # 添加b->c的边 for b in data_b.index: for c in data_b.columns: if data_b.loc[b, c] == 1: G.add_edge(b, c)
2. 设置分层节点位置
手动定义每个节点的坐标,确保同一层级节点在同一竖线上:
# 分层位置定义 pos = { # 第一层(a类) 'a1': (0, 1), 'a2': (0, 0), # 第二层(b类) 'b1': (1, 2), 'b2': (1, 1), 'b3': (1, 0), # 第三层(c类) 'c1': (2, 1), 'c2': (2, 0) }
3. 绘制分层图
plt.figure(figsize=(8, 4)) # 按层级设置节点颜色 node_colors = [ '#1f77b4' if node.startswith('a') else '#ff7f0e' if node.startswith('b') else '#2ca02c' for node in G.nodes() ] # 绘制节点、边和标签 nx.draw_networkx_nodes(G, pos, node_size=1500, node_color=node_colors, edgecolors='black') nx.draw_networkx_edges(G, pos, arrowstyle='->', arrowsize=20, edge_color='gray') nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold') # 隐藏坐标轴并展示 plt.axis('off') plt.tight_layout() plt.show()
R语言实现方案
1. 导入数据并构建图结构
library(igraph) # 导入数据 data_a <- read.csv("a_net.csv", row.names = 1) data_b <- read.csv("b_net.csv", row.names = 1) # 整理a->b的边列表 edges_ab <- which(data_a == 1, arr.ind = TRUE) edges_ab <- data.frame(from = rownames(data_a)[edges_ab[,1]], to = colnames(data_a)[edges_ab[,2]]) # 整理b->c的边列表 edges_bc <- which(data_b == 1, arr.ind = TRUE) edges_bc <- data.frame(from = rownames(data_b)[edges_bc[,1]], to = colnames(data_b)[edges_bc[,2]]) # 合并所有边并创建有向图 all_edges <- rbind(edges_ab, edges_bc) g <- graph_from_data_frame(all_edges, directed = TRUE) # 添加所有节点(包括孤立节点) all_nodes <- c(rownames(data_a), colnames(data_a), colnames(data_b)) g <- add_vertices(g, length(setdiff(all_nodes, V(g)$name)), attr = list(name = setdiff(all_nodes, V(g)$name)))
2. 设置分层布局
# 标记节点层级 V(g)$layer <- ifelse(grepl("^a", V(g)$name), 1, ifelse(grepl("^b", V(g)$name), 2, 3)) # 手动设置分层坐标 pos <- data.frame( x = V(g)$layer, y = ave(1:length(V(g)$name), V(g)$layer, FUN = function(x) seq(length(x), 1)) ) rownames(pos) <- V(g)$name
3. 绘制分层图
plot(g, layout = as.matrix(pos), vertex.size = 30, vertex.color = ifelse(V(g)$layer == 1, "#1f77b4", ifelse(V(g)$layer == 2, "#ff7f0e", "#2ca02c")), vertex.label = V(g)$name, vertex.label.cex = 1.2, edge.arrow.size = 0.5, edge.color = "gray", frame = FALSE)
内容的提问来源于stack exchange,提问作者Himan
相关产品推荐
相关产品推荐

