天天看點

Python實作《都挺好》社交網絡分析

目錄

    • 1《都挺好》簡介
    • 2人物共同出現頻數
      • 2.1制作主要人物字典
      • 2.2計算人物互相出現的頻數
    • 3繪制人物關系圖
    • 4重要人物
    • 5社群發現
    • 6人物相關系數
    • 7随時間變化的人物關系
    • 8最後

1《都挺好》簡介

該小說講述了職場金領蘇明玉從小不受家人待見,生長在家庭的邊緣,在孤獨扭曲的環境中長大成人的故事。結局是這樣的,蘇明哲回到了美國,蘇明成和朱麗離婚後不知所蹤,蘇明玉和石天冬在一起了,三個子女看清了蘇大強的為人,沒有人再管着他,最終蘇大強得了老年癡呆症,蘇明玉于心不忍,是以把他接回家照顧。

Python實作《都挺好》社交網絡分析

上圖是去除停用詞、去除人名、隻保留形容詞和名詞的小說詞雲圖;由圖中也可以看出基本都是和家庭相關的詞語。

# 小說詞雲
def getWordCloud():
    rlist=[]
    for s in content:
        for w in pseg.cut(str(s)):
            if(w.word not in stopdict and w.flag=='a' or w.flag=='n'):
                rlist.append(w.word)
    rlist = " ".join(rlist)
    w = WordCloud(font_path="C:/Users/Windows/fonts/simkai.ttf",width=600,height=400,background_color="black",max_words=50)
    w.generate(rlist)
    path=r'C:\Users\Administrator\Desktop\pic.png'
    w.to_file(path)
           

2人物共同出現頻數

2.1制作主要人物字典

需要把《都挺好》小說txt版下載下傳下來,剛開始我是通過jieba分詞把所有nr(人名)的詞語儲存下來,作為人物集合,但是①某個人物多個稱呼 ②nr類型會出現許多不合适的詞

words=pseg.cut(single)
        for w in words:
            if(w.flag=='nr')
           

參考電視貓中的人物關系圖,單獨制作了主要人物詞典

Python實作《都挺好》社交網絡分析
#主要人物 姓名詞典
pdict={'明玉':['明玉','蘇明玉','蘇總'],'朱麗':['朱麗','麗麗'],'明哲':['明哲','蘇明哲','大哥'],
       '明成':['明成','蘇明成','大哥'],'蘇大強':['蘇大強','老爹','爸'],'吳非':['吳非'],'蘇家':['蘇家'],
       '寶寶':['寶寶'],'老蒙':['老蒙','蒙總'],'小蒙':['小蒙'],'天冬':['天冬','石天冬','石大哥'],
       '柳青':['柳青'],'蔡根花':['蔡根花'],'鐘點工':['鐘點工'],'溫玮光':['溫玮光','溫總'],'蘇母':['蘇母']}

#判斷na是哪個人物
def getPerson(na):
    for key,value in pdict.items():
        if(na in value):
            return str(key)
    return '無'
           

2.2計算人物互相出現的頻數

(1)首先初始化一個16×16的人物頻數0矩陣。

(2)在小說的每一段中,當這一段中出現了詞典中的人物時,就把出現的人物兩兩對應的頻數+1。

(3)周遊頻數矩陣的右上半邊(或左下半邊)獲得人物兩兩之間的頻數。

# 計算人物互相出現的頻數 儲存成頂點-權重
def getPeopleNetwork():
    plist=list(pdict.keys())
    N=len(plist)
    pcount=np.zeros([N,N])
    #初始化 人物頻數 二維矩陣
    pcount=pd.DataFrame(data=pcount,columns=plist,index=plist)
   
    #周遊每一段,出現的人物 頻數+1
    for sentence in content:
        s_plist=[]#出現的人物集合
        words=jieba.cut(sentence)
        for w in words:
            if(getPerson(w)!='無' and getPerson(w) not in s_plist):
                s_plist.append(getPerson(w))
        #根據出現的人物集合 進行頻數+1
        for p1 in s_plist:
            for p2 in s_plist:
                pcount.loc[p1][p2]+=1

    #儲存頻數二維矩陣
    pcount.to_excel('./data/人物頻數矩陣.xlsx')
    print('w ok')

    #根據頻數二維矩陣 計算 人物1-人物2-權重
    p_network=[]
    for i in np.arange(N):
        for j in np.arange(i+1,N):
            p_network.append([plist[i],plist[j],pcount.iloc[i,j]])

    #儲存 邊-權重
    p_network=pd.DataFrame(data=p_network,columns=['人物1','人物2','count'])
    p_network.to_excel('./data/權重.xlsx')
    print('w ok')

           

