Categories
程式開發

如何利用圖數據庫發現新冠病毒傳播路徑?


天津某百貨大樓內部相繼出現 5 例新冠肺炎確診病例,從起初的 3 個病例來看,似乎找不到任何流行病學上的關聯性。在這種背景之下,作為技術人員可以通過什麼技術來找尋病例之間的聯繫呢?

最初,nCoV 新冠病毒的擴散過程是由一個人(節點)向各其他人(節點)擴散的樹狀結構,但隨著疫情擴散為網狀結構,可以使用圖數據庫來存儲相關人員、地理位置、感染時間等數據,本文將使用圖數據庫 Nebula Graph 作為工具,帶大家一起探討疫情的傳播路徑,並找到相關的疑似病例。

案例簡述

下面用 Usr1、Usr2、Usr3、Usr4、Usr5 來代指這 5 例病例,看一下他們的行為軌跡:

  • Usr1 信息: Usr1 於 1 月 24 日開始發熱,在 1 月 22 日至 1 月 30 日期間在天津百貨大廈 A 區工作,於 1 月 31 日確診;

  • Usr2 信息:Usr2 為 Usr1 丈夫,於 1 月 25 日開始出現腹瀉症狀,於 2 月 1 日確診;

  • Usr3 信息:Usr3 於 1 月 18 日接觸過一個疑似病例,而後在天津百貨大廈 B 區工作,於 1 月 24 日開始發熱,於 2 月 1 日確診;

  • Usr4 信息:Usr4 於 1 月 12 日、13 日接觸過疑似病例,而後在天津百貨大廈 C 區工作,於 1 月 21 日開始發熱,於 2 月 1 日確診;

  • Usr5 信息:Usr5 於 1 月 23 日下午 16 點到 23 點到過天津百貨大廈 A、B、C 區,1 月 29 日開始發熱,2 月 2 日確診;

下面我們來建立一個傳播路徑的模型。

病毒數據分析

以我們現有的資料顯示,本次 nCoV 的傳播路徑為人傳人(圖 Demo1),即一個點通過特定訪問路徑連接到一個點。單個節點看來傳播路徑為一個樹形結構(圖 Demo2)——確診病人 A 感染 B,B 再感染 C,C 再感染 D…。根據現在疫情傳播情況,存在多個確診病人,所以整個傳播鏈路呈網狀結構(圖 Demo3)。而無論是樹形結構還是網狀結構都很適合用圖(網絡)這種數據結構來存儲、查詢和分析。

如何利用圖數據庫發現新冠病毒傳播路徑? 1

圖模型

在建模之前我們需要清楚人和人之間的關係載體是什麼?根據現有的病例信息,我們知道 A 和 B 會的接觸場景最常見的是:同一個時間段逗留在某個相同的空間。這也是本次疫情篩選需隔離人群的重要指標:是否和確診 / 疑似病例在酒店、火車、超市有過密切接觸。

如何利用圖數據庫發現新冠病毒傳播路徑? 2

可見最小模型中有兩類節點 Person 和 Space ,關係為 stay 。最小模型有了,那麼我們需要 Person 和 Space 的什麼信息呢?

Person 類型節點的屬性:

• ID:Person 的身份證,用來標識人
• HealthStatus :健康狀態,有 2 種狀態
• Health:健康
• Sick:生病
• SickTime:發熱開始時間,可以用來追溯病人發病的先後次序

Space 類型節點的屬性:

• ID:Space ID,用來唯一標識 space
• Address:space 地址

如何利用圖數據庫發現新冠病毒傳播路徑? 3

我們構建完 Person 和 Space 的模型之後,再構建人和位置之間的關係:

在 stay 關係上,記錄有逗留的起始時間終止時間。這樣就可以幫助我們判斷兩個人是否有過時間和空間上的交集

案例建模

構建完最小模型之後,我們來分析一下天津病例中的信息,將模型應用在這個案例中。並通過圖數據庫 Nebula Graph 構建病例間關係、找尋病例1 的發病原因——病例1 怎麼被傳染的,以及病例1 確診後我們需要觀察/隔離哪些人?
整個模型的示意如下:

如何利用圖數據庫發現新冠病毒傳播路徑? 4

