3、Python图计算库入门:NetworkX基础操作、节点与边的增删改查、图的可视化

好,咱们正式开始动手了。

前面聊了那么多图论的概念,什么节点、边、邻接矩阵,听着挺唬人。但说白了,这些东西在Python里,就是几行代码的事。我个人习惯用NetworkX这个库,它是我做图计算时的首选工具。为什么?因为它简单、直观,而且功能强大到能覆盖我工作中90%的需求。

今天这一章,我就带你手把手把NetworkX跑起来。咱们从安装开始,到创建图、增删节点和边,再到把图画出来看。嗯,每一步我都会穿插一些我在实际项目中踩过的坑,你注意听。

3.1 安装与导入:别小看这一步

安装NetworkX非常简单。你只需要打开终端,敲一行命令:

pip install networkx

如果你用的是Anaconda环境,也可以用:

conda install networkx

我个人建议用pip,因为版本更新更快。我在做基金持仓分析项目时,就遇到过因为conda源里的NetworkX版本太旧,导致某些新功能用不了的情况。后来换成pip安装,问题就解决了。

安装完成后,在Python脚本里导入它:

import networkx as nx

通常我还会带上matplotlib,因为后面要画图:

import matplotlib.pyplot as plt
小提示: 如果你在Jupyter Notebook里运行,记得加上 %matplotlib inline,这样图才能直接显示在单元格下方。

3.2 创建图:空图、有向图、无向图

NetworkX里最核心的概念就是图(Graph)。你可以把它想象成一个空容器,等着你往里塞节点和边。

创建一个空的无向图:

G = nx.Graph()

创建一个空的有向图:

DG = nx.DiGraph()

有向图和无向图的区别,你想想看——在基金持仓分析里,如果A基金持有B公司的股票,那这个关系是单向的(A→B),就应该用有向图。如果只是表示两家公司有业务往来,不分谁指向谁,那就用无向图。

我刚开始做图分析时,经常搞混这个。有一次把基金持仓关系建成了无向图,结果分析出来的社区结构完全不对。后来排查了半天才发现是图类型选错了。嗯,这个坑我替你踩过了。

3.3 节点操作:增、删、改、查

图建好了,接下来就是往里加节点。节点可以是一个数字、一个字符串,甚至是一个元组。NetworkX很灵活,几乎任何可哈希的对象都能当节点。

3.3.1 添加节点

# 添加单个节点
G.add_node("易方达基金")

# 添加多个节点
G.add_nodes_from(["华夏基金", "嘉实基金", "广发基金"])

# 添加带属性的节点
G.add_node("招商基金", 规模=500, 成立年份=2002)

节点属性这个功能非常实用。我在做基金持仓分析时,会把基金的规模、成立年份、基金经理等信息都作为属性存进去。这样后面做筛选和聚类时,直接按属性过滤就行,不用再查数据库。

3.3.2 删除节点

# 删除单个节点
G.remove_node("广发基金")

# 删除多个节点
G.remove_nodes_from(["华夏基金", "嘉实基金"])
注意: 删除节点时,与该节点相连的所有边也会被自动删除。我曾经在项目中不小心删了一个关键节点,结果整个图的结构都变了,花了半天才恢复。所以删除前最好先确认一下。

3.3.3 修改节点属性

# 修改已有节点的属性
G.nodes["易方达基金"]["规模"] = 600

# 或者直接覆盖
G.add_node("易方达基金", 规模=600, 成立年份=2001)

这里有个小技巧:add_node如果节点已存在,不会报错,而是更新属性。所以你可以放心地用它来修改节点信息。

3.3.4 查询节点

# 查看所有节点
print(G.nodes())

# 查看节点数量
print(G.number_of_nodes())

# 查看节点是否存在
print(G.has_node("易方达基金"))

# 查看节点属性
print(G.nodes["易方达基金"])

这些查询操作在分析大规模图时非常常用。比如你想知道某个基金是否在图中,或者想遍历所有节点做统计,这些方法就是你的基本工具。

