返回文章列表

圖資料函式庫投影技術與應用

本文探討圖資料函式庫投影技術,使用 Neo4j 和 igraph 示範如何建立和應用投影,簡化複雜網路分析。文章涵蓋了投影的原理、Neo4j 資料匯入、索引設定、Cypher 查詢、Python 程式碼範例以及 igraph

圖資料函式庫 資料分析

在圖資料函式庫中,投影是一種簡化複雜網路分析的有效技術。透過投影,可以將多種型別的節點和邊簡化成更易於分析的結構,例如將二分圖投影成單分圖,以便應用社群檢測等圖演算法。雖然投影會損失部分資訊,但能提升分析效率和直觀性,更易於理解節點之間的關係。例如,在知識圖譜中,投影可以簡化多跳查詢,快速找到特定型別的節點之間的關聯,例如客戶地址與購買產品品牌之間的關係。

透過建立客戶與品牌之間的直接邊緣,可以有效地分析客戶地址與產品品牌之間的關聯,而無需遍歷多層關係。本文將以電影資料集為例,示範如何使用 Neo4j 和 igraph 建立和應用投影,分析演員的共同演出和電影的分享演員等資訊。

首先,將電影資料匯入 Neo4j,並建立索引以提升查詢效能。接著,使用 Cypher 查詢和 APOC 外掛程式建立節點和關係。然後,使用 Python 和 igraph 建立共同主演的演員投影,將演員和電影之間的 STARRING 關係轉換為演員之間的共同演出關係。最後,驗證投影結果,確保投影圖形與原始資料函式庫中的資訊一致。

投影(Projections)是什麼?

在處理與作者相關而非文章本身的問題時,我們可以使用投影。投影是一種圖形的表示方法,通常是將複雜的網路簡化。投影通常會將圖形資料簡化為特定實體之間的互動,從而在轉換過程中丟失部分資訊。儘管資訊會有所損失,但分析變得更加容易和直觀,並且能夠對圖形提出更直接的問題。

簡化複雜網路

圖8.2展示了圖8.1中二分圖(bipartite graph)的單一投影(unipartite projection)。現在的圖形只包含作者節點,如果原來的二分圖中的節點分享與同一篇文章的關係,則這些作者節點之間會分享一條邊。透過這種投影,我們丟失了一些資訊,例如每對作者合作的個別文章。然而,我們獲得了在同質圖上執行圖形演算法的能力,例如前面提到的社群檢測(community detection)。當我們的圖形只包含作者節點時,我們可以透過演算法合理地將圖形分成作者社群。

圖8.2 – 對應的作者圖單一投影

其他型別的異質網路可能更為複雜,包含多種節點和邊型別,例如知識圖譜。

知識圖譜的複雜性

知識圖譜可以在高度複雜的架構中表示豐富的資訊,例如包含詳細客戶資訊的圖形,其中包含許多不同的資訊關聯方式。

圖8.3 – 客戶資料圖表示例

在這種情況下,與二分圖不同,簡單的圖形測量(如某些型別節點的度數)將提供很少的有用資訊。如果我們在這個圖中找到客戶節點的度數,這是客戶購買的產品和他們的地址資訊的混合。

投影的使用

除了上述問題外,在具有大量節點和關係型別的圖形資料模型中,以有效的方式詢問某些型別的問題可能會很困難。考慮如何將地址資訊與購買產品的品牌聯絡起來;我們需要遍歷三個關係才能從地址節點到品牌節點。

圖8.3 – 異質客戶資料圖

相反,在這裡我們可以考慮使用客戶資料的圖形投影來回答特定問題,特別是如果這是一個我們可能經常詢問圖形資料函式庫的問題,例如作為推薦系統的一部分。

為了回答有關品牌與客戶地址之間關係的問題,我們可以將這兩種節點型別之間的路徑壓縮成一條單一的邊。在這樣做的過程中,我們失去了有關地址透過客戶和產品與品牌相關的方式的資訊,但透過大幅減少遍歷這些節點所需的跳數,我們大大提高了回答這些節點型別之間關係的問題的效率。現在,我們可以輕鬆計算與地址相關的品牌數量,或者可能與購買特定品牌的產品相關的位置。

這些與不同地址相關的品牌可以在下面的圖表中看到,建立了HAS_PURCHASED_FROM邊到節點的關係:

圖8.4 – 複雜異質客戶資料的二分投影

既然你已經瞭解了為什麼我們可能會使用投影,那麼在下一節中,我們將把這個想法付諸實踐。我們將探索一個大型的電影資訊資料集,建立投影以回答在異質知識圖譜中難以回答的一些問題,並使用Neo4j中的一些技巧以有效的方式處理資料。

如何使用投影