3繪制人物關系圖

節點越大,代表該人物越重要。根據頻數範圍(400,200,100,30),将邊劃分成五個程度,紅藍黃紫橙,關系依次減弱。

Python實作《都挺好》社交網絡分析

一共有5條出現次數超過400的關系,其中3條關系是明哲、明成、明玉這三兄妹之間的關系,另外兩條是明成、明玉和各自配偶的關系;而蘇大強和三位子女的關系也較強,均超過200次,稍弱于三兄妹之間的關系。這表明《都挺好》小說主要圍繞三兄妹和父親進行劇情發展,這也符合我們的實際認知。

#畫出第c1-c2章的社交網絡圖
def drawNetwork(c1,c2):
    data=pd.DataFrame(pd.read_excel('./data/權重-.xlsx',sheet_name=0)) 
    #data=data[data['人物1']!=data['人物2']]
    data=data[(data['chapter']>=c1)&(data['chapter']<=c2)]
    data=data.groupby([data['人物1'],data['人物2']])['count'].sum().reset_index()
    data=data[data['count']!=0]
    G=nx.Graph()
    for index,item in data.iterrows():
        G.add_weighted_edges_from([(item['人物1'],item['人物2'],item['count'])])
    pos = nx.spring_layout(G)
    nx.draw_networkx_labels(G,pos,font_size=11,font_family='simhei')#畫标簽
    nx.draw_networkx_nodes(G,pos,nodelist=list(nx.algorithms.pagerank(G).keys()),node_size=[x*3000 for x in list(nx.algorithms.pagerank(G).values())],node_color='lightgreen') 

    if(c1==1 and c2==40):
        #不同聯系強度的邊集合 
        degree1=[(x,y) for (x,y,z)in G.edges(data=True) if z['weight']<30]
        degree2= [(x, y) for (x, y, z) in G.edges(data=True) if 30<=z['weight'] < 100]
        degree3= [(x, y) for (x, y, z) in G.edges(data=True) if 100<=z['weight'] < 200]
        degree4= [(x, y) for (x, y, z) in G.edges(data=True) if 200<=z['weight'] < 400]
        degree5=[(x,y) for (x,y,z)in G.edges(data=True) if z['weight']>=400]

        #不同強度的邊 寬度、顔色 不同
        nx.draw_networkx_edges(G, pos, edgelist=degree1, width=0.5, edge_color='orange')
        nx.draw_networkx_edges(G, pos, edgelist=degree2, width=1.5, edge_color='m')
        nx.draw_networkx_edges(G, pos, edgelist=degree3, width=3, edge_color='yellow')
        nx.draw_networkx_edges(G, pos, edgelist=degree4, width=4, edge_color='cornflowerblue')
        nx.draw_networkx_edges(G,pos,edgelist=degree5,width=6,edge_color='red')
    else:
        for e in G.edges(data=True):
            nx.draw_networkx_edges(G,pos,edgelist=[e],width=e[2]['weight']/10,edge_color='red')
 
    plt.axis('off')
    plt.title('第{}章到第{}章人物關系網絡圖'.format(c1,c2))
    plt.show()
           

4重要人物

調用PageRank算法,可以獲得每個節點人物的pr值,我們以此值來衡量人物的重要程度。作為該小說的主角,明玉當仁不讓位居第一;值得一提的是,三兄妹位于TOP3,三兄妹及其配偶、父親蘇大強七位人物位于TOP7。

Python實作《都挺好》社交網絡分析
page_ranks = pd.Series(nx.algorithms.pagerank(G)).sort_values()
 page_ranks.plot(kind="barh")
 plt.title('pagerank')
 plt.show()
           

5社群發現

