風向往哪跑?¶

美國 2016 總統大選時期 Facebook 粉絲專頁與用戶行為的社會網路探討¶

台大經濟所 陳家威

LinkedIn Medium GitHub

2016 美國總統候選人希拉蕊與川普 圖片來源:The Altlantic


結論¶

  • 使用大選期間臉書用戶與分專的互動資訊,進行社會網路分析(Social Network Analysis, SNA)
  • 過去傾共和黨粉絲專頁的特徵中心性(Eigenvector Centrality)領先所有粉專
  • 在 2016/9/26 舉行第一次總統辯論,使得傾共和黨陣營的粉專,特徵中心性大幅下降
  • 透過 Louvain 分群法將粉專之間分陣營後,可以明顯看到共和黨與民主黨,在 9/25 的風向進行交叉,但隨後又回來

從美國大選期間,臉書(以下簡稱FB)的粉絲專頁以及用戶之間的互動關係,可以大致上了解一些選舉風向上的改變。

比方說,當中間選民發現某陣營的態度改變,或是習慣透過網軍操縱資訊後,他們對該陣營的支持度就有可能改變。而用戶對陣營的關注度與支持度,可以從用戶與不同粉專之間的互動行為略知一二。

這些互動行為構成了一個龐大的社會網路(Social network)。隨著時間的演進,這個社會網路的結構,用戶與粉專之間的互動強度,也會隨時間改變。

此研究將2016年 7/31 至 10/30 (美國大選前一週) 的臉書粉專與用戶互動數據進行社會網路分析。 此為擔任台大謝志昇教授研究助理期間所進行之研究的一小部分。 另外數據從 Facebook API 取得而來,資料清理過程與結果為研究機密,恕不公開。

讀取資料¶

原始資料龐大且繁雜,為了方便後續執行,我事先將資料改存為 .pkl 檔

import pandas as pd
import networkx as nx
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import plotly
from matplotlib import style
df = pd.read_pickle('DATA/light_data.pkl')
df = df[df["reaction_time"] != 0]               # 過濾掉沒有互動的組合
df
user_id page_id week_start_date reaction_time
4143404 502548149224 31160214090 2016-07-31 1
629806 502548149224 177486166274 2016-07-31 2
1394501 502548149224 109200595768753 2016-07-31 2
3579378 504535781680 199098633470668 2016-07-31 9
3308861 511651751124 124955570892789 2016-07-31 1
... ... ... ... ...
3758962 10211264967209740 444258668967650 2016-10-30 3
4320618 10211264967209740 889307941125736 2016-10-30 2
1847862 10211474845295627 47689998796 2016-10-30 1
3396153 10211474845295627 138691142964027 2016-10-30 3
4208844 10211474845295627 997108126967413 2016-10-30 1

5015383 rows × 4 columns

資料中, user_id表示用戶的臉書 id,page_id表示粉絲專頁的id。互動的次數在爬取資料時,已經有根據每週加總過,因此 week_start_date 表示該週第一天,而 reaction_time則為包含按讚與留言,該用戶在此分專在這個禮拜的互動次數。

其中 page_id 另外放在一個檔案中

page_info = pd.read_csv('DATA/1000-page-info.csv')[["page_id", "page_name"]]
page_info.head(10)
page_id page_name
0 15704546335 Fox News
1 153080620724 Donald J. Trump
2 346937065399354 Occupy Democrats
3 95475020353 Breitbart
4 889307941125736 Hillary Clinton
5 17614953850 Funny Or Die
6 7976226799 The Daily Show
7 18468761129 The Huffington Post
8 123624513983 Western Journalism
9 182919686769 The Daily Caller

社會網路分析¶

粉專與用戶之間的關係,可以畫成一個社會網路。

比方說,將用戶比喻為紅點,粉專比喻為藍點。每互動一次,就畫一條線。像這樣:

No description has been provided for this image

使用 networkx 套件,將上面粉專之間的關係建構為一個社會網路。因為有多個禮拜,所以每個禮拜都建一個

weeks = sorted(df.week_start_date.unique())

network_by_week = []