數據錄入

Usr1:

• Person 信息:ID 2020020201,HealthStatus:Sick,SickTime:20200124;
• Stay Time:起始時間 1 月 23 日 12 點,終止時間 18 點;
• Place 信息:天津百貨大廈 A 區;
• Stay Time:起始時間 1 月 23 日 18 點,終止時間 24日 8 點;
• Place 信息:天津市和平區 A 小區;

Usr2:

• Person 信息:ID 2020020202,HealthStatus:Sick,SickTime:20200125;
• Stay Time:起始時間 1 月 23 日 12 點,終止時間 23 點;
• Place 信息:天津市和平區 A 小區;

Usr3:

• Person 信息:ID 2020020203,HealthStatus:Sick,SickTime:20200125;
• Stay Time:起始時間 1 月 23 日 15 點,終止時間 19 點;
• Place 信息:天津百貨大廈 B 區;
• Stay Time:起始時間 1 月 23 日 12 點,終止時間 23 點;
• Place 信息:天津市河西區 B 小區;

Usr4:

• Person 信息:ID 2020020204,HealthStatus:Sick,SickTime:20200121;
• Stay Time:起始時間 1 月 23 日 11 點,終止時間 20 點;
• Place 信息:天津南開區某火鍋店;
• Stay Time:起始時間 1 月 23 日 20 點,終止時間 23 點;
• Place 信息:天津市濱海區 B 小區;

Usr5:

• Person 信息:ID 2020020205,HealthStatus:Health,SickTime:NULL(無);
• Stay Time:起始時間 1 月 23 日 11 點,終止時間 15 點;
• Place 信息:天津南開區某火鍋店;
• Stay Time:起始時間 1 月 23 日 16 點,終止時間 23 點;
• Place 信息:天津百貨大廈 A、B、C 區;

將它導入到圖數據庫中, 建立人和空間之間的關係。這里以 Usr1 的軌跡為例,其餘幾份病例類似。

-- 插入 Usr1
INSERT VERTEX person(ID, HealthStatus, SickTime) VALUES 1:(2020020201, ‘Sick’, '2020-01-24'); 
-- 插入 位置 “天津百货大厦 A 区”
INSERT VERTEX place(name) VALUES 101:("天津百货大厦 A 区")
-- Usr1 到 “天津百货大厦 A 区”
INSERT EDGE stay (start_time, end_time) VALUES 1 -> 101: ('2020-01-23 12:00:00', '2020-01-23 18:00:00')
-- 插入 位置 “天津市和平区 A 小区”
INSERT VERTEX place(name) VALUES 102:("天津市和平区 A 小区")
-- Usr1 回家
INSERT EDGE stay (start_time, end_time) VALUES 1 -> 102: ('2020-01-23 18:00:00', '2020-01-24 8:00:00')

病例數據分析

數據導入後,讓我們一步步揭開病例1 被感染之謎:

1. 查詢 Usr1 在發病前的 1 月 23 日去過哪裡

$PlaceUsr1Goto = GO FROM 1 OVER stay WHERE stay.start_time > '2020-01-23 00:00:00' AND 
stay.start_time < '2020-01-24 00:00:00'
YIELD stay._dst AS placeid

2. 查詢這段時間 Usr1 是否接觸過任何(已發病的)病例

GO FROM $PlaceUsr1Goto OVER stay REVERSELY WHERE $$.person.HealthStatus == 'Sick' 
   AND $$.person.SickTime <= "2020-01-23"

很奇怪,在 Usr1 發病的時候 (2020-01-24),他接觸的人群裡面並沒有發熱患者。那會不會是這些人又接觸過其他的患者呢(從而成為攜帶者)。讓我們繼續分析。

3. 查詢這些人又接觸過誰

$PersonUsr1Meet = GO FROM $PlaceUsr1Goto OVER stay REVERSELY YIELD stay._dst AS id
$PlaceThosePersonGoto = GO FROM $PersonUsr1meet.id OVER stay YIELD stay.start_time AS start
    stay.end_time AS end
