这段代码的整体功能是从一个红楼梦文本文件中利用python的jieba分词库通过算法提取人名,分析这些人名在文本中的出现频率以及他们之间的关联关系,然后将这些信息输出到两个文件中,并最终在控制台上以prettytable表格的形式展示关系信息。下面我会详细解释每个部分的工作原理:
导入必要的库
codecs
:用于读取和写入文件,支持多种编码方式。jieba
:中文分词工具。jieba.posseg
:用于词性标注的分词工具。PrettyTable
:用于创建漂亮的表格输出。
定义相关变量
names
:一个字典,用于存储每个人名及其出现的次数。relationships
:一个字典,用于存储人名之间的关联关系及其次数。line_names
:一个列表,用于存储每一行文本中识别出的人名。
加载任务表和分词
- 使用
jieba.load_userdict("./names.txt")
加载用户自定义词典,这里假设names.txt
包含了一些人名。 - 读取
hlm.txt
文件,对每一行进行分词和词性标注。 - 如果一个词的词性为
nr
(人名)且长度大于或等于2,则将其添加到line_names
列表,并更新names
和relationships
字典。
分析人名关联关系
- 遍历
line_names
列表,分析每一行中不同人名之间的关联关系。 - 如果两个不同的人名在同一行中出现,则增加他们之间的关联次数。
输出到文件
- 将出现次数大于10的人名及其次数输出到
People_node.txt
文件。 - 将关联次数大于10的人名对及其关联次数输出到
People_edge.txt
文件。
过滤和展示关系信息
- 读取
People_edge.txt
文件,将内容存储到列表a
中。 - 根据
names.txt
中的内容过滤掉一些关系。 - 使用
PrettyTable
创建一个表格,展示过滤后的关系信息。
细节解释
w.flag != 'nr' or len(w.word) < 2
:这里过滤掉了非人名词性的词和长度小于2的词,因为可能有一些很短的词被误判为人名。if relationships[name1].get(name2) is None
:如果name1
和name2
之间的关联关系还没有被记录,则初始化为1;否则,增加关联次数。- 在输出到文件时,代码选择了出现次数和关联次数大于10的人名和关系进行输出,这是一个阈值选择,可以根据需要进行调整。
- 在过滤和展示关系信息时,代码使用了列表推导式和
PrettyTable
库来简化代码和提高可读性。
# coding = utf-8
# 导入必要的库
import codecs # 导入codecs库,用于文件编码的读写
import jieba # 导入jieba库,用于中文分词
import jieba.posseg as pseg # 导入jieba的标注词性的模块
from prettytable import PrettyTable # 导入prettytable库,可以创建漂亮的表格
# 定义相关变量
# 定义存储人名和出现次数的字典
names = {}
# 定义存储人物关系和出现次数的字典
relationships = {}
# 定义每行分词后的人物列表
line_names = [] # 定义一个列表,用于存储每一行分词后的人物名称
# 加载词典提高分词准确性
jieba.load_userdict("./names.txt")
# 读取文章并同时录入人名
with codecs.open("./hlm.txt", "r", encoding="utf-8") as f:
for line in f.readlines():
poss = pseg.cut(line) # 对每一行词语进行词性标注
line_names.append([]) # 为该行创建一个专属列表
for w in poss:
if w.flag != 'nr' or len(w.word) < 2: # 忽略不是人名或者词的长度小于2的词
continue
line_names[-1].append(w.word) # 符合要求的词添加到行列表内
if names.get(w.word) is None: # 如果人名在names字典中不存在就添加该人名并重置次数为0
names[w.word] = 0
relationships[w.word] = {} # 在relationships字典中添加新人名并初始化其关系字典
names[w.word] += 1 # 人名出现次数加1
# 遍历每一行的人物列表,统计人物之间的关系
for line in line_names:
for name1 in line:
for name2 in line:
if name1 == name2: # 如果两个人名相同就跳过
continue
if relationships[name1].get(name2) is None: # 如果name1和name2的关系在relationships字典中不存在就在relationships字典中添加该关系
relationships[name1][name2] = 1 # 设置两者关系出现次数为1
else:
relationships[name1][name2] = relationships[name1][name2] + 1 # 关系出现次数加1
# 将出现次数大于10的人名及其出现次数写入文件
with codecs.open("./People_node.txt", "w", encoding="utf-8") as f:
f.write("ID Label \r\n")
for name, times in names.items(): # 遍历names字典,键:名字,值:出现次数
if times > 10: # 如果人名出现次数大于10就将人名、标签和出现次数写入文件
f.write(name + " " + name + " " + str(times) + "\r\n")
# 将出现次数大于10的人物关系及其出现次数写入文件
with codecs.open("./People_edge.txt", "w", "utf-8") as f:
f.write("Source Target Weight\r\n")
for name, edges in relationships.items(): # 遍历relationships字典,键:名字,值:关系出现次数
for v, w in edges.items(): # 遍历关系嵌套字典edges
if w > 10: # 如果关系出现次数大于10就将源、目标和权重写入文件
f.write(name + " " + v + " " + str(w) + "\r\n")
# 读取文件准备过滤并显示数据
f1 = open("./People_edge.txt", "r", encoding='utf-8')
f2 = open("./names.txt", "r", encoding='utf-8').read()
lines = f1.readlines() # 读取文件所有行
a = [] # 初始化一个空列表用来存放展示关系图谱的数据
# 遍历从文件中读取的每一行
for line in lines:
# 为当前行创建一个新的空列表,并添加到列表a中
a.append([])
# 去除行尾的换行符,并按空格分割字符串,得到包含各个字段的列表m
m = line.strip('\n').split(' ')
# 遍历分割后的字段列表m
for x in m:
# 将字段x添加到当前行的列表中
a[-1].append(x)
# 遍历列表a中的每一项(即每一行数据)
for items in a:
# 如果当前行的第一个字段存在(非空),并且第二个字段不在f2(names.txt的内容)中
if items[0] and items[1] not in f2:
# 则从列表a中删除这一行数据
del items
# 关闭文件
f1.close()
# 实例PrettyTable对象
x = PrettyTable()
# 设置表头为Source、Target、Weight共3列
x.field_names = ['Source', 'Target', 'Weight']
# 遍历列表a中除了第一行之外的行的数据
for i in a[1:]:
# 将a列表嵌套的每行子列表的数据添加到行内
x.add_row([i[0], i[1], i[2]])
# 打印去掉标题的记录数
print(f"Total Records Number: {len(a) - 1}")
# 展示表格
print(x)
缺陷
这段代码主要用于文本分析中的人物关系提取和可视化,它使用了中文分词和词性标注技术来识别人名,并通过分析人名在文本中的共现关系来构建人物关系网络。但还是会出现误认为某些词语是人名的现象,这个问题还需要对提取人名的算法进行改进。有其他问题也可以直接私聊我哦。