1.2 标本制作规程:节点、边与图的种类


1.2 标本制作规程:节点、边与图的种类

摘要:把现实系统翻译成图,是网络分析的第一道工序,也是最影响结论的一道工序。本节讲清节点与边这两个「看似简单」的定义决定,整理有向与无向、加权与无权、多重图、超图、二分图等图的种类,比较邻接矩阵与边列表两种存储方式的适用边界,并示范一次从原始合作记录到分析用图的完整翻译流程。

「建图」这道工序,比你想的重

做网络分析的人爱说一句行话:「先建图,再说话。」意思是任何分析结论都排在建图之后,而建图远不是把数据倒进软件那么轻松——它是建模决策的集合,每一步都藏着分岔口。

上一章的演员合作档案正好拿来当解剖样本。面对同一份演职员表,「谁和谁之间有边」至少有两种合理定义:其一,出演过同一部影片的演员互连,得到的是合作过的「同伴图」;其二,只把在同一部影片中戏份相邻(有对手戏)的演员互连,得到的是「对手戏图」。同伴图里两位从未照面的大明星可能仅凭都客串过同一部群戏就连上了;对手戏图则苛刻得多。两张图节点集合相同,边集合不同,算出来的度、社团、中心性自然各异——如果研究问题是「消息在剧组里怎么传」,对手戏图多半更贴近真相。

所以规程的第一条是:先问研究问题,再定节点和边,次序不能反。问题决定翻译口径,翻译口径决定图,图决定你能看到什么。这一点上一章支柱页已经立过牌,本节把它展开成可执行的规程。

节点与边:两个必须签字的决定

节点是什么,看似最简单,实则暗藏两种坑。第一种是粒度坑:研究城市间航班,节点是「城市」还是「机场」?一座双机场城市算一个节点还是两个?粒度选择直接改变度分布。第二种是存活坑:节点集合是全量还是快照?把已经注销的账号留在社交图里,度分布的尾巴会被人为拉长;把新加入的蛋白排除在相互作用网络外,又会低估年轻枢纽的成长性。

边是什么,坑更深,因为它同时编码了「关系类型」与「关系强度」。好友关系、互发消息、共同群组、互相评论,都能拿来定义社交图的边,得到的却是四张不同的图。规程要求在建图时白纸黑字写下三件事:关系语义(这条边在现实里对应什么)、采集方式(数据从哪来、覆盖多久)、方向与权重(对称吗、有强弱吗)。这三行字将来要跟着结论一起发表,也是自己复查时最先翻的地方。

图的种类:标本柜的第一层抽屉

翻译定案后,把标本放进正确的抽屉。常用的分类维度有两组,组合出最常用的四种图,再往外延伸几种特殊形态。

维度 选项 典型系统 选错的后果
方向 有向 微博关注、网页链接、论文引用 把单向关注当双向好友,路径与度全部失真
方向 无向 合作、婚姻、电网线路 强行给对称关系定方向,引入伪因果
权重 加权 航线运力、通信流量、相似度 丢弃权重等于宣布「一封信与一万封信等价」
权重 无权 是否共同出演 把噪声计数当强度,肥尾被高估

在此基础上还有几种值得认脸的形态。多重图允许两点之间有多条边——两家机构之间可以同时有信贷、担保、持股多种关系,压成一条边会掩盖风险的真实厚度。超图允许一条边连接多个节点——一次会议、一部影片本身就是「多人关系」,硬拆成两两边是信息损失。二分图的节点天然分两族、边只跨族连接——演员与影片、用户与商品、学者与论文都是天生的二分结构,很多「同现网络」其实是二分图的投影,这一点马上会在代码里实操。

把标本装进内存:邻接矩阵与边列表

