图论基础与图数据表示:从定义到实战
说实话,很多做金融风控的朋友第一次接触图神经网络时,都会卡在图论基础这一关。我当年也一样,觉得「图」不就是画几个圈圈连几条线吗?后来在构建资金交易网络时才发现,这里面的门道比想象中深得多。
今天我们就来彻底搞懂图论基础。我会结合自己在金融项目中的踩坑经历,带你一步步掌握图的定义、表示方法,以及Python里最常用的NetworkX库。
图的定义:不只是点和线
图是什么?说白了,就是一组节点和一组边的集合。节点代表实体,边代表关系。
我在做反欺诈项目时,把每个用户账号当作节点,转账记录当作边。这样构建出来的图,能直观地看到资金流向和异常模式。
数学上,图用 G = (V, E) 表示。V是节点集合,E是边集合。就这么简单。
核心要点:图的本质是表达「谁和谁有关系」。在金融场景中,节点可以是账户、交易、设备,边可以是转账、登录、共享IP。
图的类型:有向、无向、加权
图不是千篇一律的。根据边的性质,我们可以把图分成几种类型。每种类型对应不同的金融场景。
无向图
边没有方向。A和B之间有边,就表示A和B互相连接。我最早做社交网络分析时用的就是无向图——朋友关系是双向的嘛。
有向图
边有方向。A→B和B→A是两回事。这在金融交易中太常见了:A转账给B,不代表B会转账给A。我有个项目专门分析洗钱网络,有向图帮了大忙——能追踪资金到底从哪流向哪。
加权图
每条边带一个权重。权重可以表示金额、频率、信任度等。举个例子,我在做信用卡欺诈检测时,把交易金额作为边的权重。大额交易往往更可疑。
| 图类型 | 特点 | 金融场景举例 |
|---|---|---|
| 无向图 | 边无方向 | 共同设备登录、社交关系 |
| 有向图 | 边有方向 | 转账、支付、资金流向 |
| 加权图 | 边带权重 | 交易金额、转账频次 |
图的表示:邻接矩阵与邻接表
图在计算机里怎么存?两种主流方式:邻接矩阵和邻接表。
邻接矩阵
用一个二维数组表示节点之间的连接关系。如果有N个节点,矩阵就是N×N的。A[i][j] = 1表示节点i和j相连,0表示不相连。
优点:直观,查两个节点是否相连很快。缺点:太费内存了。N=10000时,矩阵就有1亿个元素。我早期一个项目用邻接矩阵存用户关系图,结果内存直接爆了。
邻接表
每个节点维护一个列表,存它连接了哪些节点。稀疏图(边很少)用这种方式特别省内存。
我个人习惯:稠密图用邻接矩阵,稀疏图用邻接表。金融网络大多是稀疏的——一个用户不会和几万人有交易关系。
实战建议:在NetworkX中,你不需要手动选择存储方式。库会自动优化。但理解底层原理能帮你更好地调优性能。
度与路径:图的基本度量
度(Degree)是节点连接的边数。在有向图中,还分入度(指向该节点的边数)和出度(从该节点出发的边数)。
我在分析资金交易网络时,发现出度很高的节点往往是「资金中转站」,而入度很高的节点可能是「资金归集账户」。这些特征对反洗钱很有价值。
路径(Path)是节点序列,相邻节点之间有边相连。最短路径在金融网络中很有用——比如找两个账户之间的最短转账链路。
连通性与子图
连通性描述的是图是否「断开了」。如果任意两个节点之间都有路径,那这个图就是连通的。否则,它由多个连通分量组成。
我记得有一次做企业关联关系分析,发现一个看似独立的公司集群,通过子图分析才发现它们通过多层持股关系连在一起。这就是子图分析的价值。
子图(Subgraph)是从原图中取出一部分节点和边。在金融风控中,我们经常提取某个可疑账户的「邻居子图」来做局部分析。
知识体系总览
下面这张图帮你理清本章的核心逻辑:
Python中NetworkX库入门
理论说完了,来点实际的。NetworkX是Python最流行的图分析库。我几乎每个项目都用它。
安装与导入
pip install networkx
import networkx as nx
import matplotlib.pyplot as plt
创建图
# 创建空图
G = nx.Graph()
# 添加节点
G.add_node('Alice')
G.add_node('Bob')
G.add_node('Charlie')
# 添加边
G.add_edge('Alice', 'Bob')
G.add_edge('Bob', 'Charlie')
# 查看基本信息
print(f"节点数: {G.number_of_nodes()}")
print(f"边数: {G.number_of_edges()}")
创建有向图
DG = nx.DiGraph()
DG.add_edge('Alice', 'Bob', weight=1000) # 转账1000元
DG.add_edge('Bob', 'Charlie', weight=500)
基本分析
# 计算度
print(f"Alice的度: {G.degree('Alice')}")
# 计算最短路径
print(f"最短路径: {nx.shortest_path(G, 'Alice', 'Charlie')}")
# 检查连通性
print(f"是否连通: {nx.is_connected(G)}")
# 获取连通分量
print(f"连通分量数: {nx.number_connected_components(G)}")
注意:有向图和无向图的API略有不同。比如is_connected()只适用于无向图。有向图要用is_strongly_connected()或is_weakly_connected()。我曾经在这上面踩过坑,排查了半天才发现是图类型搞错了。
可视化
nx.draw(G, with_labels=True, node_color='lightblue',
node_size=500, font_size=12)
plt.show()
小技巧:当图很大时(比如几万个节点),直接可视化会变成一团乱麻。我一般先提取子图,或者用布局算法(如spring_layout)来优化显示效果。
实战:构建一个简单的金融交易图
我们来模拟一个资金交易网络。假设有5个账户,互相有转账记录。
# 创建有向加权图
tx_graph = nx.DiGraph()
# 添加交易记录 (发送方, 接收方, 金额)
transactions = [
('A', 'B', 1000),
('B', 'C', 500),
('A', 'C', 2000),
('C', 'D', 800),
('D', 'E', 1200),
('E', 'A', 300),
]
for sender, receiver, amount in transactions:
tx_graph.add_edge(sender, receiver, weight=amount)
# 分析
print("各账户出度(转出次数):")
for node in tx_graph.nodes():
out_deg = tx_graph.out_degree(node)
print(f" {node}: {out_deg}")
print("\n各账户入度(转入次数):")
for node in tx_graph.nodes():
in_deg = tx_graph.in_degree(node)
print(f" {node}: {in_deg}")
# 找资金循环
cycles = list(nx.simple_cycles(tx_graph))
print(f"\n资金循环路径: {cycles}")
你看,通过简单的度和路径分析,我们就能发现异常模式。比如A的出度很高,E的入度很高,这些都可能暗示着资金异常流动。
好了,图论基础就讲到这里。这些概念是后续学习图神经网络的基石。你可以在自己的数据上试试NetworkX,感受一下图分析的魅力。