返回文章列表

圖資料函式庫進階操作與Python整合應用

本文探討圖資料函式庫的高階操作與 Python 整合應用,涵蓋 Cypher 查詢語言進階應用、Python 連線 Neo4j 資料函式庫、執行 Cypher 查詢、驗證查詢結果、建立可重用的 Python 類別、資料預處理、將節點、邊緣和屬性移至 Neo4j、圖表結構描述、載入資料到 Neo4j 並進行

圖資料函式庫 Python

本文介紹如何使用 Cypher 查詢語言進行複雜的圖資料分析與操作,並示範如何透過 Python 的 neo4j 模組與 Neo4j 圖資料函式庫互動,包含連線、執行查詢、驗證結果等步驟。同時,文章也提供了一個名為 Neo4jConnect 的 Python 類別,用於簡化與 Neo4j 資料函式庫的互動流程,讓開發者更方便地在 Python 中執行 Cypher 查詢,並將圖資料函式庫整合到應用程式中。文章進一步說明如何處理和載入圖表資料到 Neo4j,包括資料預處理、CSV 檔案匯入以及圖表結構描述。最後,文章示範如何利用 Python 和 Cypher 進行進階查詢和分析,例如查詢特定節點、邊緣資訊,以及如何使用 Dijkstra 演算法最佳化旅行路徑規劃。

使用圖資料函式庫的高階操作與Python整合應用

圖資料函式庫查詢與操作探討

在前面的章節中,我們已經初步瞭解了圖資料函式庫的基本操作。現在,我們將探討如何使用Cypher查詢語言進行更複雜的資料分析與操作,並學習如何透過Python與Neo4j圖資料函式庫進行互動。

Cypher查詢語言進階應用

Cypher不僅可以用於基本的資料增刪改查,還支援豐富的聚合函式,如count()sum()max()等,能夠對查詢結果進行深入分析。例如,若要統計資料函式庫中的節點數量,可以使用以下查詢:

MATCH (n)
RETURN count(n)

此外,Cypher還允許我們使用DETACH DELETE陳述式來刪除節點及其相關的邊:

MATCH (n) DETACH DELETE n

從Python連線Neo4j資料函式庫

在實際應用中,我們通常需要透過外部程式來觸發對圖資料函式庫的讀寫操作。為此,我們將介紹如何使用Python的neo4j模組來連線Neo4j資料函式庫並執行Cypher查詢。

首先,需要匯入GraphDatabase類別並建立一個連線驅動器(driver):

from neo4j import GraphDatabase

driver = GraphDatabase.driver(uri='bolt://localhost:7687', auth=('admin', 'testpython'))

其中,uri引數指定了Neo4j資料函式庫的位置,auth引數則包含了登入的使用者名稱和密碼。

執行Cypher查詢

建立連線後,我們可以透過session()方法建立一個會話(session),並在該會話中執行Cypher查詢:

session = driver.session()
query = 'CREATE (:Person {name: "Jeremy"})-[:FOLLOWS]->(:Person {name:"Mark"})'
result = session.run(query)
driver.close()

這段程式碼建立了兩個Person節點,並在它們之間建立了一條FOLLOWS關係。雖然這裡沒有直接傳回結果,但我們可以透過Neo4j Browser或另一個讀取查詢來驗證操作是否成功。

驗證查詢結果

我們可以透過執行讀取查詢來驗證寫入操作的結果。首先,在Neo4j Browser中執行以下查詢:

MATCH (n) RETURN n

這將傳回資料函式庫中的所有節點及其關係。或者,我們也可以在Python中執行讀取查詢:

node_query = 'MATCH (n:Person) RETURN n'
edge_query = 'MATCH ()-[e:FOLLOWS]->() RETURN e'

node_result = session.run(node_query).data()
edge_result = session.run(edge_query).data()

print(node_result)
print(edge_result)

建立可重用的Python類別

為了簡化與Neo4j資料函式庫的互動,我們可以建立一個名為Neo4jConnect的Python類別,用於處理連線和查詢:

class Neo4jConnect:
    def __init__(self, uri, user, password):
        self.driver = GraphDatabase.driver(uri, auth=(user, password))

    def close(self):
        self.driver.close()

    def query(self, cypher_query):
        with self.driver.session() as session:
            result = session.run(cypher_query)
            return result.data()