結果将《都挺好》的劇情人物自動劃分成兩個社群:社群1是以明玉為代表的其配偶、生意夥伴;社群2是除了明玉以外的蘇家人物集合。很明顯,明玉由于從小在家裡不受喜歡,長大後和整個蘇家是聯系較弱的(雖然内心還是有愛的)。可見算法生成的結果也是符合情理的。

Python實作《都挺好》社交網絡分析
#step2 畫社群 都在一張圖上
    part = community.best_partition(G)
    values = [part.get(node) for node in G.nodes()] #社群
    nx.draw_networkx_nodes(G,pos,alpha=0.6, node_color=values,node_size=400)
    nx.draw_networkx_edges(G,pos,edgelist=G.edges,width=0.5,edge_color='orange')
    nx.draw_networkx_labels(G,pos,font_size=12,font_family='simhei')
    plt.axis('off')
    plt.title('社群發現')
    plt.show()
           

6人物相關系數

相關系數(x,y)的值代表x和y的聯系度,計算方式是x和y都出現的段落/x的段落。圖中橫着為y,縱着為x。比如人物x和人物y之間共同出現了100次,但是x出現了200次,y出現了1000次;顯然y對于x來說更重要,因為y占據了x的50%,而x占據了y的10%。

Python實作《都挺好》社交網絡分析

(1)明玉整體來說對于其他人物來說都比較重要,她對于蘇家、小蒙、天冬、柳青、溫總的相關系數均超過0.6,除了鐘點工和蔡根花,明玉對于其他人的相關系數也較強。

(2)明哲、明成、蘇大強對于其他人物來說也較為重要。

(3)和蘇大強聯系最緊密的三位人物是:明哲-0.48、明成-0.46、明玉-0.33,這表明大兒子和二兒子對于蘇大強的重要程度相近,都領先于三女兒對其的重要程度,小說中蘇大強跟着二兒子住,一門心思想跟着大兒子去美國享福,是以兩人的相關系數最高;這也側面印證了明玉在蘇家不那麼受重視。

(4)和明玉聯系最緊密的兩位人物是明成-0.24和天冬-0.21,和明成聯系緊密是因為兩人沖突沖突很大,小說中甚至有明成去毆打明玉的情節;和天冬聯系緊密則是因為兩人的情侶關系。

(5)有趣的是,對于蔡根花來說,蘇大強是其最重要人物,相關系數高達0.65;作為蘇大強的保姆,兩人的黃昏戀可以說是讓人啼笑皆非,在電視劇熱播之後“蔡根花寶貝”、“圖你年紀大、圖你不洗澡”也成為流行一時的梗。

#人物相關系數
def getPeopleRelation():
    pcount=pd.DataFrame(pd.read_excel('./data/人物頻數矩陣.xlsx',sheet_name=0,index_col=0))#第0列作索引
    N=pcount.shape[0]
    secondmax=0
    for i in np.arange(N):
        count=pcount.iloc[i][i]
        for j in np.arange(N):
            pcount.iloc[i,j]/=count
            #if(pcount.iloc[i,j]>secondmax and pcount.iloc[i,j]!=1):
            #    secondmax=pcount.iloc[i,j]
    print(pcount)
    ax = plt.axes() 
    im = ax.imshow(pcount,cmap=plt.cm.summer,interpolation='none',vmin=0,vmax=0.6,aspect='equal')#vmax如果是1,圖會很稀疏
    plt.colorbar(im, shrink=0.5)
    plt.xticks(np.arange(N),list(pcount.index))
    plt.yticks(np.arange(N),list(pcount.index))
    plt.title('人物相關系數矩陣')
    plt.show()
    pcount.to_excel('./data/人物相關系數矩陣.xlsx')
    print('w ok')
           

7随時間變化的人物關系

下載下傳得到的《都挺好》小說中有40章,每一章的标題都以對應的數字顯示,是以可以計算人物頻數時,再加一列數字來表明是第幾章。我以每5章為一時間段,制作八個時間段的人物關系網絡圖。

Python實作《都挺好》社交網絡分析
Python實作《都挺好》社交網絡分析

比如以上面兩張圖為例,分别是1-5章和6-10章的人物關系網絡圖。可以看出:前5章就主要是三兄妹家庭之間的聯系;而6-10章明玉與天冬、柳青、老蒙的聯系突然增強,可能這幾章在講明玉與蘇家之間的關系的同時,也兼顧講述了明玉工作上的情況,是以這些生意夥伴的聯系突然增多。