接下來的步驟將涉及設定Neo4j例項、建立資料儲存,並使用Cypher與我們的投影一起工作。這將結合我們在後續章節中學到的技能,並將它們匯集在一起:

  1. 首先,讓我們建立一個新的Neo4j資料函式庫,就像在過去幾章中所做的那樣,用於儲存我們的電影知識圖譜。我們將其命名為Projection DB,並再次使用密碼testpython,然後啟動資料函式庫。

  2. 正如我們在第5、6和7章中詳細介紹的那樣,我們將使用:server user add(在Neo4j瀏覽器中)建立一個新的管理員使用者,名稱為admin,密碼為testpython,並新增PUBLICadmin角色。有關如何執行此操作的更多詳細資訊,請參閱前三章中的任何一個。

  3. 在本章中,由於我們的資料集很大,我們將使用LOAD CSV將資料匯入Neo4j。要做到這一點,我們必須首先將movie.csv移動到Neo4j可以存取的資料夾中。在主要的Neo4j桌面視窗中,點選Projection DB旁邊的…圖示,然後選擇開啟資料夾 | 匯入。這將在檔案資源管理器中開啟該資料函式庫的資料匯入資料夾,然後我們應該將movie.csv(可以在支援的GitHub儲存函式庫的資料資料夾中找到)複製到其中。

內容解密:

此步驟指導如何在Neo4j中設定新的資料函式庫並匯入電影資料集。首先,我們需要建立一個新的Neo4j資料函式庫並設定管理員使用者。然後,我們使用LOAD CSV命令匯入電影資料,這需要將CSV檔案放在Neo4j可以存取的位置。透過這些步驟,我們為使用Cypher查詢語言與投影一起工作做好了準備。

程式碼範例

// 建立新的Neo4j資料函式庫
:server user add

// 使用LOAD CSV匯入資料
LOAD CSV FROM 'file:///movie.csv' AS row
// 這裡應該有進一步的Cypher查詢來處理匯入的資料

內容解密:

  • :server user add是用於在Neo4j瀏覽器中建立新使用者的命令。
  • LOAD CSV FROM 'file:///movie.csv' AS row是用於從CSV檔案匯入資料到Neo4j的Cypher查詢。路徑file:///movie.csv應該替換為實際的檔案路徑。
  • 進一步的Cypher查詢應該用於處理匯入的資料,例如建立節點和關係。

Neo4j 資料匯入與投影設定

在進行大規模圖資料函式庫的建立時,效能的最佳化至關重要。本章節將介紹如何使用Neo4j匯入大量電影相關資料,並進行適當的索引與投影設定,以確保資料函式庫的查詢效能。

匯入電影資料至Neo4j

首先,我們需要安裝Awesome Procedures on Cypher (APOC)外掛程式,以利用其提供的額外Cypher功能。安裝步驟如下:

  1. 開啟Neo4j Desktop主視窗,選擇對應的資料函式庫。
  2. 點選右側的Plugins分頁,找到APOC並點選Install。
  3. 安裝完成後,重啟資料函式庫。

檢查匯入資料

在匯入資料之前,我們先檢查movie.csv檔案的內容。該檔案包含電影相關的實體關係資料,每一行代表一個三元組(triple),格式為「實體1、關係、實體2」。例如:

002 Operazione Luna,COUNTRY,Italy
002 Operazione Luna,DIRECTOR,Lucio Fulci
002 Operazione Luna,STARRING,Ciccio Ingrassia

使用Python讀取該檔案並檢查資料規模:

import csv

with open('./data/movie.csv', 'r') as c:
    reader = csv.reader(c)
    data = [line for line in reader]
    print(len(data))

結果顯示該檔案包含超過41萬個三元組。進一步檢查關係型別的多樣性:

edge_types = set([triple[1] for triple in data])
print(edge_types)

輸出結果包含四種關係型別:AWARD、DIRECTOR、COUNTRY和STARRING。

建立索引以提升查詢效能

為了提升查詢效能,我們需要在Entity節點的name屬性上建立索引:

CREATE INDEX entity
FOR (e:Entity)
ON (e.name)

索引的好處包括:

  • 加速查詢效能
  • 減少遍歷時間
  • 提高排序效率
  • 增強可擴充套件性
  • 最佳化資源利用

使用LOAD CSV匯入資料

使用LOAD CSV陳述式匯入movie.csv檔案的資料,並利用APOC的外掛程式功能建立關係:

:auto LOAD CSV FROM 'file:///movie.csv' AS row
CALL {
  WITH row
  MERGE (n:Entity {name:row[0]})
  MERGE (m:Entity {name:row[2]})
  WITH n, m, row[1] as type
  CALL apoc.create.relationship(n, type, {}, m)
  YIELD rel
  RETURN 1 as x
} IN TRANSACTIONS OF 1000 ROWS
RETURN x

