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
%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的基本操作了。下一章咱们会深入图算法,用这些基础操作去解决实际问题。
公众号:蓝海资料掘金营,微信deep3321