for week in weeks:
    df_this_week = df[df['week_start_date'] == week]
    week_string = pd.to_datetime(week).date()
    G = nx.Graph()

    G.add_nodes_from(df_this_week['user_id'].unique(), bipartite = 0)
    G.add_nodes_from(df_this_week['page_id'].unique(), bipartite = 1)
    G.add_weighted_edges_from(
       zip(df_this_week['user_id'], df_this_week['page_id'], df_this_week['reaction_time'])
    )

    network_by_week.append(G)

中心性¶

中心性顧名思義,就是一個衡量「有多具有中心地位」的概念。換句話說就是在社會網路中的「重要性」。一般來說有分為幾種中心性,分別從不同角度衡量重要性:

  • 度中心性(degree centrality):衡量有多少節點與之接觸。這是最直觀的中心性 - 連接的節點越多,各個節點就越重要。
  • 中介中心性(betweenness centrality):一個節點如果中介中心性高,表示他出現在很多組節點的最短路徑上。其他節點之間如果要傳遞資訊,會頻繁地通過這個節點。
  • 接近中心性(closeness centrality):衡量一個節點跟其他人所有人之間的路徑長短程度。接近中心性高,則接近哪一個節點都很快。這種節點不見得要連出去很多,但他要找誰都可以很快聯繫到。

不同的中心性,衡量了不同的概念。在統統大選的分析中,我認為最能代表「風向」概念的,是特徵(向量)中心性 (eigenvector centrality)

特徵向量中心性 (eigenvector centrality)¶

這個中心性非常難定義,但是卻有一個重要的性質:與他連結的人重要程度越高,他的重要程度就越高。 換句話說,一個粉專的特徵中心性,會因為與之連結的用戶的特徵中心性高,而跟著變高。其中比重與他的互動次數有關:

$$ C^{user}(u) = \frac{1}{\lambda} \sum_{p \in page} C^{page}(p) a_{up} \\ C^{page}(p) = \frac{1}{\lambda} \sum_{u \in user} C^{user}(u) a_{pu} $$

更簡單的說,今天某一個粉專的重要程度,就是把跟他互動的人的重要程度加起來。同理一個用戶的重要程度,就是把他互動的粉專的重要程度加起來。

今天如果共和黨勢如破竹,與之相同陣營的粉絲專頁也會獲得粉絲較多的互動,如此一來它的重要性也會被傳遞過去。

在上面的方程式中, $a_{up}$ 代表了用戶$u$與粉專$p$,在這一個禮拜的互動次數。我將互動次數作為重要性的加權。之後我也會把這個加權拿掉,看看結果如何。$\lambda$ 則是特徵值,可以視為一種伸縮量,以避免將其他節點的中心性加總後始自己的中心性過大。

為了要計算這種中心性,我們必須先瞭解一些社會網路以及圖論的數學。假如將這個 $a_{up}$建構成一個 $(n_u \times n_p)$ 矩陣 $A$,並且將所有用戶的中心性彙整成一個 $(n_u \times 1)$ 向量 $C^U$,所有粉專的中心性彙整成一個 $(n_p \times 1)$ 向量 $C^P$,那麼: $$ AC^P = \lambda C^U \\ A'C^U = \lambda C^P $$

合併可以得到 $$ A A' C^U = \lambda^2 C^U \\ A' A C^P = \lambda^2 C^P $$ 這樣就可以很明白得知,為何這樣的中心性被稱為「特徵向量中心性」了。

因此,要得到粉絲專頁的特徵中心性,首先需要得到這個 $A$ 矩陣(好像稱作附屬矩陣),再計算出 $A'A$ 的特徵向量。在 Faust (1997) 中說明到,要選用特徵值 $\lambda$ 最大的那一個特徵向量。以下會用 scipy 下的函數來計算特徵向量。由於用戶並不會與每一個粉專都有互動,因此 $A$ 矩陣元素會有許多 0,也就是一個稀疏矩陣 (sparse matrix)。數學上針對一堆空白的矩陣,有一套更快速計算的方法,所以我使用 scipy.sparse 底下的函數來計算

from scipy.sparse.linalg import eigsh

