2. 图数据基础:节点、边、属性
做图特征工程,得先搞清楚图长什么样。你可能会想:「不就是点和线吗?」没错,但具体到团伙识别场景,每个「点」和每条「线」背后都藏着业务含义。我在项目里最常见的问题就是:大家把节点和属性混为一谈,最后特征全乱套了。
先給一个直观的框架:
2.1 节点:谁是「谁」
节点就是图里的一个主体。在团伙识别里,节点可以是人、手机号、银行卡、IP地址、设备ID……任何一个能作为实体的东西。我一开始犯过个错误——把「通话记录」也放进节点里。记住,行为是边,不是节点。
节点通常用唯一ID标记,比如身份证号、设备指纹。每个节点可以挂一堆属性,像年龄、性别、注册时间、历史风险分。这些属性后面会成为特征工程的核心原料。
技巧: 设计节点时,先想清楚「这个实体有没有独立参与团伙行为的能力」。一个人、一张卡可以,但一条短信内容不行——短信内容应该作为边的属性。
2.2 边:关系说了算
边就是节点之间的连接。无向边表示「双方对等」,比如互为好友;有向边表示「方向性」,比如转账(A→B),或资金归集(C→D)。团伙识别里,方向特别重要——钱流向上游还是下游,往往藏着组织层级。我曾经遇到一个案子,只看双向通话没发现异常,加上方向后发现所有通话都指向同一个人,头目立刻浮现。
边也可以有属性:通话时长、转账金额、见面地点经纬度、同设备登录次数……这些属性直接决定了边的「强度」或「风险权重」。
| 元素 | 团伙识别示例 | 常用属性 |
|---|---|---|
| 节点 | 人员、银行卡、手机、设备 | 年龄、性别、注册时间、风险分 |
| 边 | 通话、转账、见面、同设备登录 | 金额、次数、时长、IP段 |
2.3 属性:给数据和关系「贴标签」
属性分两类:节点属性和边属性。节点属性描述「谁是谁」,边属性描述「发生了什么」。做特征工程时,这两类属性要分开处理。我见过有人把通话金额加在节点上,结果多人共用一张卡时,金额归属全乱套了。
举个代码例子,用 NetworkX 快速构建一个团伙图:
import networkx as nx
G = nx.Graph()
# 添加节点,带上属性
G.add_node('A', age=32, risk_score=0.85)
G.add_node('B', age=28, risk_score=0.72)
G.add_node('头目X', role='组织者')
# 添加边,带上属性
G.add_edge('A', 'B', relation='通话', duration=120)
G.add_edge('A', '头目X', relation='见面', location='咖啡馆')
G.add_edge('B', '头目X', relation='转账', amount=5000)
print("节点数量:", G.number_of_nodes())
print("边数量:", G.number_of_edges())
print("节点A的属性:", G.nodes['A'])
避坑: 我曾经把节点ID写成中文姓名,结果数据量一大,重名导致图结构分裂。节点ID必须全局唯一,推荐使用哈希值或业务编码。
2.4 图的几种「变种」
图不是只有一种。根据业务需要,可能遇到:
- 有向图 vs 无向图:转账、上下级关系必须用有向图;好友、同行关系用无向图。
- 加权图 vs 无权图:通话次数、转账金额作为权重,比单纯的有无权更有信息量。
- 多重边:两个人既有通话又有转账,应该保留两条边,而不是合并成一条。合并会损失行为多样性。
- 异构图:节点类型不同(人、卡、设备),边类型也不同。团伙识别几乎都是异构图,同一张图里既有「人-人」边,也有「人-卡」边。
总结一下: 节点是主体,边是关系,属性是细节。把这个三元组弄扎实了,后面做邻居聚合、子图提取才有底气。我在项目复盘时发现,80%的特征工程问题都出在最基础的这三样东西没定义清楚。搞清楚再动手,别急着堆代码。