MultiDiGraph 是一个非常强大的数据结构,它属于有向多重图,为了完全理解它,我们把它拆分成三个关键词:

- 图:由一组顶点和连接这些顶点的边组成的网络结构。
- 有向:边是有方向的,如果从节点 A 到节点 B 有一条有向边,我们只能从 A 到达 B,而不能直接从 B 到达 A,在 Python 的
networkx库中,这通常用元组(u, v)表示,u是起点,v是终点。 - 多重:在任意两个节点之间,可以存在多条边,这与普通的“简单图”(Simple Graph)不同,简单图中两个节点之间最多只能有一条边。
| 特性 | 描述 |
|---|---|
| 节点 | 图中的基本元素,可以代表任何对象(如人、城市、单词等)。 |
| 边 | 连接两个节点的线段,有方向。 |
| 多重边 | 关键特性:允许两个节点之间存在多条方向相同的边,每条边都是独立的。 |
主要应用场景
MultiDiGraph 非常适合那些需要用多条、有方向的连接来建模复杂关系的场景。
- 交通网络:从一个城市(节点 A)到另一个城市(节点 B),可以有多条不同的航线(多条边),每条航线都有其特定的航班号和时刻表。
- 工作流:在一个工作流程中,一个任务(节点 A)完成后,可以触发两个并行的后续任务(节点 B 和 C),或者,两个不同的前置任务(节点 A 和 D)都可以触发同一个后续任务(节点 B)。
- 数据流图:在机器学习模型中,一个特征(节点 A)可能通过两个不同的预处理步骤(两条边)影响最终的预测(节点 B)。
- 社交网络:用户 A 可以给用户 B 发送多条私信(多条边),或者用户 A 和 B 之间可以有多个不同类型的互动(如“点赞”、“评论”、“转发”)。
- 化学反应:一种反应物(节点 A)可以在不同催化剂(不同边)的作用下,生成同一种产物(节点 B)。
Python 实现 (使用 networkx 库)
在 Python 中,最常用和最强大的图论库是 networkx,它提供了 MultiDiGraph 类。
安装 networkx
如果你还没有安装,请先安装它:
pip install networkx
创建和基本操作
import networkx as nx
import matplotlib.pyplot as plt
# 1. 创建一个空的 MultiDiGraph
G = nx.MultiDiGraph()
# 2. 添加节点
# 节点可以是任何可哈希的 Python 对象,如字符串、数字
G.add_node("A")
G.add_node("B")
G.add_node("C")
# 3. 添加边
# 这是 MultiDiGraph 的核心:可以在相同的 (u, v) 对上添加多条边
# networkx 会自动为它们分配一个唯一的键,通常是整数
G.add_edge("A", "B") # 第一条从 A 到 B 的边
G.add_edge("A", "B") # 第二条从 A 到 B 的边
G.add_edge("B", "C") # 第一条从 B 到 C 的边
G.add_edge("A", "C") # 一条从 A 直接到 C 的边
# 4. 添加带属性的边
# 你可以为每条边添加一个字典来存储其属性,如 'weight', 'label', 'flight_id' 等
G.add_edge("B", "C", weight=5, label="fast_route")
G.add_edge("B", "C", weight=1, label="scenic_route")
# 5. 查看图的信息
print(f"节点列表: {list(G.nodes())}")
print(f"边列表: {list(G.edges())}")
print("-" * 20)
# 6. 访问特定的边
# 对于多重边,G.edges() 会返回所有边,包括重复的
print("所有边(包括多重边):")
for u, v, key in G.edges(keys=True):
# key 是 networkx 为多重边分配的唯一标识符
edge_data = G.edges[u, v, key]
print(f" 边: {u} -> {v}, 键: {key}, 属性: {edge_data}")
print("-" * 20)
# 7. 获取两个节点之间的所有边
# 使用 G.get_edge_data(u, v) 返回一个字典,键是边的 key,值是边的属性
edges_between_b_and_c = G.get_edge_data("B", "C")
print("从 B 到 C 的所有边及其属性:")
for key, data in edges_between_b_and_c.items():
print(f" 键: {key}, 属性: {data}")
print("-" * 20)
# 8. 获取节点的入度和出度
# - 出度: 从该节点出发的边的数量(包括所有多重边)
# - 入度: 指向该节点的边的数量(包括所有多重边)
print("节点度数:")
print(f" 节点 A: 出度={G.out_degree('A')}, 入度={G.in_degree('A')}")
print(f" 节点 B: 出度={G.out_degree('B')}, 入度={G.in_degree('B')}")
print(f" 节点 C: 出度={G.out_degree('C')}, 入度={G.in_degree('C')}")
可视化
networkx 可以与 matplotlib 结合来可视化图,对于 MultiDiGraph,可视化时所有边都会被画出。

# 使用 matplotlib 绘制图形
plt.figure(figsize=(8, 6))
pos = nx.spring_layout(G, seed=42) # 为节点设置一个固定的布局,使每次运行结果一致
# 绘制节点
nx.draw_networkx_nodes(G, pos, node_size=700, node_color='skyblue')
# 绘制边
# 由于边有方向,我们使用箭头
nx.draw_networkx_edges(G, pos, edge_color='gray',
arrowstyle='->', arrowsize=20,
connectionstyle='arc3,rad=0.1') # 使用弧形让重叠的边更清晰
# 绘制节点标签
nx.draw_networkx_labels(G, pos, font_size=12, font_family='sans-serif')
# 绘制边标签(显示边的权重)
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
"MultiDiGraph 示例")
plt.axis('off') # 关闭坐标轴
plt.show()
运行上面的代码,你会看到一个图形,其中从 A 到 B 有两条线,从 B 到 C 有两条线,直观地展示了“多重”的概念。
MultiDiGraph vs. 其他图类型
| 图类型 | 有向? | 多重边? | 何时使用 |
|---|---|---|---|
Graph |
否 | 否 | 最简单的图,用于无向、无重复连接的关系,如社交好友网络(无向)、分子结构(单键)。 |
DiGraph |
是 | 否 | 有向图,用于有方向、但两点间最多只有一条连接的关系,如网页链接(有向)、单向关注关系。 |
MultiGraph |
否 | 是 | 无向多重图,用于无向、但可有多条连接的关系,如两个城市间的多条公路。 |
MultiDiGraph |
是 | 是 | 有向多重图,用于最复杂的关系,即有方向且可有多条连接,如本文所述的交通、工作流等场景。 |
MultiDiGraph 是 networkx 库中一个功能强大的数据结构,专门用于表示和处理有向多重图,当你需要建模的系统或数据集具有以下特征时,它就是理想的选择:
- 关系有方向性(A 影响 B,但 B 不一定影响 A)。
- 节点对之间可以存在多个独立的关系(从 A 到 B 有三条不同的航线)。
通过为每条边添加属性字典,你可以进一步丰富模型的信息量,使其能够承载更复杂的业务逻辑和数据。

