小蛇學python(12)分析《今生今世》人物關系圖譜

《今生今世》是渣男胡蘭成所寫的一部自傳體小說揪荣。今天我們就來分析一下在他所寫的自傳中的人物關系圖譜雅任,分析一下胡蘭成到底和多少女人有關系吱抚。

代碼奉上

# -*- coding: utf-8 -*-
#@author: Yuhao Zhang
#2018.5.30

import jieba
import codecs
from collections import defaultdict
from pandas import DataFrame
import pandas as pd

#路徑設置昔驱。
#文檔介紹
#jsjs.txt是胡蘭成寫的小說《今生今世》。
#person.txt是一個語料庫洛史,里面放了很多該小說的角色名稱惯殊。
#synonymous_dict.txt是角色的別名
#其他兩個文件是存放程序計算所得各個人物關系之間的邊的權(quán)重
TEXT_PATH = 'jsjs.txt'
DICT_PATH = 'person.txt'
SYNONYMOUS_DICT_PATH = 'synonymous_dict.txt'
SAVE_NODE_PATH = 'node.csv'
SAVE_EDGE_PATH = 'edge.csv'

#類的初始化
class RelationshipView:
    def __init__(self, text_path, dict_path, synonymous_dict_path):
        self._text_path = text_path
        self._dict_path = dict_path
        self._synonymous_dict_path = synonymous_dict_path
        self._person_counter = defaultdict(int)
        self._person_per_paragraph = []
        self._relationships = {}
        self._synonymous_dict = {}

    def generate(self):
        self.count_person()
        self.calc_relationship()
        self.save_node_and_edge()

    def synonymous_names(self):
        with codecs.open(self._synonymous_dict_path, 'r', 'utf-8') as f:
            lines = f.read().split('\n')
        for l in lines:
            self._synonymous_dict[l.split(' ')[0]] = l.split(' ')[1]
        return self._synonymous_dict

    def get_clean_paragraphs(self):
        new_paragraphs = []
        last_paragraphs = []
        with codecs.open(self._text_path, 'r', 'utf-8') as f:
            paragraphs = f.read().split('\r\n')
            paragraphs = paragraphs[0].split('\u3000')
        for i in range(len(paragraphs)):
            if paragraphs[i] != '':
                new_paragraphs.append(paragraphs[i])
        for i in range(len(new_paragraphs)):
            new_paragraphs[i] = new_paragraphs[i].replace('\n', '')
            new_paragraphs[i] = new_paragraphs[i].replace(' ', '')
            last_paragraphs.append(new_paragraphs[i])
        return last_paragraphs

    def count_person(self):
        paragraphs = self.get_clean_paragraphs()
        synonymous = self.synonymous_names()
        print('start process node')
        with codecs.open(self._dict_path, 'r', 'utf-8') as f:
            name_list = f.read().split(' 10 nr\n')
        for p in paragraphs:
            jieba.load_userdict(self._dict_path)
            poss = jieba.cut(p)
            self._person_per_paragraph.append([])
            for w in poss:
                if w not in name_list:
                    continue
                if synonymous.get(w):
                    w = synonymous[w]
                self._person_per_paragraph[-1].append(w)
                if self._person_counter.get(w) is None:
                    self._relationships[w] = {}
                self._person_counter[w] += 1
        return self._person_counter

    def calc_relationship(self):
        print("start to process edge")
        for p in self._person_per_paragraph:
            for name1 in p:
                for name2 in p:
                    if name1 == name2:
                        continue
                    if self._relationships[name1].get(name2) is None:
                        self._relationships[name1][name2] = 1
                    else:
                        self._relationships[name1][name2] += 1
        return self._relationships

    def save_node_and_edge(self):
        excel = []
        for name, times in self._person_counter.items():
            excel.append([])
            excel[-1].append(name)
            excel[-1].append(name)
            excel[-1].append(str(times))
        data = DataFrame(excel, columns=['Id', 'Label', 'Weight'])
        data.to_csv('node.csv', encoding='gbk')

        excel = []
        for name, edges in self._relationships.items():
            for v, w in edges.items():
                if w > 3:
                    excel.append([])
                    excel[-1].append(name)
                    excel[-1].append(v)
                    excel[-1].append(str(w))
        data = DataFrame(excel, columns=['Source', 'Target', 'Weight'])
        data.to_csv('edge.csv', encoding='gbk')

        print('save file successful!')

if __name__ == '__main__':
    v = RelationshipView(TEXT_PATH, DICT_PATH, SYNONYMOUS_DICT_PATH)
    v.generate()

先將代碼全部貼出來,大家復制粘貼即可運行也殖,接下來我再慢慢得一行一行仔細講解代碼邏輯土思。

先把程序運行效果圖貼出來。

基礎關系圖譜
戀人關系圖譜

設計思想

整個程序的實現(xiàn)過程是這樣的忆嗜。

首先己儒,我們預先準備好語料庫。(里面含有小說主人公姓名以及別名霎褐,這樣做的目的降低了程序的難度址愿,不然還要涉及到知識圖譜的實體識別。有關知識圖譜的東西我最近在看冻璃,以后會寫這方面博客)