為了顯示某兩個人物随時間變化的關系,我重新計算八個時間段内的某兩個人物的出現次數和相關系數,挑選以下幾個進行說明。

(1)明玉對于蘇大強的相關系數

從第2時間段的0.2左右開始,明玉對于蘇大強的相關系數一直随時間增加到第7時間段的0.45左右,最後一個時間段稍微減少至0.4左右;這表明明玉對于蘇大強的重要性在前期較弱,但是随着時間發展,明玉越來越重要,直到最後重要性比第2時間段的重要性多了1倍;由于結局是明玉照顧得了老年癡呆的蘇大強,是以明顯在後面幾個時間段,明玉對于蘇大強的關聯都較強。

Python實作《都挺好》社交網絡分析

(2)朱麗對于明成的相關系數

從第5時間段的峰值0.6左右開始逐漸減弱,直至最後的0.1左右;這與小說中兩人關系破裂最終離婚的劇情也是相符合的。

Python實作《都挺好》社交網絡分析
#擷取人物關系随時間變化
def getTimePeople(p1,p2):
    data=pd.DataFrame(pd.read_excel('./data/權重-.xlsx',sheet_name=0)) 
    y=[] #相關系數
    y1=[]#p1出現次數
    y2=[]#p1 p2共同出現次數
    for c in np.arange(1,40,5): 
        mdata=data[(data['chapter']>=c)&(data['chapter']<=c+4)]
        mdata=mdata.groupby([mdata['人物1'],mdata['人物2']])['count'].sum().reset_index()
        mdata=mdata[(mdata['人物1']==p1)|(mdata['人物2']==p1)]
        for index,item in mdata[(mdata['人物1']==p1)&(mdata['人物2']==p1)].iterrows():
            count=item[2]
        y1.append(count)
        mdata=mdata[(mdata['人物1']==p2)|(mdata['人物2']==p2)]
        for index,item in mdata.iterrows():
            y2.append(item[2])
            y.append(item[2]/count if count>0 else 0)
 
    plt.figure()
    plt.subplot(2, 1, 1)
    plt.title('{p2}對于{p1}的相關系數'.format(p2=p2,p1=p1))
    plt.plot(np.arange(1,9),y,color='red')
    plt.subplot(2, 1, 2)
    plt.bar(np.arange(1,9),y1)
    plt.bar(np.arange(1,9),y2)
    plt.legend(labels=['{p1}的出現次數'.format(p1=p1),'{p1}和{p2}共同出現的次數'.format(p1=p1,p2=p2)],loc='best')
    plt.show()
           

8最後

明玉從被蘇家嫌棄的三女兒成長為職場女強人,映證着小說結尾的一句話,“自己過得好,才是一切”。在生活這張複雜網絡下,每個人又何嘗不是一個節點,不斷努力豐富自己,也許才能更自主的選擇周圍的邊和聯系程度,也許有一天會發現自己所處的“小世界”一切都挺好。

問題:按照每段中出現了不同的人物,就兩兩進行頻數+1的處理思路,但是這樣同時有個問題,就是當一段中出現同一個人物不隻一次時的情況如何處理,比如某一段出現了A3次,B2次,C1次:

①本作業中使用的是不重複統計,即AA BB CC AB AC BC BA BC CB的次數都+1;

②但是我個人感覺按次數計算也合理,即AA+3 BB+2 CC+1 AB+6 AC+3 BC+2 BA+6 CA+3 CB+2;剛開始我是這麼計算的,進行社群發現之後的結果分成了三個社群:明玉及其生意夥伴;明成、朱麗、鐘點工小家庭;明哲、蘇大強以及蘇家其他的人;當時認為這個社群發現結果很合理,三兄妹直接分成對應的三個社群,但是由于這樣處理頻數是虛高的,是以還是放棄了這種方法,直接用①方法進行處理。

是以針對人物共同出現頻數,我也沒有去細究如何處理。

參考文章:

https://blog.csdn.net/blmoistawinde/article/details/85344906

https://blog.csdn.net/csdnnews/article/details/84312510

https://blog.csdn.net/erin_hh/article/details/52938211

繼續閱讀