def eigenvector_centrality(network: nx.Graph):
    """Calculates the eigenvector centrality of a bipartite network"""
    user_nodes = sorted({n for n, d in network.nodes(data=True) if d["bipartite"] == 0})
    page_nodes = sorted({n for n, d in network.nodes(data=True) if d["bipartite"] == 1})

    A = nx.bipartite.biadjacency_matrix(network,
                                        row_order=user_nodes,
                                        column_order=page_nodes)

    evalue_page, evector_page = eigsh(
        (A.T @ A).asfptype(),   # A' A
        k=1,                    # calculate one only
        which='LA')             # get the largest

    # create a dict
    evector_page_dict = dict(zip(
                            page_nodes,
                            [abs(r[0]) for r in evector_page]
                        ))
    return evector_page_dict
centrality_by_week_page = [eigenvector_centrality(G) for G in network_by_week]

我將第一個禮拜的中心性,對應粉專名稱之後,由高到低排序:

first_week_centrality_df = (pd.DataFrame(
     centrality_by_week_page[0].items(),
     columns=["page_id", "eigenvector_centrality"]
     )
     .merge(page_info[["page_id", "page_name"]], on = "page_id")
     .sort_values(by = "eigenvector_centrality", ascending=False))

first_week_centrality_df.head(10)
page_id eigenvector_centrality page_name
370 153080620724 0.562609 Donald J. Trump
102 15704546335 0.517959 Fox News
575 133961323610549 0.289411 Donald Trump For President
310 95475020353 0.278827 Breitbart
139 22067606728 0.174215 Allen West
509 112723252096438 0.167596 The Political Insider
650 179035672287016 0.150079 American News
343 123624513983 0.133615 Western Journalism
701 226821494115353 0.120344 Nation In Distress
264 69813760388 0.112960 Sean Hannity

接著把每個禮拜的都整理在表格裡

centrality_panel_data = pd.DataFrame()

for w, cen in enumerate(centrality_by_week_page):
    week = weeks[w]
    centrality_df = pd.DataFrame(
        cen.items(),
        columns=["page_id", "eigenvector_centrality"]
    )
    centrality_df["week_start_date"] = week
    centrality_panel_data = pd.concat([centrality_panel_data, centrality_df])

centrality_panel_data = centrality_panel_data.merge(page_info, on = "page_id")
centrality_panel_data
page_id eigenvector_centrality week_start_date page_name
0 5281959998 0.002696 2016-07-31 The New York Times
1 5281959998 0.008039 2016-08-07 The New York Times
2 5281959998 0.002108 2016-08-14 The New York Times
3 5281959998 0.001636 2016-08-21 The New York Times
4 5281959998 0.001855 2016-08-28 The New York Times
... ... ... ... ...
13133 173347701125 0.000455 2016-10-02 Governor Jan Brewer
13134 173347701125 0.000204 2016-10-09 Governor Jan Brewer
13135 173347701125 0.019458 2016-10-16 Governor Jan Brewer
13136 173347701125 0.028357 2016-10-23 Governor Jan Brewer
13137 173347701125 0.033625 2016-10-30 Governor Jan Brewer

13138 rows × 4 columns

中心性的走向¶

我接著將第一個禮拜中心性前 10 高的粉專,未來中心性的走向畫出來。

top_10_in_first_week = list(centrality_panel_data[centrality_panel_data['week_start_date'] == weeks[0]]
                        .sort_values(by = "eigenvector_centrality", ascending = False)
                        .head(10)["page_id"])
centrality_panel_data[
    centrality_panel_data["page_id"].isin(top_10_in_first_week)
    ].pivot(
        index = "week_start_date", columns="page_name", values = "eigenvector_centrality"
    ).plot(
        labels = {
            "value" : "Centrality",
            "page_name": "Page Name",
            "week_start_date": "Date"
        },
        title = "Eigenvector Centrality of Fan Pages during 2016 Presidential Election <br>"+\
            "<sup>Top 10 in First Week</sup>"
    )

中心性前幾名的,包含川普個人粉專、Fox 新聞台、布萊巴特新聞網等等,都是較為傾向共和黨,也就是川普陣營的粉專。其中布萊巴特新聞網更是極端右翼的新聞媒體。第10名的最後一名 Sean Hannity 為保守派政治評論員,一樣也是傾向川普的右派支持者。

