• 使用Python探索四大名著【红楼梦】人物之间的关系,简直帅呆了


    嗨,大哥们,我来了!

    《红楼梦》作为我国四大名著之一,古典小说的巅峰之作,粉丝量极其庞大,而红学也经久不衰。所以我们今天通过 Python 来捋一下红楼梦里那错综复杂的人物关系,话不多说,开始整活!

    一、准备工作

    • 红楼梦txt格式电子书一份
    • 金陵十二钗+贾宝玉人物名称列表
    宝玉 nr
    黛玉 nr
    宝钗 nr
    湘云 nr
    凤姐 nr
    李纨 nr
    元春 nr
    迎春 nr
    探春 nr
    惜春 nr
    妙玉 nr
    巧姐 nr
    秦氏 nr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    该分列表是为了做分词时使用,后面的 nr 就是人名的意思。

    二、人物出镜次数

    首先读取小说

    with open("红楼梦.txt", encoding="gb18030") as f:
        honglou = f.read()
    
    • 1
    • 2

    接下来进行出场次数数据整理

    honglou = honglou.replace("\n", " ")
    honglou_new = honglou.split(" ")
    renwu_list = ['宝玉', '黛玉', '宝钗', '湘云', '凤姐', '李纨', '元春', '迎春', '探春', '惜春', '妙玉', '巧姐', '秦氏']
    renwu = pd.DataFrame(data=renwu_list, columns=['姓名'])
    renwu['出现次数'] = renwu.apply(lambda x: len([k for k in honglou_new if x[u'姓名'] in k]), axis=1)
    renwu.to_csv('renwu.csv', index=False, sep=',')
    renwu.sort_values('出现次数', ascending=False, inplace=True)
    attr = renwu['姓名'][0:12]
    v1 = renwu['出现次数'][0:12]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样我们就得到了 attr 和 v1 两个数据,内容如下


    下面就可以通过 pyecharts 来绘制柱状图了

    bar = (
        Bar()
        .add_xaxis(attr.tolist())
        .add_yaxis("上镜次数", v1.tolist())
        .set_global_opts(title_opts=opts.TitleOpts(title="红楼梦上镜13人"))
    )
    bar.render_notebook()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    三、人物关系

    1、数据处理

    我们先将读取到内存中的小说内容进行 jieba 分词处理

    import jieba
    jieba.load_userdict("renwu_forcut")
    renwu_data = pd.read_csv("renwu_forcut", header=-1)
    mylist = [k[0].split(" ")[0] for k in renwu_data.values.tolist()]
    
    • 1
    • 2
    • 3
    • 4

    通过 load_userdict 将我们上面自定义的词典加载到了 jieba 库中

    分词处理

    tmpNames = []
    names = {}
    relationships = {}
    for h in honglou:
        h.replace("贾妃", "元春")
        h.replace("李宫裁", "李纨")
        poss = pseg.cut(h)
        tmpNames.append([])
        for w in poss:
            if w.flag != 'nr' or len(w.word) != 2 or w.word not in mylist:
                continue
            tmpNames[-1].append(w.word)
            if names.get(w.word) is None:
                names[w.word] = 0
            relationships[w.word] = {}
            names[w.word] += 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    因为文中"贾妃", “元春”,“李宫裁”, “李纨” 等人物名字混用严重,所以这里做替换处理。

    然后使用 jieba 库提供的 pseg 工具来做分词处理,会返回每个分词的词性。

    之后做判断,只有符合要求且在我们提供的字典列表里的分词,才会保留。

    一个人每出现一次,就会增加一,方便后面画关系图时,人物 node 大小的确定。

    对于存在于我们自定义词典的人名,保存到一个临时变量当中 tmpNames

    处理每个段落中的人物关系

    for name in tmpNames:
            for name1 in name:
                for name2 in name:
                    if name1 == name2:
                        continue
                    if relationships[name1].get(name2) is None:
                        relationships[name1][name2] = 1
                    else:
                        relationships[name1][name2] += 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    对于出现在同一个段落中的人物,我们认为他们是关系紧密的,同时每出现一次,关系增加1 。

    可以把相关信息保存到文件当中

    with open("relationship.csv", "w", encoding='utf-8') as f:
            f.write("Source,Target,Weight\n")
            for name, edges in relationships.items():
                for v, w in edges.items():
                    f.write(name + "," + v + "," + str(w) + "\n")
    with open("NameNode.csv", "w", encoding='utf-8') as f:
        f.write("ID,Label,Weight\n")
        for name, times in names.items():
            f.write(name + "," + name + "," + str(times) + "\n")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    文件1:人物关系表,包含首先出现的人物、之后出现的人物和一同出现次数。

    文件2:人物比重表,包含该人物总体出现次数,出现次数越多,认为所占比重越大。

    2、数据分析

    下面我们可以做一些简单的人物关系分析

    这里我们还是使用 pyecharts 绘制图表

    def deal_graph():
        relationship_data = pd.read_csv('relationship.csv')
        namenode_data = pd.read_csv('NameNode.csv')
        relationship_data_list = relationship_data.values.tolist()
        namenode_data_list = namenode_data.values.tolist()
        nodes = []
        for node in namenode_data_list:
            if node[0] == "宝玉":
                node[2] = node[2]/3
            nodes.append({"name": node[0], "symbolSize": node[2]/30})
        links = []
        for link in relationship_data_list:
            links.append({"source": link[0], "target": link[1], "value": link[2]})
        g = (
            Graph()
            .add("", nodes, links, repulsion=8000)
            .set_global_opts(title_opts=opts.TitleOpts(title="红楼人物关系"))
        )
        return g
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    首先把两个文件通过 pandas 读取到内存当中

    对于“宝玉”,由于其占比过大,如果统一进行缩放,会导致其他人物的 node 过小,展示不美观,所以这里先做了一次缩放

    最后我们得到的人物关系图如下


    铁子们,今天的分享就到这, 如果感觉文章内容不错的话,记得关注+收藏让更多的人看到!

    给大家分享一套视频,非常全面!

    Python爬虫入门到实战100例

  • 相关阅读:
    2024年宝鸡市国家级、省级、市级科技企业孵化器申报奖励补贴标准及申报条件
    【超好懂的比赛题解】“学佳澳杯”湖北文理学院第一届程序设计竞赛 个人题解
    初识canvas
    地埋式积水监测站的应用及优势
    勤于奋:国外LEAD找任务方法
    解决edge浏览器无法打开pdf文件问题
    MySql 终端常用指令
    Arcgis提取玉米种植地分布,并以此为掩膜提取遥感影像
    STM32CUBEIDE(11)----输出PWM及修改PWM频率与占空比
    阿里巴巴微服务核心手册:Spring Boot+Spring cloud+Dubbo
  • 原文地址:https://blog.csdn.net/fei347795790/article/details/126285137