3.4 边操作:增、删、改、查

节点是图的骨架,边才是图的灵魂。没有边,节点就是孤立的点,谈不上什么图分析。

3.4.1 添加边

# 添加单条边
G.add_edge("易方达基金", "贵州茅台")

# 添加多条边
edges = [("易方达基金", "五粮液"), ("华夏基金", "贵州茅台"), ("嘉实基金", "宁德时代")]
G.add_edges_from(edges)

# 添加带权重的边
G.add_edge("易方达基金", "贵州茅台", weight=0.15)

权重这个属性,在基金持仓分析里就是持仓比例。比如易方达基金持有贵州茅台15%的仓位,那这条边的weight就是0.15。后面做图算法时,权重会直接影响计算结果。

3.4.2 删除边

# 删除单条边
G.remove_edge("易方达基金", "五粮液")

# 删除多条边
G.remove_edges_from([("华夏基金", "贵州茅台"), ("嘉实基金", "宁德时代")])
避坑指南: 我曾经在删除边时,不小心传了一个不存在的边,结果程序直接报错。所以删除前最好先用 G.has_edge(u, v) 检查一下边是否存在。

3.4.3 修改边属性

# 修改边的权重
G.edges["易方达基金", "贵州茅台"]["weight"] = 0.20

# 或者用add_edge覆盖
G.add_edge("易方达基金", "贵州茅台", weight=0.20)

3.4.4 查询边

# 查看所有边
print(G.edges())

# 查看边数量
print(G.number_of_edges())

# 查看边是否存在
print(G.has_edge("易方达基金", "贵州茅台"))

# 查看边的属性
print(G.edges["易方达基金", "贵州茅台"])

3.5 图的可视化:一图胜千言

数据在代码里是冷冰冰的,但画出来就不一样了。NetworkX自带的可视化功能虽然不算华丽,但足够我们用。

3.5.1 基础绘图

# 绘制图
nx.draw(G, with_labels=True)
plt.show()

就这么简单。你会看到节点和边被画在画布上,节点上还带着标签。

3.5.2 自定义布局

NetworkX提供了多种布局算法,用来决定节点在画布上的位置。我个人最常用的是spring布局,它模拟了物理弹簧的力学效果,让图看起来更自然。

# 使用spring布局
pos = nx.spring_layout(G, seed=42)
nx.draw(G, pos, with_labels=True, node_color='lightblue', edge_color='gray')
plt.show()

你还可以试试circular_layout(圆形布局)、kamada_kawai_layout(力导向布局)等。不同的布局适合不同的场景。比如展示基金与股票的关系,用spring布局能清晰看出聚类结构。

3.5.3 带权重的可视化

# 根据权重调整边的粗细
weights = [G[u][v]['weight'] * 10 for u, v in G.edges()]
nx.draw(G, pos, with_labels=True, width=weights, node_color='lightgreen')
plt.show()

这样,持仓比例高的边就会更粗,一眼就能看出哪些是重仓股。

核心要点: 可视化不是为了好看,而是为了发现数据中的模式。我在分析基金持仓时,经常通过可视化发现一些异常连接——比如某只基金突然重仓了一只冷门股,这往往意味着有内幕消息或者策略调整。

3.6 本章知识体系

下面这张图,是我用SVG画的本章知识结构。你可以把它当作一个快速索引,看看今天都学了什么。

NetworkX 基础操作知识体系 安装与导入 pip install networkx 创建图 Graph / DiGraph 可视化 nx.draw / 布局算法 节点操作 • 添加: add_node / add_nodes_from • 删除: remove_node • 修改: 直接赋值属性 边操作 • 添加: add_edge / add_edges_from • 删除: remove_edge • 权重: weight 属性 查询操作 nodes() / edges() / has_node() / has_edge() / 属性访问

好了,这一章的内容就到这里。你跟着我把代码敲一遍,应该就能掌握NetworkX的基本操作了。下一章咱们会深入图算法,用这些基础操作去解决实际问题。


公众号:蓝海资料掘金营,微信deep3321