這些粉絲專頁在前幾週都的特徵中心性變化並沒有很大,但在 9月25的時候,有一個非常明顯的下降,可以說是幾乎為零。總統候選人川普的正式粉專,也逃不過這個事件的影響。

9月25那一週,發生什麼事?¶

2016 美國總統大選於 9/26 進行第一次總統候選人辯論會。而辯論結束之後,根據各新聞媒體民調,一律認為希拉蕊在辯論的表現贏過川普。以下內容擷取自維基百科的整理:

民調來源 希拉蕊贏 (%) 川普贏 (%) 無意見 (%)
FiveThirtyEight V -
CNN/ORC 62 27 -
Public Policy Polling 51 40 -
YouGov 57 30 -
Politico/Morning Consult 49 26 25
Echelon Insights 48 22 -
Reuters/Ipsos 56 26 -
NBC News/SurveyMonkey 52 21 26
Gallup 61 27 -
Fox News 61 21 -
ABC News/The Washington Post 53 18 -

因此,我們嘗試將 9/25 那一週,中心性最高的前十名,畫出來觀察:

top_10_in_9_25 = list(centrality_panel_data[centrality_panel_data['week_start_date'] == weeks[8]]
                        .sort_values(by = "eigenvector_centrality", ascending = False)
                        .head(10)["page_id"])
centrality_panel_data[
    centrality_panel_data["page_id"].isin(top_10_in_9_25)
    ].pivot(
        index = "week_start_date", columns="page_name", values = "eigenvector_centrality"
    ).plot(
        labels = {
            "value" : "Centrality",
            "page_name": "Page Name",
            "week_start_date": "Date"
        },
        title = "Eigenvector Centrality of Fan Pages during 2016 Presidential Election <br>"+\
        "<sup>Top 10 During Debate</sup>"
    )

果然可以看到,在 9/25 後面兩週,民主黨陣營的粉絲專頁,包然極左派粉專 Occupy Democrats、希拉蕊的官方粉專、哈芬登郵報(HuffPost),在特徵中心性上都來到了極高值。 這意味著以右派為首的粉絲專頁以及用戶的活躍程度大幅增加,其中也可能包含中間選民的態度改變。

以下將第一週前5名與第八週前5名並排呈現:

centrality_panel_data[
    centrality_panel_data["page_id"].isin(top_10_in_9_25[:5] + top_10_in_first_week[:5])
    ].pivot(
        index = "week_start_date", columns="page_name", values = "eigenvector_centrality"
    ).plot(
        labels = {
            "value" : "Centrality",
            "page_name": "Page Name",
            "week_start_date": "Date"
        },
        title = "Eigenvector Centrality of Fan Pages during 2016 Presidential Election <br>"+\
        "<sup>Wings Combined</sup>"
    )

互動次數為加權的影響¶

以上的分析中,都是將「互動次數」作為加權所計算出來的特徵向量,換句話說,是計算 $A'A$ 的特徵向量。然而,或許右派與左派支持者的互動頻率較為不同,比方說川普支持者或許較頻繁的按讚或是留言。這樣的行為是否值得被考慮在計算特徵中心性時作為加權,我認為有待商榷。因此以下提供另外一種計算特徵中心性的方法,不過著次只要有互動,無論多少次,都算為一個 1 : $$ b_{pn} = 1(a_{pn} \ge 1) $$

以下建立一個新的函數

from scipy.sparse.linalg import eigsh

def unweightet_eigenvector_centrality(network: nx.Graph):
    """Calculates the eigenvector centrality of a bipartite network"""
    user_nodes = sorted({n for n, d in network.nodes(data=True) if d["bipartite"] == 0})
    page_nodes = sorted({n for n, d in network.nodes(data=True) if d["bipartite"] == 1})

    A = (nx.bipartite.biadjacency_matrix(network,
                                        row_order=user_nodes,
                                        column_order=page_nodes) > 0) * 1

    evalue_page, evector_page = eigsh(
        (A.T @ A).asfptype(),   # A' A
        k=1,                    # calculate one only
        which='LA')             # get the largest

    # create a dict
    evector_page_dict = dict(zip(
                            page_nodes,
                            [abs(r[0]) for r in evector_page]
                        ))
    return evector_page_dict