GO FROM $PlaceThosePersonGoto.id FROM stay REVERSELY 
    WHERE $$.person.HealthStatus == 'Sick'
    AND $$.person.SickTime  $PlaceTHosePersonGoto.start 
    AND stay.end_time < $PlaceThosePersonGoto.end  -- 并且有过接触

我們發現,雖然 Usr1 在 1 月 23 日 12 點到 1 月 24 日 8 點之間接觸的人(Usr2, Usr5)都還沒有發熱,但是 Usr5 卻在之前接觸過發熱病人 Usr4。

至此,我們找到了這條傳播鏈路:

Usr4 在 1 月 21 日發病。發病後,他仍前往天津南開區某火鍋店(1 月 23 日 11 點 - 20 點)。在這裡,他接觸到(當時健康的)Usr5(1 月 23 日 11 點-15 點)。在接觸過程中使得 Usr5 成為一個攜帶者。之後Usr5 前往天津百貨大廈A、B、C 區( 1 月23 日16 - 23 點),在這段時間內,他將病毒傳染給在A 區上班的Usr1(1 月23 日12 點- 18 點)。最終 Usr1 在 1 月 24 日發病。

4. 之後排查需要隔離哪些人

Usr1 確診之後,我們需要查看她在哪些時候到過哪些地方。而對應這個時間段相同地點內,又有哪些人同她接觸。我們判斷這些親密接觸者,需要重點隔離和觀察。

GO FROM 1 OVER stay YIELD stay.start_time AS usr1_start, 
stay.end_time AS usr1_end, stay._dst AS placeid
| GO FROM $placeid OVER stay REVERSELY WHERE 
stay.start_time > usr1_start AND stay.start_time < usr1_end
YIELD $$.person.ID

可以發現 Usr1 和 Usr2 在天津市和平區 A 小區有過交集,這使得 Usr2 需要被重點觀察。然而不幸的是,Usr2隨後也跟著發病了。

傳播路徑可視化展示

上面這段分析過程,也可以使用圖形化界面的方式來交互分析,這樣更加直觀。

如何利用圖數據庫發現新冠病毒傳播路徑? 5

但是,如果有非常大批量的關注嫌疑點(例如上千萬離開湖北的潛在人員和他們的二次三次到N次的傳播軌跡),通過批量程序查詢的方式會更加高效。

小結

由於春節返鄉和一些不可描述的影響,導致冠狀病毒的大面積擴散。從報導和社交媒體上可以看到,各個社區、村莊、企業都採用了相當嚴格的隔離措施,要求個人每日匯報行踪和健康狀態,並密切跟踪從疫區來的人員。這樣十幾億人的隔離和追踪需要極大的人力物力和動員能力,充分體現了“集中力量辦大事”的製度優越性。

但另外一方面,這樣的自我申報和層層統計,非常依賴個人的自覺,也依賴於匯報體系的響應速度。特別是當生死攸關的時候,個人反而有很強的動機隱瞞過去的行為和病史,導致未能得到及時的隔離和救治,也極大的影響了需要專業分工合作的現代經濟生產活動。

事實上,隨著大數據技術的發展和智能設備的普及,使得國內的安防、運營商、交通、醫療部門的數據體系已經建立的較為全面,已經了具備對於海量人員的行為軌跡進行記錄和分析的基礎。在天津這個案例中,我們只選取了少數幾個病例和場所作為示意,隨著數據規模的增加(例如幾十億的人員和位置)和查詢深度的增加(2次3次乃至N次傳播) ,圖數據庫技術的特點體現的更加明顯。 (相比於層層匯報和統計)可以大大提高和定位疑似患者的速度,避免大量攜帶者在不知情時的四處活動。這樣既能減少一線醫療和社區工作人員的壓力,也能降低全社會的全面隔離時間,盡快恢復已經孱弱的經濟活動。

作者簡介

吳敏,Nebula Graph 總監。浙大博士畢業後一直從事分佈式系統研發工作,十餘年數據庫從業經驗。當前負責分佈式圖數據庫 Nebula Graph 產品設計和技術社區,重點關注高性能企業級圖數據庫的系統設計。

參考資料

http://www.bjd.com.cn/a/202002/03/WS5e37d067e4b002ffe994092e.html

https://github.com/vesoft-inc/nebula