內容解密:

  1. LOAD CSV:從指定路徑讀取CSV檔案。
  2. CALL:使用子查詢處理每一行資料。
  3. MERGE:建立或匹配Entity節點。
  4. apoc.create.relationship:動態建立關係,關係型別由CSV檔案的第二列決定。
  5. IN TRANSACTIONS OF 1000 ROWS:分批處理資料,避免記憶體不足。

驗證匯入結果

執行以下Cypher查詢,驗證資料是否正確匯入:

MATCH (n:Entity {name:'002 Operazione Luna'})-[:COUNTRY]-(m:Entity {name:'Italy'})
RETURN n, m

該查詢匹配電影「002 Operazione Luna」與國家「Italy」之間的COUNTRY關係,並傳回相關節點。

圖示說明

此圖示呈現了電影「002 Operazione Luna」與國家「Italy」之間的COUNTRY關係。

透過上述步驟,我們成功地將電影相關資料匯入Neo4j,並進行了必要的索引設定,以確保資料函式庫的查詢效能。接下來,我們可以進一步利用Neo4j的圖演算法和投影功能,進行更深入的資料分析和挖掘。

如何使用投影

在將電影資料集載入永續性資料函式庫後,我們可以建立各種投影,以利用圖形資料回答不同的問題。如果我們想了解哪些演員在不同的電影中共同演出,我們可以建立一個共同主演的演員投影。我們還可以建立一個投影,深入研究哪些電影分享共同的演員,以及這受到拍攝國家/地區的影響程度。接下來,我們將討論如何生成這兩種預測,然後再使用投影來明智且有效地分析我們的資料。

在 igraph 中建立投影

讓我們透過從 Neo4j 資料函式庫讀取並將其匯入 igraph 來在 Python 中建立第一個投影。為此,我們將使用我們建立的套件來匯入我們將在這些圖形專案中重複使用的函式。我們將使用的函式是 Neo4jConnect 函式,可以從相關 GitHub 儲存函式庫中的 graphtastic 套件匯入(在本章的技術要求部分中有連結)。以下步驟將涵蓋匯入此模組以連線到 Neo4j,然後逐步使用 igraph 和 Python 建立投影。

  1. 從 graphtastic 套件匯入 Neo4jConnect 類別:
from graphtastic.database.neo4j import Neo4jConnect
  1. 現在我們已經有了連線到 Neo4j 資料函式庫的類別和方法,我們可以計算出如何以圖形投影的形式從 Neo4j 取得一些資料。我們的目標是建立一個包含有關哪些演員在電影中共演的資訊的圖形。在我們的 Neo4j 資料函式庫中,我們有 STARRING 關係,它將電影實體節點連線到演員實體節點。

  2. 讓我們透過在 Neo4j 瀏覽器中使用 Cypher 查詢一對共同出演同一部電影的演員,快速檢視這些關係。在這裡,我們可以使用具有特定 STARRING 關係型別的 MATCH 來識別正確型別的實體節點:

MATCH (act1:Entity)<-[:STARRING]-(film:Entity)-[:STARRING]->(act2:Entity)
RETURN act1, film, act2 LIMIT 1

從瀏覽器結果中,我們可以看到演員透過電影連線起來。但是,在我們的投影中,我們希望連線共同主演的演員。因此,我們在 Python 中執行的任何 Cypher 查詢都需要在我們的模式中找到節點和邊,並傳回包含演員資訊的節點。

  1. 知道了這一點,我們可以在 Python 中編寫一個方法來存取此演員資料,get_co_stars_neo4j()。此方法只會接受一個 Neo4j 連線物件,並使用我們在 Neo4j 瀏覽器中找到的模式,使用 MATCH 陳述句查詢共同主演的演員。我們只需要傳回模式邊緣的節點,這裡別名為 act1 和 act2。我們使用 connection.query() 方法執行查詢,並使用 result.data() 存取包含查詢結果的字典列表。最後一步,我們透過存取結果節點的 name 屬性(包含在字典中),並將其轉換為成對的共同主演算法員列表來清理資料結構:
def get_co_stars_neo4j(connection):
    query = 'MATCH (act1:Entity)<-[:STARRING]-(film:Entity)-[:STARRING]->(act2:Entity) RETURN act1, act2'
    result = connection.query(query).data()
    result = [[act['act1']['name'], act['act2']['name']] for act in result]
    return result
  1. 現在我們可以使用 Neo4jConnect 類別和我們的憑據建立一個 Neo4j 連線,並呼叫 get_co_stars_neo4j() 取得成對的共同主演算法員,然後記得關閉資料函式庫連線。讓我們透過列印前幾個成對的演員來確保一切如預期:
connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
co_stars = get_co_stars_neo4j(connection)
connection.close()
print(co_stars[:5])

內容解密:

此步驟驟在於建立與Neo4j資料函式庫的連線並提取共同主演的演員資料。

  • 使用Neo4jConnect類別建立連線。
  • 呼叫get_co_stars_neo4j()函式取得共同主演的演員對。
  • 使用Cypher查詢查詢共同主演的演員。
  • 將結果轉換為成對的列表。
  1. 有了包含演員成對的列表,我們基本上現在在 Python 中有了一個邊緣列表,我們可以用它來建立一個圖形。正如我們在前面的章節中所涵蓋的,我們還需要一個節點列表和屬性列表,以便在 igraph 中組裝它。

  2. 我們可以透過使用雙重列表理解來取得邊緣列表中的所有節點列表,並取該列表的集合,從而獲得一個唯一節點列表:

nodes = list(set([node for edge in co_stars for node in edge]))
  1. 此外,我們可以透過建立一個字典來為這些節點分配遞增的整數 igraph ID。使用字典理解,我們可以在唯一的節點列表上使用 enumerate(),為每個節點建立對映的遞增 ID:
igraph_ids = {film: node_id for node_id, film in enumerate(nodes)}

內容解密:

這裡介紹瞭如何處理節點和分配igraph ID。

  • 使用雙重列表理解取得唯一節點列表。
  • 使用enumerate()函式為每個節點分配唯一的igraph ID。
  1. 在 igraph 中,我們需要使用這些新的 ID 將邊緣新增到圖形中,因此我們必須用 igraph_ids 中的唯一 igraph ID 取代每個節點名稱。我們可以再次使用理解來完成此任務,從而得到一個由整數對組成的邊緣列表,對應於我們的電影:
edgelist = [[igraph_ids[n], igraph_ids[m]] for n, m in co_stars]

內容解密:

此步驟驟描述瞭如何使用igraph ID建立邊緣列表。

  • 將節點名稱替換為對應的igraph ID。
  • 生成一個整數對列表,表示圖形中的邊緣。
  1. 現在我們擁有了組裝圖形所需的一切: I. 首先,讓我們匯入 igraph 並使用 igraph.Graph() 建立一個空圖形。 II. 接下來,我們可以使用 add_vertices() 將節點新增到圖形中,相當於我們的唯一 igraph 節點 ID 的長度。 III. 然後,我們可以使用圖形物件 gadd_edges() 方法連結這些節點,並將 ID 的邊緣列表作為引數傳遞。 IV. 最後,透過存取 g.vs,我們可以將演員屬性逐一新增到每個節點,這是來自我們的唯一節點列表的每個演員的名稱:
import igraph
g = igraph.Graph()
g.add_vertices(len(igraph_ids))
g.add_edges(edgelist)
g.vs['actor'] = nodes

內容解密:

此部分描述瞭如何在igraph中建立和組態圖形。

  • 建立一個空圖形並新增節點和邊緣。
  • 為每個節點新增actor屬性。

圖形驗證

為了驗證我們的投影是否正確應用,讓我們在 igraph 圖形中找到一條邊緣,並在儲存在 Neo4j 中的原始圖形資料函式庫中找到相應的路徑。首先,透過存取圖形的 g.es 屬性並找到與索引 0 的邊緣相關聯的節點 ID,可以找到 igraph 圖形中的一條邊緣。使用這些 igraph 節點 ID,我們可以將此索引饋入 g.vs,並找到將用於在 Neo4j 中找到相應路徑的演員屬性,將它們分配給 actor1actor2 變數。

actor1 = g.vs[g.es[0].source]['actor']
actor2 = g.vs[g.es[0].target]['actor']

內容解密:

此步驟驟描述瞭如何驗證igraph中的邊緣是否正確對應於Neo4j中的路徑。

  • 使用g.es屬性存取邊緣。
  • 使用g.vs屬性找到與邊緣相關聯的演員。

在Neo4j中驗證路徑

現在,我們可以使用 Neo4jConnect 開啟與 Neo4j 的連線,並將這些演員名稱插入到 Cypher 查詢中。我們可以使用 f-strings 形成有效的 Cypher 查詢,尋找最初查詢 Neo4j 以建立投影的實體 | 電影 | 實體模式。傳回演員和電影後,我們可以使用 .data() 建立結果字典,然後使用 .close() 關閉連線並將其列印到控制檯:

connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
query = f'MATCH (act1:Entity {{name:"{actor1}"}})<-[:STARRING]-(film:Entity)-[:STARRING]->(act2:Entity {{name:"{actor2}"}}) RETURN act1, film, act2'
result = connection.query(query).data()
connection.close()
print(result)

內容解密:

此步驟驟描述瞭如何在Neo4j中驗證igraph中的邊緣。

  • 使用Cypher查詢驗證igraph中的邊緣是否正確對應於Neo4j中的路徑。
  • 傳回相關的實體和電影資訊以進行驗證。