然后我們將這篇將近三十萬字的小說按段落分開响谓,對每一段進行單獨分析损合,對兩個實體之間的邊的權(quán)重進行計算。具體的說娘纷,它的權(quán)重是如何計算的呢嫁审?比如第一段我們結(jié)合語料庫發(fā)現(xiàn)里面有三個胡蘭成,一個張愛玲赖晶,一個周佛海律适。那么我們就給胡蘭成和張愛玲之間的邊權(quán)重更新為3,張愛玲和周佛海之間更新為1遏插,胡蘭成和周佛海之間更新為3捂贿。然后將每個段落的每兩個點之間的權(quán)重加和,最后寫入excel表中保存為csv格式胳嘲。這是為了方便使用Gephi可視化厂僧。

當然同時我們也進行了節(jié)點大小的計算,其實就是單純計算這個實體名字在文中出現(xiàn)的次數(shù)了牛。比如胡蘭成出現(xiàn)了5752次颜屠,那么它在可視化圖中的大小你可以理解為5752個單位這么大。

node.csv
edge.csv

代碼詳解

我只講我認為最難懂的部分鹰祸,一些我認為簡單的如果你不懂可以私信評論我都可以甫窟,我一般會很快回復。

我認為最難懂的就是count_person()函數(shù)中的一部分蛙婴,當然這一部分也可以說是精華粗井。

        for p in paragraphs:
            jieba.load_userdict(self._dict_path)
            poss = jieba.cut(p)
            self._person_per_paragraph.append([])
            for w in poss:
                if w not in name_list:
                    continue
                if synonymous.get(w):
                    w = synonymous[w]
                self._person_per_paragraph[-1].append(w)
                if self._person_counter.get(w) is None:
                    self._relationships[w] = {}
                self._person_counter[w] += 1
        return self._person_counter

首先在一個將小說每段作為一個字符串元素的列表paragraphs中循環(huán),也就是說p是小說中的每一段敬锐。

然后在加載了語料庫之后背传,對該段進行結(jié)巴分詞呆瞻,并將該段分詞結(jié)果存在poss列表中台夺。如果語料庫中的實體在poss中可以找的到,那么我們就從存儲別名字典中依據(jù)這個實體來找他的唯一名字(因為有可能是別名痴脾,我們要統(tǒng)一換成真正的姓名進行加權(quán))颤介。

get()是字典這個類型所有的屬性,在該段代碼中體現(xiàn)為赞赖,判斷該字典中有無含有以w為key的元素滚朵。

大家也許會疑惑,為何self._person_counter是一個字典呢前域?這源于程序一開頭的一行代碼辕近。

self._person_counter = defaultdict(int)

想要理解這個defaultdict的作用,將我下面貼出來的這幾行代碼敲進去試試就知道了匿垄。它其實起到一個給字典的值設置一個默認值的快捷方式移宅。

from collections import defaultdict

person = defaultdict(int)

test_list = ['胡蘭成', '張愛玲', 'Bob', 'Bob', 'Nick', '胡蘭成']

for p in test_list:
    person[p] += 1
print(person)

可視化

可視化我是用到了Gephi這個軟件归粉。

PS下載這個軟件需要用到java組件,需要下載一個jre漏峰,聽過來人一句勸告糠悼,千萬不要下載最新的那個10的那個版本。

然后就是Gephi簡單的使用教程浅乔。放幾張網(wǎng)圖倔喂,大家學習一下。

新建工程
導入數(shù)據(jù)
選擇樣式
修改字體
調(diào)整布局
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末靖苇,一起剝皮案震驚了整個濱河市席噩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贤壁,老刑警劉巖班挖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芯砸,居然都是意外死亡萧芙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門假丧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來双揪,“玉大人,你說我怎么就攤上這事包帚∮嫫冢” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵渴邦,是天一觀的道長疯趟。 經(jīng)常有香客問我,道長谋梭,這世上最難降的妖魔是什么信峻? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮瓮床,結(jié)果婚禮上盹舞,老公的妹妹穿的比我還像新娘。我一直安慰自己隘庄,他們只是感情好踢步,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著丑掺,像睡著了一般获印。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上街州,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天兼丰,我揣著相機與錄音绰咽,去河邊找鬼。 笑死地粪,一個胖子當著我的面吹牛取募,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蟆技,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼玩敏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了质礼?” 一聲冷哼從身側(cè)響起旺聚,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎眶蕉,沒想到半個月后砰粹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡造挽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年碱璃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饭入。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡嵌器,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谐丢,到底是詐尸還是另有隱情爽航,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布乾忱,位于F島的核電站讥珍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏窄瘟。R本人自食惡果不足惜衷佃,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寞肖。 院中可真熱鬧纲酗,春花似錦、人聲如沸新蟆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琼稻。三九已至,卻和暖如春饶囚,著一層夾襖步出監(jiān)牢的瞬間帕翻,已是汗流浹背鸠补。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留嘀掸,地道東北人紫岩。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像睬塌,于是被迫代替她去往敵國和親泉蝌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 隨著天氣漸暖揩晴,每天六點多太陽就已高高升起勋陪,清晨的陽光透過窗簾,房間里漸漸明亮起來硫兰。而我再也不如之前那般貪睡诅愚。睜開雙...
    貓寧馨兒閱讀 569評論 3 7
  • 所有別人浪漫的事情,我都會被感動劫映,因為我希望它能發(fā)生在我身上~ 可是~你會給我一個浪漫的求婚嗎~
    坦蕩兮兮閱讀 170評論 0 0
  • 《螢火之森》 《夏目友人帳》 “此生無悔入夏目违孝,來世愿做帳中妖”
    我叫偶爾閱讀 760評論 0 0