與之前過程相同,建立每週每粉專的無加權特徵中心性資料

u_centrality_by_week_page = [unweightet_eigenvector_centrality(G) for G in network_by_week]
u_centrality_panel_data = pd.DataFrame()

for w, cen in enumerate(u_centrality_by_week_page):
    week = weeks[w]
    centrality_df = pd.DataFrame(
        cen.items(),
        columns=["page_id", "unweighted_eigenvector_centrality"]
    )
    centrality_df["week_start_date"] = week
    u_centrality_panel_data = pd.concat([u_centrality_panel_data, centrality_df])

u_centrality_panel_data = u_centrality_panel_data.merge(page_info, on = "page_id")

接著將第一週5五與第八週前5,一併畫在圖表中

page_from_left_right = \
        list(u_centrality_panel_data[u_centrality_panel_data['week_start_date'] == weeks[0]]
            .sort_values(by = "unweighted_eigenvector_centrality", ascending = False)
            .head(5)["page_id"]) + \
        list(u_centrality_panel_data[centrality_panel_data['week_start_date'] == weeks[8]]
            .sort_values(by = "unweighted_eigenvector_centrality", ascending = False)
            .head(5)["page_id"])


u_centrality_panel_data[
    u_centrality_panel_data["page_id"].isin(page_from_left_right)
    ].pivot(
        index = "week_start_date", columns="page_name", values = "unweighted_eigenvector_centrality"
    ).plot(
        labels = {
            "value" : "Centrality",
            "page_name": "Page Name",
            "week_start_date": "Date"
        },
        title = "Unweighted Eigenvector Centrality of Fan Pages during 2016 Presidential Election <br>"+\
            "<sup>Top 5 in Frist Week and Top 5 During Debate</sup>"
    )

中心性的變動比較沒有先前這麼極端,但同時還是可以觀察到在總統大選辯論會之後,雙方陣營的中心性有明顯的交叉,而三個禮拜之後,傾共和黨粉專的中心性又慢慢回升了。


粉專政黨傾向 -- 自動分類¶

以上的分析都是藉由 Google 以及查證的方式,來將粉專進行政治傾向的分類。如果用戶本身偏向某一政黨,則他在FB上的互動也會透露他的政黨傾向。比方說,平常按川普貼文讚的用戶,高幾率也會與 Fox News 以及他的後援會互動。

由於先前已經建構出粉專與用戶之間的互動網路,因此可以藉由 群體偵測 (community detection) 的方式,為不同粉專進行分類。也可以看看有沒有一些粉專,過去的用戶偏向共和黨,而後來搖擺至民主黨。

Louvain 演算法¶

在早期應用中,進行群體偵測的演算法為 GN 演算法。但由於這個演算法需要計算每一條邊作為最短路徑的重要性 (edge betweenness centrality),因此十分費時。不過這個演算法為社會網路研究提供了一嶄新的明燈,那就是 模組度 (modularity) 的概念。模組度可以幫助判斷,這樣的分群算不算一種好的分群

$$ Q = \frac{1}{2m} \sum{ij} \left[ A_{ij} -\frac{k_i k_j}{2m} \right] \delta(C_i, C_j) $$

其中 $\frac{k_i k_j}{2m}$ 可以看成兩的節點之間該有多少權重的期望值,而與 $A_{ij}$的差異,就是他偏離期望的程度。這個差異只有在兩個節點在同一個群體之內有意義,因為如果兩個節點之間的連接權重多出越多,直觀上就越值得被分在同一群組內。因此乘上$\delta(C_i, C_j)=1 \quad \text{if} C_i = C_j$,也就是只有 i 與 j 在同一個群組內,才會是 1。

越好的分群,這個值應該要越大,因為如果兩個節點之間的連接程度高過於平均($A_{ij} - \frac{k_i k_j}{2m}$ 很大),卻被分到不同群 ($\delta(C_i, C_j) = 0$),這個 Q 就會變小。 分群問題變成一個極大化的問題。Blondel et al.(2008) 在 Louvain 大學發明了一種貪婪演算法,來最大化這個模組度 Q。

