Graphs4Good: 使用Neo4j Bloom實現新冠病毒案例的接觸史追蹤

(本文的英文原文發表於:https://community.neo4j.com/t/graphs4good-protecting-community-from-covid-19-by-answering-key-questions-in-neo4j-bloom/17291)

       儘管目前世界上大多數國家都在半封鎖狀態、以減慢致命的COVID-19病毒的傳播,但尋找治癒方法的鬥爭從未停止。贏得這場前所未有的反病毒戰爭的關鍵很大程度上取決如何利用現有的數據來確定感染源、瞭解病毒如何傳播以及保護社區免受感染。到目前爲止,接觸史追蹤(Contact Tracing)已被認爲是遏制情況惡化的最有效方法之一。

       時機至關重要。

       政府,組織甚至個人都需要一種追蹤高風險患者和地點的方法。每天發佈的確診病例數量還遠遠不夠。能夠將那些確診案例的詳細信息包括何人、何時、何地以及傳播途徑等聯繫起來的能力比以往任何時候都更加重要,只有這樣才能提供及時和可行的反應和預測。顯然,這是圖數據庫解決方案的一個完美的問題。

       基於Neo4j社區和相關政府機構的一些想法,使用公開可用的案例數據(不包含身份信息),我用週四花了一個晚上的時間使用Neo4j Bloom構建此解決方案,以幫助回答(是的,以自然語言)接觸史追蹤的關鍵問題。以下是具體的方法。

1.加載數據

       爲了深入瞭解病毒如何在人羣中傳播,我們需要個別的病例數據,而不是病例統計。實際上,Kaggle發起的COVID-19挑戰賽提供這樣的個別案例數據,可在此處找到:UNCOVER COVID-19挑戰賽1 

        我也在Github上分享了本項目,可以找到2個數據文件。個例文件的佈局不難理解,以下是Cypher LOAD CSV語句,可將數據加載到Neo4j中:

// 1.1 Load individual case data of Canada file by Kaggle

USING PERIODIC COMMIT 1000
LOAD CSV WITH HEADERS FROM 'file:///individual-level-cases.csv' AS line
MERGE (p:State:Canada{name:line.province})
CREATE (c:Case:Canada{caseId:line.case_id})
SET c.ageGroup = line.age, c.healthRegion = line.health_region, c.reportDate = line.date_report,
c.acquireMethod = line.locally_acquired, c.acquireSource = line.travel_history_country, c.source = line.case_source,
c.sex = line.sex, c.state= line.province, c.community = line.community,
c.linkedCase = line.linked_case
MERGE (c) -[:LIVES_IN]-> (p)
WITH c,p,split(line.travel_history_country,',') AS places
UNWIND places AS place
MERGE (pl:Place{name:trim(place)})
MERGE (c) -[:HAS_VISITED]-> (pl)

// 1.2 Load indivudual case file of Korea:

USING PERIODIC COMMIT 1000
LOAD CSV WITH HEADERS FROM 'file:///PatientInfo.csv' AS line
MERGE (p:State:South Korea{name:line.province})
CREATE (c:Case:South Korea{caseId:line.patient_id})
SET c.ageGroup = line.age, c.healthRegion = line.city, c.reportDate = line.confirmed_date,
c.acquireMethod = line.infection_case, c.acquireSource = line.infection_case, c.deceasedDate = line.deceased_date,
c.sex = line.sex, c.state= line.province, c.linkedCase = line.infected_by, c.state = line.state
MERGE (c) -[:LIVES_IN]-> (p)

我們給所有去世者案例增加一個額外的標籤:

MATCH(c:Case)
WHERE c.state ='deeased'SET
c:Deceased

記得創建一些索引:

CREATE INDEX ON:Case(caseId);
CREATE INDEX ON:Case(reportDate);
CREATE INDEX ON:Case(sex);

完成後的數據模型也非常簡單。

2. 使用Neo4j Bloom定義查詢

       Bloom可以從Neo4j Desktop安裝並啓動它,也可安裝Bloom服務器插件從Neo4數據庫服務器運行。

       除了圖可視化外,Bloom的美麗之處還在於其自然語言風格的搜索。例如,要找出居住在Seoul的所有案例,只需輸入:“ case live_in Seoul”:

       還記得上面的數據模型嗎?Case是標籤名稱,LIVES_IN是關係類型名稱。在鍵入時,Bloom會自動在Neo4j數據庫中搜索元數據和索引,以找到最佳匹配項。

       Bloom具有許多令人興奮的功能。我喜歡的一個,並且總是令其他人驚訝的是它的搜索模板(Search Template)功能。搜索模板是一個預定義的Cypher查詢,它有一個搜索短語,供其他用戶查找和執行。它通常更復雜,並且支持參數。

       例如,可以定義以下查詢模板以查找某個地點中所有已確認的案例。

該Cyper模板有2個參數:$ place和$ date。實際的Cypher查詢如下:

MATCH path = (c:Case) -[:LIVES_IN]-> (p:State)
WHERE c.reportDate = $date AND p.name = $place
RETURN path;

其功能應該很好理解。參數$ place的候選值來自State節點的name屬性。保存此模板後,只需輸入以下內容即可執行該模板: “Show confirmed cases in Seoul on 2020-03-01”,然後按Enter鍵就可以得到結果。是不是很酷?

3.關鍵問題

這裏有更多有用的搜索。

問: 案例 #2000000019 是怎樣被傳染的?
答: Show ultimate source of a case 2000000019.

Cypher:
MATCH path = (c:Case{caseId:$case}) -[:INFECTED_FROM*]-> (c1)
WITH path, size(nodes(path)) AS distance
ORDER BY distance DESC LIMIT 1
RETURN path;

 

問: 某個地區的“零號病患”是誰?
答: Show the Super Spreader

Cypher:
MATCH (c:Case)
WITH c, size((c) <-[:INFECTED_FROM]- ()) AS degree
ORDER BY degree DESC LIMIT 1
WITH c
MATCH path = (c) <-[:INFECTED_FROM*]- (c2)
RETURN path;

問: 這個地區的確診病例的發展過程是怎樣的?
答: Show all confirmed cases till xxxx-xx-xx (這裏連時間滑塊都不需要!)
Cypher:
MATCH (c:Case)
WHERE exists(c.reportDate) AND c.reportDate <= $date
OPTIONAL MATCH (c) -[r:INFECTED_FROM]-> (c1)
RETURN c,r,c1;

 

       圖中,藍色的點是確診案例,較大的灰色點是過時的案例。點之間的紅線是感染的途徑。在Bloom中可以定義條件樣式,顯示節點的不同風格。

4. 更進一步:添加位置信息

       爲了實現更有效的接觸追蹤,我們需要人們去過的地方和時間的位置數據。感謝我的同事Rik Van Bruggen,他創建了一些模擬的位置數據,以使其成爲更具說服力和吸引力的解決方案。

       免責聲明:在數據集中看到的所有名稱都不是真實的人。

       添加位置和場所數據後的圖模型:

       加載數據的Cypher語句:

//import the persons
load csv with headers from
"
https://docs.google.com/spreadsheets/u/0/d/1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE/export?format=csv&id=1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE&gid=0" as csv
create (p:Person {id: csv.PersonId, name:csv.PersonName, healthstatus:csv.Healthstatus, confirmedtime:datetime(csv.ConfirmedTime)});

//import the places
load csv with headers from
"
https://docs.google.com/spreadsheets/u/0/d/1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE/export?format=csv&id=1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE&gid=205425553" as csv
create (p:Place {id: csv.PlaceId, name:csv.PlaceName, type:csv.PlaceType, location:point({x: toFloat(csv.Lat), y: toFloat(csv.Long)})});

// Speed up match by creating indices
create index on :Place(id);
create index on :Place(location);
create index on :Place(name);
create index on :Person(id);
create index on :Person(name);
create index on :Person(healthstatus);
create index on :Person(confirmedtime);

//import the stays
load csv with headers from
"
https://docs.google.com/spreadsheets/u/0/d/1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE/export?format=csv&id=1R-XVuynPsOWcXSderLpq3DacZdk10PZ8v6FiYGTncIE&gid=1261126668" as csv
match (p:Person {id:csv.PersonId}), (pl:Place {id:csv.PlaceId})
create (p)-[:STAYED_AT]->(s:Stay {id:csv.StayId, starttime:datetime(csv.StartTime), endtime:datetime(csv.EndTime)})-[:LOCATED_AT]->(pl);

5. 追蹤確診病例

問:最近三天誰被感染了?去過什麼地方?此外,誰也去過那些地方、並可能會面臨更高的風險?
答:
Show people who were infected in the last 3 days
 

Cypher查詢模板:
MATCH (p:Person)
WHERE p.healthstatus = 'Sick' AND duration.inDays(p.confirmedtime, datetime()) + datetime() < duration({days:$num}) + datetime()
RETURN p;

       這裏使用了Neo4j的Duration數據類型計算“過去N天”。可以在Neo4j Cypher在線手冊中找到關於Duration數據類型的更多詳細信息。

       結果顯示,過去3天中有13人被感染,在66個地方停留過105次。這些地方有其他486位(未經測試或未經確診)、3249次停留,這些人現在可能已經面臨高感染風險。

問:對於確診的病例X,誰曾與其接觸、因此有高風險?
答: Show everyone who may be infected by X.

Cypher:

match (p:Person {name:$name})
with p
limit 1
match path= (p)--(s1:Stay)--(pl:Place)--(s2:Stay)--(p2:Person {healthstatus:"Healthy"})
return path;

       這裏只列出部分,不難想象還有很多功能可以方便地實現。

       那麼,能在地圖上看到案例的分佈嗎?當然,Neo4j的答案(總是)“YES”。Estelle Scifo開發了一個名爲Neomap的圖應用程序,正好滿足這一目的。可以參考Neomap簡介中介紹的方法、只需幾分鐘就可以完成。

 

後記

      我大約用了24小時,在同事們的幫助下構建了這個演示,包括數據和可視化。的確只是24小時!所有創建的內容都通過Github共享,包括示例數據庫、Cypher腳本、包含樣式和搜索模板的Bloom透視文件。

       對抗疫病的流行會是一項持續不斷的工作,不僅需要政府機構和醫務人員,而且還需要更多的普通人通過待在家裏並遵循健康的生活方式來做出貢獻。很棒的是,除了呆在家裏,我們可以利用知識和技術(例如圖數據庫)來做點事情。Neo4j組織的GraphHack Graphs4Good 2 計劃的最新進展,已經有許多類似項目正在創造令人鼓舞的成果。

       Neo4j支持有益於公衆的項目,即Graphs4Good,包括提供Neo4j Enterprise DBMS的免費訂閱許可,來支持那些使用Neo4j圖數據庫技術來構建解決方案的公益型項目。欲知詳情,請與我們聯繫

      加油,Graph4good!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章