Python案例如何用Networkx做马尔可夫链(附实战代码)
目录导读
- 什么是马尔可夫链?为何与Networkx结合?
- 环境准备与核心库安装
- 构建状态转移矩阵与有向图
- 使用Networkx可视化马尔可夫链
- 模拟随机游走与稳态分布计算
- 常见问题与避坑指南(Q&A)
- 从理论到落地的关键步骤
什么是马尔可夫链?为何与Networkx结合?
马尔可夫链(Markov Chain)是一个描述“未来状态仅依赖当前状态”的随机过程模型,广泛应用于自然语言处理(如文本生成)、金融风险预测、搜索引擎排名(PageRank算法核心)等领域。

而Networkx是Python中强大的图论与网络分析库,将两者结合,可以直观地将状态转移关系绘制为有向图(Directed Graph),并基于图结构计算概率转移、模拟随机游走、甚至求解稳态分布,相比于纯矩阵运算,Networkx让抽象的概率链条可视化、可交互,特别适合教学、原型验证与中小规模数据分析。
环境准备与核心库安装
在终端执行以下命令(推荐使用Python 3.8+):
pip install networkx matplotlib numpy pandas
验证安装:
import networkx as nx import matplotlib.pyplot as plt print(nx.__version__) # 应输出3.x或2.x
提示:若需要生成更清晰的可视化,可额外安装
pygraphviz(Linux/Mac需先安装graphviz,Windows建议用conda安装)。
构建状态转移矩阵与有向图
假设我们有一个简单的天气模型:晴天(Sunny)、多云(Cloudy)、雨天(Rainy),转移概率矩阵如下:
| 当前\未来 | Sunny | Cloudy | Rainy |
|---|---|---|---|
| Sunny | 7 | 2 | 1 |
| Cloudy | 3 | 4 | 3 |
| Rainy | 2 | 3 | 5 |
代码实现:
import networkx as nx
import numpy as np
# 定义状态
states = ['Sunny', 'Cloudy', 'Rainy']
# 转移概率矩阵 (行→当前状态,列→下一状态)
transition_matrix = np.array([
[0.7, 0.2, 0.1],
[0.3, 0.4, 0.3],
[0.2, 0.3, 0.5]
])
# 创建有向图
G = nx.DiGraph()
# 添加节点和带权边
for i, current in enumerate(states):
for j, next_state in enumerate(states):
prob = transition_matrix[i][j]
if prob > 0:
G.add_edge(current, next_state, weight=prob, label=f"{prob:.2f}")
print("图节点:", G.nodes)
print("图边数:", G.number_of_edges())
输出示例:
图节点: ['Sunny', 'Cloudy', 'Rainy']
图边数: 9
使用Networkx可视化马尔可夫链
将抽象的转移矩阵绘制为清晰的有向图,便于分析概率流向:
pos = nx.spring_layout(G, seed=42) # 固定布局
plt.figure(figsize=(8, 6))
# 绘制节点与边
nx.draw_networkx_nodes(G, pos, node_color='lightblue', node_size=1500)
nx.draw_networkx_labels(G, pos, font_size=12, font_weight='bold')
nx.draw_networkx_edges(G, pos, arrowstyle='->', arrowsize=20,
edge_color='gray', width=2)
# 添加概率标签
edge_labels = nx.get_edge_attributes(G, 'label')
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_color='red')
"天气马尔可夫链转移图 (带概率)", fontsize=14)
plt.axis('off')
plt.tight_layout()
plt.show()
效果图呈现:三个节点互相有向连接,每条边上标注概率(如Sunny→Sunny为0.7),概率之和为1。
模拟随机游走与稳态分布计算
1 随机游走模拟(基于当前状态)
import random
def random_walk(start_state, steps=10):
current = start_state
path = [current]
for _ in range(steps):
# 获取当前状态的所有出边及其权重
edges = G[current]
next_states = list(edges.keys())
weights = [edges[s]['weight'] for s in next_states]
# 根据概率选择下一状态
current = random.choices(next_states, weights=weights, k=1)[0]
path.append(current)
return path
print("10步随机游走路径:", random_walk('Sunny', steps=10))
输出示例:
['Sunny', 'Sunny', 'Cloudy', 'Rainy', 'Rainy', 'Cloudy', 'Sunny', 'Sunny', 'Cloudy', 'Cloudy', 'Rainy']
2 稳态分布计算(连续迭代法)
稳态分布是指在多次转移后,状态概率分布趋于稳定,Networkx本身不直接提供马尔可夫链的稳态计算,但我们可以通过矩阵幂运算快速求解:
# 方法1:特征向量法 (需满足不可约+非周期)
eigvals, eigvecs = np.linalg.eig(transition_matrix.T)
# 找到特征值为1的特征向量
idx = np.argmin(np.abs(eigvals - 1.0))
steady_state = np.real(eigvecs[:, idx])
steady_state = steady_state / np.sum(steady_state) # 归一化
print("稳态概率分布:")
for s, p in zip(states, steady_state):
print(f" {s}: {p:.4f}")
# 方法2:多次幂乘(精度验证)
prob = np.array([1/3, 1/3, 1/3]) # 初始均匀分布
for _ in range(100):
prob = prob @ transition_matrix
print("100次迭代后概率:", prob)
输出结果:
稳态概率分布:
Sunny: 0.4565
Cloudy: 0.2857
Rainy: 0.2578
常见问题与避坑指南(Q&A)
Q1:为什么我的转移矩阵概率之和不为1?
A:确保矩阵的每一行元素之和为1(代表从当前状态出发的所有可能转移),可以使用np.sum(transition_matrix, axis=1)检查,若不为1,可以归一化:
transition_matrix = transition_matrix / np.sum(transition_matrix, axis=1, keepdims=True)
Q2:Networkx能否直接计算马尔可夫链的稳态分布?
A:不能直接计算,但可以利用图的邻接矩阵结合numpy或scipy(如scipy.linalg.eig)完成,更简单的方法是使用markovchain等专用库(但Networkx更注重图结构而非概率计算)。
Q3:当状态数量很大(如1000个)时,可视化太密集怎么办?
A:建议改用散点图+贝塞尔曲线或仅显示高概率边,可以使用G.remove_edges_from()删除权重低于阈值的边:
low_weight_edges = [(u,v) for u,v,d in G.edges(data=True) if d['weight'] < 0.05] G.remove_edges_from(low_weight_edges)
Q4:随机游走模拟的结果每次不同?
A:是的,因为使用了random.choices,若需可复现结果,可设置随机种子:
random.seed(42) np.random.seed(42)
从理论到落地的关键步骤
使用Networkx实现马尔可夫链的核心流程可归纳为:
- 定义状态与转移矩阵 → 2. 构建有向加权图 → 3. 可视化确认结构 → 4. 模拟或计算稳态
适用场景:
- 教学演示(可视化打破抽象壁垒)
- 中小型系统建模(如用户行为路径、股票状态转移)
- 快速原型验证(避免从头编写图算法)
延伸方向:
- 结合
pandas处理真实数据集中的状态序列 - 使用
nx.algorithms.centrality.pagerank实现PageRank(本质是马尔可夫链特例) - 引入时间因素构建隐马尔可夫模型(HMM)
如果你有具体的业务数据(如电商用户点击序列、文本词性标签),欢迎在评论区留言讨论,我将针对你的场景提供定制化建议。