這個非監督式的分群方法可以非常快,過去也被拿來分析 Twitter 還有手機通訊等等資料。這些資料動輒上億個節點,所以用在我們的資料是綽綽有餘。

因為我們只對粉專有興趣,因此將原本兩種節點的社會網路,結合成單一節點的網路。假如用戶 u 同時對 A B 粉專按讚,則 A 與 B 就獲得一個關聯。

from itertools import count
def gen_page_comembership_graph(G:nx.Graph):
    """Turns a bipartite graph into page only, preserving the node names"""
    user_nodes = sorted({n for n, d in G.nodes(data=True) if d["bipartite"] == 0})
    page_nodes = sorted({n for n, d in G.nodes(data=True) if d["bipartite"] == 1})

    # use unweighted
    A = (nx.bipartite.biadjacency_matrix(G, row_order=user_nodes, column_order=page_nodes) > 0)* 1

    page_comembership_matrix = (A.T @ A).asfptype()

    new_G = nx.from_scipy_sparse_array(page_comembership_matrix)
    mapping = {u:v for u, v in zip(count(), page_nodes)}
    return nx.relabel_nodes(new_G, mapping)
page_comembership_graph_by_week = [gen_page_comembership_graph(G)
                                   for G in network_by_week]

communities_by_week: list[list[set]] = [nx.community.louvain_communities(G)
                                        for G in page_comembership_graph_by_week]

communities_by_week 包含了每一個禮拜的分群。每一個禮拜包含了數個經Louvain分群之後的結果。

我進一步將包含川普的分群標示為共和黨"Republican"、包含希拉蕊的分群標為民主黨 "Democrat"、另外還有另一位總統候選人 Gary Johnson 所在的自由意志黨 "Libertarian" 。其餘分類為"Others"。

ideology_panel = pd.DataFrame()

trump_page_id = 153080620724
clinton_page_id = 889307941125736
johnson_page_id = 165297924363

for w, community_sets in enumerate(communities_by_week):
    week = weeks[w]

    trump_community = []
    clinton_community = []
    johnson_community = []
    other = []

    for s in community_sets:
        if trump_page_id in s:
            trump_community = list(s)
            continue

        if clinton_page_id in s:
            clinton_community = list(s)
            continue

        if johnson_page_id in s:
            johnson_community = list(s)
            continue

        other += list(s)

    # prepare for dataframe
    temp_ideology_df = pd.DataFrame(
        {
            'page_id': trump_community + clinton_community + johnson_community + other,
            'community': ["Republican"] * len(trump_community) +
                         ["Democrat"] * len(clinton_community) +
                         ["Libertarian"] * len(johnson_community) +
                          ["Others"] * len(other)
        }
    )
    temp_ideology_df['week_start_date'] = week
    ideology_panel = pd.concat([ideology_panel, temp_ideology_df])

把上面的分群結合到有特徵中心性的表格中

u_centrality_panel_data_community = u_centrality_panel_data.merge(
    ideology_panel, on = ["week_start_date", "page_id"])

u_centrality_panel_data_community[
    u_centrality_panel_data_community.page_id.isin(page_from_left_right)
    ].query("week_start_date == '2016-07-31'").head(10)
page_id unweighted_eigenvector_centrality week_start_date page_name community
1421 15704546335 0.463815 2016-07-31 Fox News Republican
1547 18468761129 0.063269 2016-07-31 The Huffington Post Democrat
1939 22067606728 0.172206 2016-07-31 Allen West Republican
5170 153080620724 0.400613 2016-07-31 Donald J. Trump Republican
7102 112723252096438 0.203428 2016-07-31 The Political Insider Republican
7256 114517875225866 0.042081 2016-07-31 The Other 98% Democrat
9071 179035672287016 0.239638 2016-07-31 American News Republican
11026 346937065399354 0.063586 2016-07-31 Occupy Democrats Democrat
12862 889307941125736 0.032486 2016-07-31 Hillary Clinton Democrat

這樣一來,每一個粉專除了有當週的特徵中心性以外,也包含了這個粉專在當週的分群。為了確認一下分群的效果,我將幾個主要粉專的分群印出來:

test_page_name = ["Fox News", "CNN", "Donald J. Trump", "Hillary Clinton", "Occupy Democrats", 
                  "The Huffington Post", "The Political Insider", "American News"]

u_centrality_panel_data_community[
    u_centrality_panel_data_community.page_name.isin(test_page_name)
].pivot(
    index = "week_start_date",
    columns = "page_name",
    values = "community"
)[test_page_name]
page_name Fox News CNN Donald J. Trump Hillary Clinton Occupy Democrats The Huffington Post The Political Insider American News
week_start_date
2016-07-31 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-08-07 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-08-14 Republican Others Republican Democrat Democrat Democrat Republican Republican
2016-08-21 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-08-28 Republican Others Republican Democrat Democrat Democrat Republican Republican
2016-09-04 Republican Others Republican Democrat Democrat Democrat Republican Republican
2016-09-11 Republican Others Republican Democrat Democrat Democrat Republican Republican
2016-09-18 Republican Others Republican Democrat Democrat Democrat Republican Republican
2016-09-25 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-10-02 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-10-09 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-10-16 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-10-23 Republican Democrat Republican Democrat Democrat Democrat Republican Republican
2016-10-30 Republican Democrat Republican Democrat Democrat Democrat Republican Republican

看上去,陣營非常明確的幾個粉專以及新聞網,是有辦法被正確透過分群演算法進行分類以及判定的。

接下來,可以回答前面的一個觀察:是否在總統大選之後,傾向共和黨的粉專,其中心性平均而言都降低了?

我們可以先將不同陣營的中心性進行加總:

ideology_centrality_mean_panel = (u_centrality_panel_data_community
                        .query("community != 'Others'")
                        .pivot_table(
                            index = "week_start_date",
                            columns = "community",
                            values = "unweighted_eigenvector_centrality",
                            aggfunc= np.mean
                        )
                    )
ideology_centrality_mean_panel
community Democrat Libertarian Republican
week_start_date
2016-07-31 0.005015 0.002686 0.028381
2016-08-07 0.008039 0.003409 0.027957
2016-08-14 0.004319 0.002716 0.030536
2016-08-21 0.003619 0.003457 0.030032
2016-08-28 0.004382 0.003694 0.029484
2016-09-04 0.003438 0.002570 0.025865
2016-09-11 0.005770 0.002940 0.028071
2016-09-18 0.008601 0.003601 0.025698
2016-09-25 0.019583 0.002632 0.013086
2016-10-02 0.006693 0.002565 0.027114
2016-10-09 0.005332 0.002874 0.026586
2016-10-16 0.006705 0.003947 0.027352
2016-10-23 0.003587 0.003563 0.027935
2016-10-30 0.004020 0.003357 0.028640

將上面的變化以及標準差視覺化如下。(使圖形轉成可互動的套件 plotly無法成功產出以下圖形,因此改使用 seaborn 繪製)

fig, ax = plt.subplots(1,1, figsize = (10,5))
sns.lineplot(
    u_centrality_panel_data_community, 
    x = "week_start_date", y = "unweighted_eigenvector_centrality", 
    hue = "community", ax = ax)

ax.set_ylabel("Centrality")
ax.set_xlabel("Date")
ax.legend(title = "Ideology",bbox_to_anchor=(1.02, 0.55), loc='upper left', borderaxespad=0)
ax.set_title("Average Unweighted Eigenvector Centrality Based on Ideology", size = 15)
fig = ax.get_figure()
No description has been provided for this image

加入變異數之後,可以看到兩陣營在 9/25 那一週的平均特徵中心性,的確產生了一個明顯的變化。

結論¶

此研究結合了社會網路研究中,衡量中心性的方法,以及分群的方法。透過分析 2016 美國總統大選期間幾位候選人以及其政治傾向的粉專與臉書用戶之間的互動關係,可以得到以下結論

  • 過去親共和黨粉絲專頁的特徵中心性領先所有粉專
  • 在 2016/9/25 那一個禮拜,第一次總統辯論,偏共和黨陣營的粉專,特徵中心性大幅下降
  • 透過 Louvain 分群法將粉專之間分陣營後,可以明顯看到共和黨與民主黨,在 9/25 的風向進行交叉,但隨後又回來