图在计算机里有两种经典存法。邻接矩阵用行列都对应节点,两点相连处置非零值:查任意两点是否相邻只要看一个格子,做矩阵运算、算谱性质时几乎必用;代价是存储量按节点数的平方增长——十万节点的稀疏社交网络,矩阵里绝大多数格子是零,纯属浪费。边列表只存实际存在的边,一行一条记录:存储量与边数成正比,天然适配稀疏网络与流式读取,是数据交换的事实标准;代价是「两点是否相邻」这类查询要靠哈希索引辅助。

经验法则很朴素:万节点以下、需要代数运算(谱、矩阵幂、同步线性化),用邻接矩阵;更大规模、以遍历和统计为主,用边列表加邻接表。两者可以随时互转,networkx 把这件事压缩成两个函数调用。

import networkx as nx # 边列表建图:演员-演员合作对(已从演职员表翻译而来) pairs = [("张三", "李四"), ("张三", "王五"), ("李四", "王五"), ("王五", "赵六"), ("赵六", "钱七"), ("钱七", "孙八")] G = nx.Graph() G.add_edges_from(pairs) # 无向图:合作是相互的 A = nx.to_numpy_array(G, nodelist=sorted(G.nodes())) print("邻接矩阵:") print(A.astype(int)) print("边列表:", sorted(G.edges()))

输出里矩阵对角线全零、对称、每行求和即为该节点的度——三个特征正好对应「无自环、无向、度」三件事。把 nx.Graph 换成 nx.DiGraph,矩阵立刻失去对称性;换成 nx.Graphadd_weighted_edges_from,格子里的就不再是与零而是强度。图的种类从来不是学术分类,而是数据结构的选型。

一次完整的翻译:从二分记录到投影图

真实数据常以二分形态出现:演职员表记录的是「演员—影片」,不是「演员—演员」。翻译规程的最后一步,是把二分图投影成同质网络——让出演过同一部影片的演员相连,并统计合作次数作为权重。这一步的每一行都值得盯紧:投影会凭空制造完全图式的稠密团(一部大片的所有演员两两相连),也会把「合作过一次」与「合作过十次」压平——前者导致聚类虚高,后者需要权重来补救。

from collections import defaultdict # 二分记录:影片 -> 参演演员 film_cast = { "长街": ["张三", "李四", "王五"], "夜航": ["王五", "赵六"], "海雾": ["李四", "钱七", "孙八", "周九"], } co_work = defaultdict(int) # 合作对 -> 合作次数 for cast in film_cast.values(): cast = sorted(cast) for i in range(len(cast)): for j in range(i + 1, len(cast)): co_work[(cast[i], cast[j])] += 1 WG = nx.Graph() for (a, b), w in co_work.items(): WG.add_edge(a, b, weight=w) for a, b, data in sorted(WG.edges(data=True)): print(f"{a} — {b}: 合作 {data['weight']} 次") print("加权聚类倾向检查:王五的邻居互相认识吗?", [WG.has_edge(x, y) for x, y in [("张三", "李四"), ("张三", "王五")]])

投影结果里,「长街」的三位演员两两连边、权重各计一次——一部影片就这样变成了一个三角形。这正是第 3 章要讲的「高聚类」在生成端的样子:三角形的密度不是巧合,而是「多人共享同一事件」这类二分结构的数学投影。权重则保留了强度信息:连续合作的搭档与偶遇的客串,在图里终于可以区分。

规程清单与常见坑

⚠️ 常见坑一:拿到数据先画图再想问题。图的定义应当由假设驱动,先画出来的图会反过来把你困在它的口径里。
⚠️ 常见坑二:二分图直接当同质网络分析。演员—影片的度分布和演员—演员的度分布完全是两种动物,混用会把「影片参演人数」误读成「演员朋友数」。
⚠️ 常见坑三:多重关系压成单边。金融风险分析里这几乎是事故级的错误——信贷断了担保还在,风险敞口的「厚度」必须靠多重图或加权图保留。

签完字、装对抽屉、选好存储,标本制作宣告完成。下一节领三把测量尺——度、路径、连通性——给刚做好的标本量出第一批形态数据。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U