<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用python分析《三國演義》中的社交網(wǎng)絡(luò)

          共 7899字,需瀏覽 16分鐘

           ·

          2021-02-16 09:45

          作者介紹:blmoistawinde, 西南某高校學(xué)森一枚,喜歡有意思的數(shù)據(jù)挖掘分析。希望給世界帶來些清新空氣~

          個人博客地址:https://blog.csdn.net/blmoistawinde。


          前言


          一直以來對自然語言處理和社交網(wǎng)絡(luò)分析都很感興趣,前者能幫助我們從文本中獲得很多發(fā)現(xiàn),而后者能夠讓我們對人們和各個事物之間普遍存在的網(wǎng)絡(luò)般的聯(lián)系有更多認識。當二者結(jié)合,又會有怎樣的魔力呢?


          作為一個三國迷,我就有了這樣的想法:能不能用文本處理的方法,得到《三國演義》中的人物社交網(wǎng)絡(luò),再進行分析呢?python中有很多好工具能夠幫助我實踐我好奇的想法,現(xiàn)在就開始動手吧。


          準備工作


          獲得《三國演義》的文本。


          chapters = get_sanguo() ? ? ? ? ? ? ? ? # 文本列表,每個元素為一章的文本
          print(chapters[0][:106])


          第一回 宴桃園豪杰三結(jié)義 斬黃巾英雄首立功
          滾滾長江東逝水,浪花淘盡英雄。是非成敗轉(zhuǎn)頭空。
          青山依舊在,幾度夕陽紅。
          白發(fā)漁樵江渚上,慣看秋月春風(fēng)。一壺濁酒喜相逢。
          古今多少事,都付笑談中


          《三國演義》并不是很容易處理的文本,它接近古文,我們會面對古人的字號等一系列別名。比如電腦怎么知道“玄德”指的就是“劉備”呢?那就要我們給它一些知識。我們?nèi)送ㄟ^學(xué)習(xí)知道“玄德”是劉備的字,電腦也可以用類似的方法完成這個概念的連接。我們需要告訴電腦,“劉備”是實體(類似于一個對象的標準名),而“玄德”則是“劉備”的一個指稱,告訴的方式,就是提供電腦一個知識庫。


          entity_mention_dict, entity_type_dict = get_sanguo_entity_dict()
          print("劉備的指稱有:",entity_mention_dict["劉備"])


          劉備的指稱有:['劉備', '劉玄德', '玄德', '使君']


          除了人的實體和指稱以外,我們也能夠包括三國勢力等別的類型的指稱,比如“蜀”又可以叫“蜀漢”,所以知識庫里還可以包括實體的類型信息來加以區(qū)分。


          print("劉備的類型為",entity_type_dict["劉備"])
          print("蜀的類型為",entity_type_dict["蜀"])
          print("蜀的指稱有",entity_mention_dict["蜀"])

          劉備的類型為 人名
          蜀的類型為 勢力
          蜀的指稱有 ['蜀', '蜀漢']


          有了這些知識,理論上我們就可以編程聯(lián)系起實體的各個綽號啦。不過若是要從頭做起的話,其中還會有不少的工作量。而HarvestText[1]是一個封裝了這些步驟的文本處理庫,可以幫助我們輕松完成這個任務(wù)。


          ht = HarvestText()
          ht.add_entities(entity_mention_dict, entity_type_dict) ? ? ?# 加載模型
          print(ht.seg("誓畢,拜玄德為兄,關(guān)羽次之,張飛為弟。",standard_name=True))

          ['誓畢', ',', '拜', '劉備', '為兄', ',', '關(guān)羽', '次之', ',', '張飛', '為弟', '。']



          社交網(wǎng)絡(luò)建立


          成功地把指稱統(tǒng)一到標準的實體名以后,我們就可以著手挖掘三國的社交網(wǎng)絡(luò)了。具體的建立方式是利用鄰近共現(xiàn)關(guān)系。每當一對實體在兩句話內(nèi)同時出現(xiàn),就給它們加一條邊。那么建立網(wǎng)絡(luò)的整個流程就如同下圖所示:



          我們可以使用HarvestText提供的函數(shù)直接完成這個流程,讓我們先在第一章的小文本上實踐一下:


          # 準備工作
          doc = chapters[0].replace("操","曹操") ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# 由于有時使用縮寫,這里做一個微調(diào)
          ch1_sentences = ht.cut_sentences(doc) ? ? # 分句
          doc_ch01 = [ch1_sentences[i]+ch1_sentences[i+1] for i in range(len(ch1_sentences)-1)] ?#獲得所有的二連句
          ht.set_linking_strategy("freq")

          # 建立網(wǎng)絡(luò)
          G = ht.build_entity_graph(doc_ch01, used_types=["人名"]) ? ? ? ? ? ? ?# 對所有人物建立網(wǎng)絡(luò),即社交網(wǎng)絡(luò)

          # 挑選主要人物畫圖
          important_nodes = [node for node in G.nodes if G.degree[node]>=5]
          G_sub = G.subgraph(important_nodes).copy()
          draw_graph(G_sub,alpha=0.5,node_scale=30,figsize=(6,4))


          他們之間具體有什么關(guān)系呢?我們可以利用文本摘要得到本章的具體內(nèi)容:


          stopwords = get_baidu_stopwords() ? ?#過濾停用詞以提高質(zhì)量

          for i,doc in enumerate(ht.get_summary(doc_ch01, topK=3, stopwords=stopwords)):
          ?print(i,doc)

          玄德見皇甫嵩、朱儁,具道盧植之意。嵩曰:“張梁、張寶勢窮力乏,必投廣宗去依張角。
          時張角賊眾十五萬,植兵五萬,相拒于廣宗,未見勝負。植謂玄德曰:“我今圍賊在此,賊弟張梁、張寶在潁川,與皇甫嵩、朱儁對壘。
          次日,于桃園中,備下烏牛白馬祭禮等項,三人焚香再拜而說誓曰:“念劉備、關(guān)羽、張飛,雖然異姓,既結(jié)為兄弟,則同心協(xié)力,


          本章的主要內(nèi)容,看來就是劉關(guān)張?zhí)覉@三結(jié)義,并且共抗黃巾賊的故事。


          三國全網(wǎng)絡(luò)繪制


          有了小范圍實踐的基礎(chǔ),我們就可以用同樣的方法,整合每個章節(jié)的內(nèi)容,畫出一張橫跨三國各代的大圖。


          G_chapters = []
          for chapter in chapters:
          ? ?sentences = ht.cut_sentences(chapter) ? ? # 分句
          ? ?docs = [sentences[i]+sentences[i+1] for i in range(len(sentences)-1)]
          ? ?G_chapters.append(ht.build_entity_graph(docs, used_types=["人名"]))

          # 合并各張子圖
          G_global = nx.Graph()
          for G0 in G_chapters:
          ? ?for (u,v) in G0.edges:
          ? ? ? ?if G_global.has_edge(u,v):
          ? ? ? ? ? ?G_global[u][v]["weight"] += G0[u][v]["weight"]
          ? ? ? ?else:
          ? ? ? ? ? ?G_global.add_edge(u,v,weight=G0[u][v]["weight"])

          # 忽略游離的小分支只取最大連通分量
          largest_comp = max(nx.connected_components(G_global), key=len)
          G_global = G_global.subgraph(largest_comp).copy()
          print(nx.info(G_global))

          Name:
          Type: Graph
          Number of nodes: 1290
          Number of edges: 10096
          Average degree: ?15.6527


          整個社交網(wǎng)絡(luò)有1290個人那么多,還有上萬條邊!那么我們要把它畫出來幾乎是不可能的,那么我們就挑選其中的關(guān)鍵人物來畫出一個子集吧。


          important_nodes = [node for node in G_global.nodes if G_global.degree[node]>=30]
          G_main = G_global.subgraph(important_nodes).copy()


          pyecharts進行可視化


          from pyecharts import Graph
          nodes = [{"name": "結(jié)點1", "value":0, "symbolSize": 10} for i in range(G_main.number_of_nodes())]
          for i,name0 in enumerate(G_main.nodes):
          ? ?nodes[i]["name"] = name0
          ? ?nodes[i]["value"] = G_main.degree[name0]
          ? ?nodes[i]["symbolSize"] = G_main.degree[name0] / 10.0
          links = [{"source": "", "target": ""} for i in range(G_main.number_of_edges())]
          for i,(u,v) in enumerate(G_main.edges):
          ? ?links[i]["source"] = u
          ? ?links[i]["target"] = v
          ? ?links[i]["value"] = G_main[u][v]["weight"]

          graph = Graph("三國人物關(guān)系力導(dǎo)引圖")
          graph.add("", nodes, links)
          graph.render("./images/三國人物關(guān)系力導(dǎo)引圖.html")
          graph

          博客上不能顯示交互式圖表,這里就給出截圖:顯示了劉備的鄰接結(jié)點



          整個網(wǎng)絡(luò)錯綜復(fù)雜,背后是三國故事中無數(shù)的南征北伐、爾虞我詐。不過有了計算機的強大算力,我們依然可以從中梳理出某些關(guān)鍵線索,比如:


          人物排名-重要性


          對這個問題,我們可以用網(wǎng)絡(luò)中的排序算法解決。PageRank就是這樣的一個典型方法,它本來是搜索引擎利用網(wǎng)站之間的聯(lián)系對搜索結(jié)果進行排序的方法,不過對人物之間的聯(lián)系也是同理。讓我們獲得最重要的20大人物:


          page_ranks = pd.Series(nx.algorithms.pagerank(G_global)).sort_values()
          page_ranks.tail(20).plot(kind="barh")
          plt.show()



          《三國演義》當仁不讓的主角就是他們了,哪怕你對三國不熟悉,也一定會對這些人物耳熟能詳。


          人物排名-權(quán)力值


          這個問題看上去跟上面一個問題很像,但其實還是有區(qū)別的。就像人緣最好的人未必是領(lǐng)導(dǎo)一樣,能在團隊中心起到凝聚作用,使各個成員相互聯(lián)系合作的人才是最有權(quán)力的人。中心度就是這樣的一個指標,看看三國中最有權(quán)力的人是哪些吧?


          between = pd.Series(nx.betweenness_centrality(G_global)).sort_values()
          between.tail(20).plot(kind="barh")
          plt.show()



          結(jié)果的確和上面的排序有所不同,我們看到劉備、曹操、孫權(quán)、袁紹等主公都名列前茅。而另一個有趣的發(fā)現(xiàn)是,司馬懿、司馬昭、司馬師父子三人同樣榜上有名,而曹氏的其他后裔則不見其名,可見司馬氏之權(quán)傾朝野。司馬氏之心,似乎就這樣被大數(shù)據(jù)揭示了出來!


          社群發(fā)現(xiàn)


          人物關(guān)系有親疏遠近,因此往往會形成一些集團。社交網(wǎng)絡(luò)分析里的社區(qū)發(fā)現(xiàn)算法就能夠讓我們發(fā)現(xiàn)這些集團,讓我使用community庫[2]中的提供的算法來揭示這些關(guān)系吧。


          import community ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?# python-louvain
          partition = community.best_partition(G_main) ? ? ? ? # Louvain算法劃分社區(qū)
          comm_dict = defaultdict(list)
          for person in partition:
          ? ?comm_dict[partition[person]].append(person)


          在下面3個社區(qū)里,我們看到的主要是魏蜀吳三國重臣們。(只有一些小“問題”,有趣的是,電腦并不知道他們的所屬勢力,只是使用算法。)


          draw_community(2)


          community 2: 張遼 曹仁 夏侯惇 徐晃 曹洪 夏侯淵 張郃 許褚 樂進 李典 于禁 
          荀彧 劉曄 郭嘉 滿寵 程昱 荀攸 呂虔 典韋 文聘 董昭 毛玠


          draw_community(4)



          community 4: 曹操 諸葛亮 劉備 關(guān)羽 趙云 張飛 馬超 黃忠 許昌 孟達[魏] 孫乾
          曹安民 劉璋 關(guān)平 龐德 法正 伊籍 張魯 劉封 龐統(tǒng) 孟獲 嚴顏 馬良 簡雍 蔡瑁
          陶謙 孔融 劉琮[劉表子] 劉望之 夏侯楙 周倉 陳登


          draw_community(3)



          community 3: 孫權(quán) 孫策 周瑜 陸遜 呂蒙 丁奉 周泰 程普 韓當 徐盛 張昭[吳] 馬相 黃蓋[吳] 潘璋 甘寧 魯肅 凌統(tǒng) 太史慈 諸葛瑾 韓吳郡 蔣欽 黃祖 闞澤 朱桓 陳武 呂范


          draw_community(0)



          community 0: 袁紹 呂布 劉表 袁術(shù) 董卓 李傕 賈詡 審配 孫堅 郭汜 陳宮 馬騰 
          袁尚 韓遂 公孫瓚 高順 許攸[袁紹] 臧霸 沮授 郭圖 顏良 楊奉 張繡 袁譚 董承
          文丑 何進 張邈[魏] 袁熙


          還有一些其他社區(qū)。比如在這里,我們看到三國前期,孫堅、袁紹、董卓等主公們?nèi)盒壑鹇?,好不熱鬧。


          draw_community(1)



          community 1: 司馬懿 魏延 姜維 張翼 馬岱 廖化 吳懿 司馬昭 關(guān)興 吳班 王平 
          鄧芝 鄧艾 張苞[蜀] 馬忠[吳] 費祎 譙周 馬謖 曹真 曹丕 李恢 黃權(quán) 鐘會 蔣琬
          司馬師 劉巴[蜀] 張嶷 楊洪 許靖 費詩 李嚴 郭淮 曹休 樊建 秦宓 夏侯霸 楊儀
          ?高翔 張南[魏] 華歆 曹爽 郤正 許允[魏] 王朗[司徒] 董厥 杜瓊 霍峻 胡濟 賈充
          ? 彭羕 吳蘭 諸葛誕 雷銅 孫綝 卓膺 費觀 杜義 閻晏 盛勃 劉敏 劉琰 杜祺 上官雝
          ? 丁咸 爨習(xí) 樊岐 曹芳 周群


          這個社區(qū)是三國后期的主要人物了。這個網(wǎng)絡(luò)背后的故事,是司馬氏兩代三人打敗姜維率領(lǐng)的蜀漢群雄,又掃除了曹魏內(nèi)部的曹家勢力,終于登上權(quán)力的頂峰。


          動態(tài)網(wǎng)絡(luò)


          研究社交網(wǎng)絡(luò)隨時間的變化,是個很有意思的任務(wù)。而《三國演義》大致按照時間線敘述,且有著極長的時間跨度,順著故事線往下走,社交網(wǎng)絡(luò)會發(fā)生什么樣的變化呢?


          這里,我取10章的文本作為跨度,每5章記錄一次當前跨度中的社交網(wǎng)絡(luò),就相當于留下一張快照,把這些快照連接起來,我們就能夠看到一個社交網(wǎng)絡(luò)變化的動畫??煺者€是用networkx得到,而制作動畫,我們可以用moviepy。


          江山代有才人出,讓我們看看在故事發(fā)展的各個階段,都是哪一群人活躍在舞臺中央呢?


          import moviepy.editor as mpy
          from moviepy.video.io.bindings import mplfig_to_npimage
          width, step = 10,5
          range0 = range(0,len(G_chapters)-width+1,step)
          numFrame, fps = len(range0), 1
          duration = numFrame/fps
          pos_global = nx.spring_layout(G_main)

          def make_frame_mpl(t):
          ? ?i = step*int(t*fps)
          ? ?G_part = nx.Graph()
          ? ?for G0 in G_chapters[i:i+width]:
          ? ? ? ?for (u,v) in G0.edges:
          ? ? ? ? ? ?if G_part.has_edge(u,v):
          ? ? ? ? ? ? ? ?G_part[u][v]["weight"] += G0[u][v]["weight"]
          ? ? ? ? ? ?else:
          ? ? ? ? ? ? ? ?G_part.add_edge(u,v,weight=G0[u][v]["weight"])
          ? ?largest_comp = max(nx.connected_components(G_part), key=len)
          ? ?used_nodes = set(largest_comp) & set(G_main.nodes)
          ? ?G = G_part.subgraph(used_nodes)
          ? ?fig = plt.figure(figsize=(12,8),dpi=100)
          ? ?nx.draw_networkx_nodes(G,pos_global,node_size=[G.degree[x]*10 for x in G.nodes])
          # ? ? nx.draw_networkx_edges(G,pos_global)
          ? ?nx.draw_networkx_labels(G,pos_global)
          ? ?plt.xlim([-1,1])
          ? ?plt.ylim([-1,1])
          ? ?plt.axis("off")
          ? ?plt.title(f"第{i+1}到第{i+width+1}章的社交網(wǎng)絡(luò)")
          ? ?return mplfig_to_npimage(fig)
          animation = mpy.VideoClip(make_frame_mpl, duration=duration)

          animation.write_gif("./images/三國社交網(wǎng)絡(luò)變化.gif", fps=fps)


          美觀起見,動畫中省略了網(wǎng)絡(luò)中的邊。



          隨著時間的變化,曾經(jīng)站在歷史舞臺中央的人們也漸漸地會漸漸離開,讓人不禁唏噓感嘆。正如《三國演義》開篇所言:


          古今多少事,都付笑談中。


          今日,小輩利用python做的一番笑談也就到此結(jié)束吧……


          本文為簡潔起見省略了一些細節(jié)代碼,公眾號后臺回復(fù)“三國”或者“三國演義”可以獲取本文代碼地址


          注:


          [0] 本文受到了數(shù)據(jù)森麟前面的?“水泊梁山“互聯(lián)網(wǎng)有限公司一百單八將內(nèi)部社交網(wǎng)絡(luò) 極大的啟發(fā),很高興能夠接觸到這些有趣的數(shù)據(jù)分析,和這一群有趣的人~


          [1] harvesttext是本人的作品~(*__*) ~,已在Github上開源并可通過pip直接安裝,旨在幫助使用者更輕易地完成像本文這樣的文本數(shù)據(jù)分析。除了本文涉及的功能以外,還有情感分析、新詞發(fā)現(xiàn)等功能。大家覺得有用的話,不妨親身嘗試下,看看能不能在自己感興趣的文本上有更多有趣有用的發(fā)現(xiàn)呢?


          [2]commutity庫的本名是python-louvain,使用了和Gephi內(nèi)置相同的Louvain算法進行社區(qū)發(fā)現(xiàn)


          [3]由于處理古文的困難性,本文中依然有一些比較明顯的錯誤,希望大家不要介意~


          點擊【在看】,不再錯過及時的內(nèi)容推薦↓↓↓

          瀏覽 60
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费做爱网站 | 青春草在线免费视频 | 福利视频一区 | 豆花在线精品视频 | 国产成人无码久久久天美传媒 |