內容解密:

  1. 類別初始化__init__方法負責建立與Neo4j資料函式庫的連線,接收uriuserpassword作為引數。
  2. 關閉連線close方法用於關閉與資料函式庫的連線,釋放資源。
  3. 執行查詢query方法允許執行任意的Cypher查詢,並傳回結果。

這個類別簡化了與Neo4j資料函式庫的互動過程,使得在Python中執行Cypher查詢變得更加方便。透過這種方式,我們可以更靈活地操作圖資料函式庫,並將其整合到更廣泛的應用程式中。

在Neo4j中儲存圖表

在建立了圖表資料函式庫並撰寫了與Neo4j互動的方法之後,我們可以開始使用Python和Neo4j來儲存和探索我們的圖表資料。

資料預處理

首先,讓我們來看看我們的資料(來源於史丹佛大學:https://snap.stanford.edu/data/reachability.html)。我們有兩個檔案:reachability_250.txt和reachability-meta.csv。

檢視資料

如果我們開啟reachability-meta.csv並檢視前幾行,我們會發現一個關於美國和加拿大城市的資訊列表: “node_id”,“name”,“metro_pop”,“latitude”,“longitude” 0,“Abbotsford, BC”,133497.0,49.051575,-122.328849 1,“Aberdeen, SD”,40878.0,45.45909,-98.487324 2,“Abilene, TX”,166416.0,32.449175,-99.741424 3,“Akron/Canton, OH”,701456.0,40.79781,-81.371567

這個檔案包含了我們的城市節點的屬性,以節點ID為鍵。我們有城市名稱、城市人口以及它們的緯度和經度。這個CSV檔案可以直接載入到Neo4j中,因此我們不需要進行任何預處理。

然而,我們的另一個檔案reachability_250.txt不是CSV格式,而Neo4j更喜歡CSV格式的輸入。開啟reachability_250.txt並檢查前幾行,我們可以看到它包含了一個邊緣列表:

Directed graph: reachability.txt

Transportation reachability of cities in the United States and Canada

(edge from i to j if the estimated travel time from i to j is less than a threshold)

Edges are weighted by similarity (more negative –> more dissimilar)

Nodes: 456 Edges: 27677

FromNodeId ToNodeId Weight

57 0 -84 113 0 -90 235 0 -170

根據資料來源,圖表是有向的。每個邊緣代表兩個城市之間的旅行,由節點ID表示。邊緣權重是兩個城市之間旅行時間的衡量指標,負值越大表示距離越遠。還有六行資訊需要刪除。

資料預處理步驟

為了預處理這個資料以便Neo4j匯入,讓我們將其轉換為CSV格式並刪除標題行。我們還可以使距離度量為非負數,以簡化權重。所有這些預處理都可以使用Python的csv模組和一些列表推導來完成,就像我們在前面的章節中所做的那樣:

import csv

with open('./data/reachability_250.txt', 'r') as txt:
    reader = csv.reader(txt, delimiter=' ')
    edges = [edge for edge in reader][6:]
    edges = [[edge[0], edge[1], int(edge[2])*-1] for edge in edges]

with open('./data/reachability.csv', 'w', newline='') as c:
    writer = csv.writer(c)
    for edge in edges:
        writer.writerow(edge)

內容解密:

  1. 開啟原始資料檔案:使用open函式開啟reachability_250.txt檔案,並使用csv.reader讀取內容。
  2. 過濾標題行:透過列表推導,跳過前6行標題內容,直接讀取邊緣資料。
  3. 轉換權重:將權重值乘以-1,使其變為非負數,簡化後續處理。
  4. 寫入CSV檔案:將處理後的邊緣資料寫入新的CSV檔案中,方便Neo4j匯入。

將節點、邊緣和屬性移至Neo4j

我們將使用Python向Neo4j資料函式庫寫入資料。首先,我們需要撰寫一些Cypher查詢陳述式,然後讓Python將這些查詢陳述式傳送到Neo4j。

步驟一:清空Neo4j資料函式庫

在開始之前,請確保Neo4j資料函式庫是空的。如果不是,可以在Neo4j瀏覽器視窗中執行MATCH (n) DETACH DELETE n查詢陳述式來清空資料函式庫。

步驟二:將CSV檔案複製到Neo4j的匯入資料夾

  1. 在主要的Neo4j應用程式視窗中(不是目前資料函式庫的瀏覽器視窗),點選新建立的資料函式庫旁邊的…圖示。
  2. 從那裡,選擇“Open folder”然後選擇“Import”,以在作業系統的檔案瀏覽器中開啟/import資料夾。
  3. reachability.csvreachability-meta.csv複製到這個匯入資料夾中。

內容解密:

  1. 清空資料函式庫:使用Cypher查詢陳述式MATCH (n) DETACH DELETE n刪除所有節點和關係,確保資料函式庫為空。
  2. 匯入CSV檔案:將處理好的CSV檔案複製到Neo4j指定的匯入資料夾中,以便使用Cypher的LOAD CSV命令匯入資料。

圖表結構描述

我們的資料來源已經是一個合理的圖表格式,包含一個節點屬性檔案和一個邊緣列表。來源還為我們提供了關於圖表結構描述的資訊。圖表包含有向邊緣,這些邊緣總是在城市之間,使其成為一個同質圖。我們的節點屬性對於每個節點都是相同的型別,使得這個航空旅行圖表的結構描述相當簡單。

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title 圖表結構描述

rectangle "圖表結構描述" as n1
rectangle "實作" as n2
rectangle "應用" as n3

n1 --> n2
n2 --> n3

@enduml

此圖示展示了城市之間的航空旅行關係,是有向圖,每個邊代表從一個城市到另一個城市的旅行路線。

內容解密:

  • 同質圖:所有節點代表相同型別的實體(城市),所有邊代表相同型別的關係(航空旅行路線)。
  • 有向邊:邊有方向,表示從一個城市到另一個城市的旅行方向。
  • 節點屬性:每個城市節點具有相同的屬性,如名稱、人口、緯度和經度。

使用圖資料函式庫的高階操作與分析

載入資料到Neo4j並進行Cypher查詢

在將資料成功匯入Neo4j後,我們可以透過Cypher查詢語言來存取和操作圖資料。首先,我們使用LOAD CSV WITH HEADERS語法來載入reachability-meta.csv檔案中的節點屬性,並使用CREATE陳述式建立具有相應屬性的節點。

LOAD CSV WITH HEADERS from 'file:///reachability-meta.csv' as row
CREATE (city:City {
  node_id: toInteger(row.node_id),
  name: row.name,
  population: toInteger(row.metro_pop),
  latitude: toFloat(row.latitude),
  longitude: toFloat(row.longitude)
})

內容解密:

  1. LOAD CSV WITH HEADERS:從指定路徑載入CSV檔案,並將第一行視為欄位名稱。
  2. CREATE (city:City {...}):為每一行CSV資料建立一個名為City的節點,並賦予相應的屬性。
  3. toInteger()toFloat():將CSV中的字串轉換為整數和浮點數,以正確表示節點屬性。

接著,我們使用Cypher載入邊緣資料(reachability.csv),並在節點之間建立:AIR_TRAVEL關係。

LOAD CSV from 'file:///reachability.csv' as row
MATCH (from:City {node_id: toInteger(row[0])})
MATCH (to:City {node_id: toInteger(row[1])})
MERGE (from)-[:AIR_TRAVEL {travel_time: toInteger(row[2])}]-(to)

內容解密:

  1. LOAD CSV:載入沒有標頭的CSV檔案。
  2. MATCH (from:City {...})MATCH (to:City {...}):根據CSV中的節點ID匹配對應的節點。
  3. MERGE (from)-[:AIR_TRAVEL {...}]-(to):在匹配的節點之間建立:AIR_TRAVEL關係,並新增travel_time屬性。

使用Python與Cypher進行查詢與分析

我們可以透過Python連線Neo4j並執行Cypher查詢,以驗證資料是否正確匯入。

from graphtastic.database.neo4j import Neo4jConnect

connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
cypher = 'MATCH (n) RETURN count(n)'
result = connection.query(cypher).data()
connection.close()
print(result)

內容解密:

  1. Neo4jConnect:建立與Neo4j資料函式庫的連線。
  2. connection.query(cypher).data():執行Cypher查詢並取得結果。

進一步地,我們可以執行更複雜的查詢,例如查詢特定邊緣的資訊。

edge_test = [57, 0]
cypher = f'MATCH (n:City {{node_id:{edge_test[0]}}})' \
         '-[r:AIR_TRAVEL]->' \
         f'(m:City {{node_id:{edge_test[1]}}}) ' \
         'RETURN n.name, m.name, r.travel_time'
result = connection.query(cypher).data()
print(result)

內容解密:

  1. edge_test:定義要查詢的邊緣所對應的節點ID。
  2. Cypher查詢:匹配指定的邊緣,並傳回相關節點名稱和邊緣的travel_time屬性。

利用Python與Cypher最佳化旅行分析

在完成資料匯入和基本查詢後,我們可以進一步進行更複雜的分析,例如找出人口最多的城市。

cypher = 'MATCH (n) ' \
         'RETURN n.name, n.population ' \
         'ORDER BY n.population ' \
         'DESC LIMIT 1'
result = connection.query(cypher).data()
print(result)

內容解密:

  1. MATCH (n):匹配所有節點。
  2. ORDER BY n.population DESC LIMIT 1:按人口數量降序排序,並傳回第一個結果,即人口最多的城市。

使用Python和Cypher最佳化旅行規劃

在前面的章節中,我們已經瞭解如何使用Neo4j圖形資料函式庫和Python進行簡單的查詢。現在,我們將進一步探討如何利用圖形資料結構和Cypher查詢語言來最佳化旅行規劃。

找出從紐約可便捷抵達的城市

假設我們想要找出從紐約市可以短時間內抵達的城市。我們可以使用Cypher查詢語言中的WHERE函式來過濾符合條件的節點。

MATCH (n:City {name: "New York, NY"}) 
MATCH (n)-[r:AIR_TRAVEL]->(m) 
WHERE r.travel_time < 100 
RETURN m.name

內容解密:

  1. MATCH (n:City {name: "New York, NY"}):匹配名稱為「New York, NY」的城市節點。
  2. MATCH (n)-[r:AIR_TRAVEL]->(m):匹配從n到其他城市m之間存在飛行關係的節點。
  3. WHERE r.travel_time < 100:過濾飛行時間少於100分鐘的關係。
  4. RETURN m.name:傳回符合條件的城市名稱。

使用Python執行上述Cypher查詢:

cypher = 'MATCH (n:City {name: "New York, NY"}) ' \
         'MATCH (n)-[r:AIR_TRAVEL]->(m) ' \
         'WHERE r.travel_time < 100 ' \
         'RETURN m.name'
connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
result = connection.query(cypher).data()
connection.close()
print(result)
print(len(result))

計算從聖地牙哥到聖約翰斯的最佳路徑

現在,讓我們嘗試找出從聖地牙哥(San Diego, CA)到聖約翰斯(St. John’s, NL)的最佳飛行路徑。

MATCH (n:City {name: "San Diego, CA"}) 
MATCH (m:City {name: "St. Johns, NL"}) 
MATCH p=(n)-[*..3]->(m) 
RETURN p, relationships(p) as rels

內容解密:

  1. MATCH (n:City {name: "San Diego, CA"})MATCH (m:City {name: "St. Johns, NL"}):分別匹配起點和終點城市的節點。
  2. MATCH p=(n)-[*..3]->(m):匹配從起點到終點最多經過3次轉機的路徑。
  3. RETURN p, relationships(p) as rels:傳回路徑及其相關的飛行關係。

使用Python執行上述Cypher查詢:

cypher = 'MATCH (n:City {name: "San Diego, CA"}) '\
         'MATCH (m:City {name: "St. Johns, NL"}) '\
         'MATCH p=(n)-[*..3]->(m) ' \
         'RETURN p, relationships(p) as rels'
connection = Neo4jConnect('bolt://localhost:7687', 'admin', 'testpython')
result = connection.query(cypher).data()
connection.close()
print(result)
print(len(result))

使用Dijkstra演算法最佳化旅行路徑

Dijkstra演算法是一種常見的圖形理論演算法,用於找出加權圖中兩節點之間的最短路徑。假設我們有一個圖,其中節點代表城市,邊代表城市之間的飛行關係,並帶有權重(例如,飛行時間)。

實作Dijkstra演算法

首先,我們需要解析Cypher查詢結果,將其轉換為適合在Python中使用的格式。然後,我們可以使用igraph函式庫來實作Dijkstra演算法。

  1. 解析路徑結果:
paths = [path['p'] for path in result]
nodes = [node for path in paths for node in path if node != 'AIR_TRAVEL']

內容解密:

  1. paths = [path['p'] for path in result]:從查詢結果中提取路徑。
  2. nodes = [node for path in paths for node in path if node != 'AIR_TRAVEL']:扁平化路徑列表,去除代表飛行的字串。

接下來,我們將進一步探討如何使用igraph函式庫來實作Dijkstra演算法,以找出最優的旅行路徑。