别再只画词云了!用NetworkX挖掘《三国演义》隐藏的‘朋友圈’与势力图谱
从词频统计到关系网络用NetworkX解锁《三国演义》的深层人物图谱当大多数人还在用词云展示《三国演义》人物出现频率时我们已经可以更进一步——揭示那些隐藏在文本深处的联盟、敌对与权力枢纽。传统词频统计就像只看到了历史舞台上的演员名单而社会网络分析SNA则能还原整场戏剧的角色互动与势力版图。1. 为什么词云不够文本分析的维度升级词云工具能快速呈现谁说的多但无法回答这些关键问题曹操与刘备的直接互动究竟有多少次诸葛亮是仅仅出场次数多还是真正处于信息交换的核心位置当吕布这个名字出现时通常与哪些人物形成强关联NetworkX构建的共现网络可以量化这些关系数据。我们定义两个人物如果在同一段落出现即产生连接通过累计共现次数计算关系强度。这种分析方法与历史学家研究谁与谁结盟的思路高度一致。提示中文古典小说的段落结构天然适合共现分析段落转换往往意味着场景切换2. 环境配置与数据预处理2.1 核心工具链# 基础分析套件 import networkx as nx # 网络分析 import jieba.posseg as pseg # 带词性标注的分词 from collections import defaultdict # 高效计数 # 可视化组件 import matplotlib.pyplot as plt plt.style.use(ggplot) # 更美观的绘图风格2.2 人物别名归一化处理古典小说必须解决的核心问题原始词归一化结果出现示例孔明曰诸葛亮孔明曰此计大妙云长关羽云长提刀上马玄德刘备玄德闻言大惊实现代码示例name_mapping { 孔明: 诸葛亮, 卧龙: 诸葛亮, 云长: 关羽, 关公: 关羽, 玄德: 刘备, 刘皇叔: 刘备 } def normalize_name(raw_name): return name_mapping.get(raw_name, raw_name)3. 构建人物关系网络的实战步骤3.1 共现关系提取算法段落级扫描以\n划分段落保留自然场景上下文双向关系计数避免(A,B)和(B,A)被重复记录权重归一化将绝对次数转换为0-1之间的相对值def build_relation_network(text, main_chars): relation_counts defaultdict(int) paragraphs [p for p in text.split(\n) if len(p) 10] # 过滤短段落 for para in paragraphs: present_chars set() for name in main_chars: if name in para: present_chars.add(name) # 记录所有共现组合 chars_list list(present_chars) for i in range(len(chars_list)): for j in range(i1, len(chars_list)): pair tuple(sorted((chars_list[i], chars_list[j]))) relation_counts[pair] 1 # 权重归一化 max_count max(relation_counts.values()) if relation_counts else 1 return {k: v/max_count for k,v in relation_counts.items()}3.2 网络中心性指标解读计算节点重要性的三大指标指标类型计算方式文学解读度中心性直接连接数人际交往广度接近中心性到其他节点的平均距离信息传播效率中介中心性占据最短路径的比例权力枢纽程度典型人物对比# 计算各项指标 degree_cent nx.degree_centrality(G) betweenness_cent nx.betweenness_centrality(G) # 结果示例 print(诸葛亮的中介中心性, betweenness_cent.get(诸葛亮, 0)) print(吕布的度中心性, degree_cent.get(吕布, 0))4. 多层次关系可视化技巧4.1 权重阈值分层设置不同权重区间呈现差异化的关系强度核心联盟权重0.6用粗实线表示如刘备-诸葛亮一般关联0.3权重≤0.6细实线表示如曹操-许褚弱联系权重≤0.3虚线表示如孙权-马超def visualize_network(G, weight_thresholds[0.3, 0.6]): pos nx.spring_layout(G, k0.8, iterations50) # 按权重分类边 strong_edges [(u,v) for (u,v,d) in G.edges(dataTrue) if d[weight] weight_thresholds[1]] medium_edges [(u,v) for (u,v,d) in G.edges(dataTrue) if weight_thresholds[0] d[weight] weight_thresholds[1]] weak_edges [(u,v) for (u,v,d) in G.edges(dataTrue) if d[weight] weight_thresholds[0]] plt.figure(figsize(12,12)) nx.draw_networkx_nodes(G, pos, node_size800, alpha0.8) # 分层绘制边 nx.draw_networkx_edges(G, pos, edgeliststrong_edges, width3, alpha0.9, edge_colordarkred) nx.draw_networkx_edges(G, pos, edgelistmedium_edges, width2, alpha0.6, edge_colororange) nx.draw_networkx_edges(G, pos, edgelistweak_edges, width1, alpha0.3, edge_colorgray, styledashed) nx.draw_networkx_labels(G, pos, font_size10, font_familySimHei) plt.axis(off)4.2 动态演化分析通过分章回构建网络观察关系变化# 分章回分析示例 chapter_networks [] for chapter in text.split(第): # 简单按章回分割 if not chapter: continue G_chapter build_network(chapter, main_chars) chapter_networks.append(G_chapter) # 计算诸葛亮在各章的中介中心性 zhuge_evolution [nx.betweenness_centrality(G).get(诸葛亮,0) for G in chapter_networks]5. 从数据回到文本验证与解读当发现夏侯惇的中介中心性异常高时回到原文验证定位其关键连接点主要关联曹操与曹仁文本证据操令夏侯惇为都督引曹仁、李典等守樊城历史背景夏侯家族在曹魏军中的桥梁作用这种分析方式不仅验证了网络指标的可信度更能发现传统阅读容易忽略的次级重要人物。