一、背景與數據來源介紹
新型肺炎作爲一種存在潛伏期的傳染病,分析其傳染關係及接觸關係非常有利於疫情的防控,對疫後的研究分析也有幫助。本文將介紹基於圖數據庫對新型肺炎圖譜進行建模與分析的過程及效果。
圖數據庫(Graph Database)是一種複雜關係數據的處理系統,一種使用頂點、邊和屬性來表示與存儲數據,並以圖結構進行語義查詢的數據庫。圖數據庫的關鍵概念是邊,通過邊將頂點連接在一起,從而進行快速的圖檢索操作。
圖數據庫非常適合用於分析此類關聯關係數據,此次使用百度開源的HugeGraph圖數據庫作爲分析工具。分析數據數據均來源於各地衛健委或權威網站公開公佈,如北京、石家莊、溫州、南昌、宜春等城市。分析場景包括:
- 疫情中分析,如:病例接觸了哪些人以及個人的風險分析、高風險羣體分析等;
- 疫情後分析,如:病毒傳播路徑、病毒變異、抗體等分析。
新型肺炎傳染圖譜
本次演示共導入了5類實體數據:包括正常人、病例、地址、交通工具、醫院等數據信息,以及各類實體之間的關聯關係,如“病例乘坐某交通工具”關係。
數據導入後效果
導入數據詳細介紹如下:
1、病例數據:共導入了43條病例數據,包括病例的年齡、性別、感染原因、症狀、確診日期、省市等信息。
2、地址數據:共導入了32條地址數據,比如“裕華區裕翔社區衛生服務中心”,主要包括上述病例出現過的地址。
3、交通工具數據:共導入了6條交通工具數據,如高鐵、航班、公交地鐵等信息,主要包括上述病例乘坐過的交通工具。(這個數據較少省市公佈)
4、醫院數據:共導入了10條醫院數據,如“北醫科大學第四醫院”,主要包括上述病例收治的醫院。
5、“傳染鏈”視圖:共導入了8條傳染數據,如“病例甲傳染了病例乙”,主要包括上述病例和疑似病例的傳染關係。
6、“交通工具鏈”視圖:共導入了8條交通工具乘坐信息數據,比如“病例甲在1月18日乘坐了G310次高鐵”,主要包括上述病例和疑似病例的乘坐關係。
7、“出現於場所”視圖:共導入了9條病例、19條正常人出現的場所地址信息數據,比如“病例甲在1月21日10點到過XX超市”,主要包括上述病例和正常人的出現的地址關係。
8、某個病例關係鏈視圖:除上述關聯關係外,還導入了“感染於”、“現住”、“常住”、“收治於”等關係,主要包括上述病例和疑似病例的各種其它關係。
9、“正常人”數據:共導入了22條正常人數據,包括姓名、年齡、性別、詳細地址等信息;另外還導入了28條正常人的軌跡信息,如“某個正常人在1月19日乘坐過G512次高鐵”、“某個正常人在1月20日10點到過XX超市”。(注意:正常人數據並不準確,僅供演示參考)
到此,數據建模與導入介紹完畢。接下來基於此數據集進行各種分析場景的演示。
二、分析場景演示
場景1:基本統計信息
本場景中包含了7種基本的統計分析,分別是:分析確診病例的城市分佈情況、病例的省份分佈情況、病例的確診日期分佈情況、病例的平均年齡、病例的年齡段分佈情況等。(注意:這些統計信息僅僅是基於上述導入數據集計算的)
1、病例的城市分佈情況
注:表格部分展示了分析結果,表格底部灰色部分展示了分析計算的查詢語句,查詢語句使用的是HugeGraph提供的標準Gremlin圖查詢語言,下同。
石家莊市 |
18人 |
北京市 |
8人 |
溫州市 |
7人 |
宜春市 |
6人 |
南昌市 |
4人 |
g.V().hasLabel('病例').groupCount().by('市') .unfold().order().by(values,desc) |
2、病例的省份分佈情況
河北省 |
18人 |
江西省 |
10人 |
北京市 |
8人 |
浙江省 |
7人 |
g.V().hasLabel('病例').groupCount().by('省') .unfold().order().by(values,desc) |
3、病例的確診日期分佈情況(Top3)
2020-02-03 |
7人 |
2020-01-29 |
6人 |
2020-01-20 |
5人 |
g.V().hasLabel('病例').groupCount().by('確診日期') .unfold().order().by(values,desc).limit(3) |
4、病例的平均年齡
平均年齡 |
44歲 |
g.V().hasLabel('病例').values('年齡').mean() |
5、病例的年齡段分佈情況
40~49歲 |
13人 |
30~39歲 |
11人 |
60~69歲 |
6人 |
50~59歲 |
4人 |
10~19歲 |
2人 |
70~79歲 |
1人 |
20~29歲 |
1人 |
0~9歲 |
1人 |
g.V().hasLabel('病例') .filter{it.get().property('年齡').isPresent()} .groupCount().by(values('年齡') .math('floor(_/10)*10')).unfold() .order().by(values,desc) |
6、最容易的感染傳染方式(Top3)
家人親密接觸 |
11人 |
探親 |
7人 |
武漢市務工返回 |
6人 |
g.V().hasLabel('病例') .filter{it.get().property('感染原因').isPresent()} .groupCount().by('感染原因').unfold() .order().by(values,desc).limit(4) |
7、家人親密接觸方式感染的傳染關係分佈情況
夫妻 |
4對 |
母子 |
3對 |
父子 |
2對 |
父女 |
1對 |
嶽婿 |
1對 |
g.E().hasLabel('傳染').groupCount().by('傳染關係') .unfold().order().by(values,desc) |
場景2:與確診病例直接接觸過的人(1層關係)
下圖中的3個紅色點代表已確診病例,深藍色點代表正常人,這些人與病例有過接觸,有感染風險:
場景2:與病例直接接觸過的人
查詢語句:
g.V().hasLabel('病例')
.union(out('乘坐').in('正常乘坐').simplePath(),
out('出現於').in('正常出現於').simplePath())
.hasLabel('正常人').path()
注:查詢語句使用的是HugeGraph提供的標準Gremlin圖查詢語言,下同。
場景3:與確診病例間接接觸過的人(2層關係)
下圖中的左上角紅色點代表已確診病例,深藍色點代表正常人,這些正常人與病例有過直接接觸(如田某某),或者間接接觸(其它藍色點,如李某某與田某某在建設大街XX菸酒超市接觸過),均有感染風險:
場景3:與病例間接接觸過的人
查詢語句:
g.V().hasLabel('病例')
.union(
out('乘坐').in('正常乘坐').simplePath().out('正常乘坐').in('正常乘坐').simplePath(),
out('出現於').in('正常出現於').simplePath().out('正常出現於').in('正常出現於').simplePath())
.hasLabel('正常人').path()
場景4:某個正常人是否與確診病例直接或間接接觸過
換一個角度,以正常人爲中心來考慮並進行分析。下圖中的左上角藍色點是一個正常人“黃某某”,查看他是否有直接或間接和紅色點所代表的已確診病例關係,從圖中可以看出,他與病例“sjz0203-21”有直接接觸,且與病例“sjz0202-14”有間接接觸。
場景4:某個正常人是否與病例有接觸
查詢語句:
g.V('黃某某').repeat(out('正常乘坐','正常出現於')
.in('正常乘坐','正常出現於','乘坐','出現於').simplePath())
.times(2).emit(hasLabel('病例'))
.hasLabel('病例').path()
場景5:找出所有的超級傳播者
我們要從所有的病例裏面找到超級傳播者,這裏假設一個病例若傳染了5個以上的人則定義爲超級傳播者,下圖中心的紅色點“sjz0202-14”病例即是找出來的超級傳播者(傳染了“sjz0202-15”和“sjz0203-21”等5個人)。
場景5:找出超級傳播者
查詢語句:
g.V().hasLabel('病例').where(outE('傳染').count().is(gte(5)))
.order().by(outE('傳染').count(),desc).limit(10)
場景6:找出超級傳播者接觸過的人(包括已確診病例和正常人)
一般來說,超級傳播者的風險比較高,與超級傳播者接觸過的人風險也是比較高的,下圖展示瞭如何找到這些高風險的人:中心的紅色點是超級傳播者,周圍的紅色點和藍色點所代表的人是高風險的人。
場景6:超級傳播者接觸過的人
查詢語句:
g.V().hasLabel('病例').where(outE('傳染').count().is(gte(5)))
.order().by(outE('傳染').count(),desc).limit(10)
.both().choose(hasLabel('病例'),identity(),both('乘坐','出現於','正常乘坐','正常出現於').simplePath())
.path()
場景7:病例傳染鏈分析(如疫情後病毒變異分析)
爲了在疫情後進行病毒變異分析,需要分析一個病例的上游傳染鏈,即某病例是被誰傳染的,上一個人又是被誰傳染的,找到直到源頭,形成一條傳染鏈。有了傳染鏈之後可以對鏈上的每個病例的病毒信息進行比對分析(假設每條病例數據裏面存儲了病毒的身份簽名信息)。下圖展示了最左邊的病例“sjz0205-123”的傳染鏈,傳染源頭則是最右邊的病例“sjz0127-9”。
場景7-1:病例的傳染鏈
查詢語句:
g.V('sjz0205-123').repeat(__.in('傳染'))
.until(__.not(__.in('傳染'))).path()
下一步計算鏈上的病毒變異次數,也就是比對鏈上病例的病毒簽名,計算去重之後病毒的種類數量。如下圖示例中的結果是變異3次。
場景7-2:傳染鏈的病毒變異次數
查詢語句:
g.V('sjz0205-123').repeat(__.in('傳染'))
.until(__.not(__.in('傳染'))).path()
.unfold().values('病毒').dedup().count()
每個病例都有一條傳染鏈,爲了分析所有病例的傳染鏈的病毒變異情況,比如找出變異次數最多的那條鏈,那麼需要先找到所有的傳染鏈,然後計算每條鏈的變異次數,最後比較各條鏈,找出變異次數最多的一條鏈。下圖展示了所有的傳染鏈。
場景7-3:找出所有傳染鏈
查詢語句:
g.V().hasLabel('病例').repeat(__.in('傳染'))
.until(__.not(__.in('傳染'))).path().dedup()
下圖展示了所有傳染鏈的病毒變異次數。
場景7-4:找出所有傳染鏈的病毒變異次數
查詢語句:
g.V().hasLabel('病例').repeat(__.in('傳染')).until(__.not(__.in('傳染'))).path().dedup()
.project('傳染鏈','變異次數').by(unfold().id().fold()).by(unfold().values('病毒').dedup().count())
場景8:病例傳染環分析
在疫情早中期,對於某個病例來說,隨着病毒傳染更多的人,往往會在該病例的周圍形成一圈一圈的傳染環,比如某病例傳染了3個人,假設被傳染的人又每個傳染3人,則第一層環上包括3人,第二層環上包含9人。爲了分析某一層環上病例的病毒變異情況,需要找到該層環上的病例,然後對環上的每個病例的病毒信息進行比對分析。下圖展示了病例“sjz0127-9”的第一層和第二層傳染環。
場景8:病例的傳染環
查詢語句:
g.V('sjz0127-9').repeat(out('傳染')).times(2).emit().path()
介紹完畢。
HugeGraph圖數據庫介紹:
HugeGraph是一款易用、高效、通用的開源圖數據庫系統(Graph Database,GitHub項目地址), 實現了Apache TinkerPop3框架及完全兼容Gremlin查詢語言, 具備完善的工具鏈組件,助力用戶輕鬆構建基於圖數據庫之上的應用和產品。HugeGraph支持百億以上的頂點和邊快速導入,並提供毫秒級的關聯關係查詢能力(OLTP), 並可與Hadoop、Spark等大數據平臺集成以進行離線分析(OLAP)。
HugeGraph典型應用場景包括深度關係探索、關聯分析、路徑搜索、特徵抽取、數據聚類、社區檢測、 知識圖譜等,適用業務領域有如網絡安全、電信詐騙、金融風控、廣告推薦、社交網絡和智能機器人等。
本系統的主要應用場景是解決百度安全事業部所面對的反欺詐、威脅情報、黑產打擊等業務的圖數據存儲和建模分析需求,在此基礎上逐步擴展及支持了更多的通用圖應用。
HugeGraph圖數據庫優勢:
- 大規模:支持百億規模的頂點及邊的存儲與分析,適合多維度、深鏈路的複雜分析場景;
- 高性能:在圖存儲和圖計算方面進行深度優化,多種批量導入工具,快速完成百億級數據導入,實現毫秒級的關聯關係查詢能力(OLTP), 且可與Hadoop、Spark等大數據平臺集成以進行離線分析;
- 可視化界面:嚮導式的元數據建模、數據導入操作,降低使用成本;提供直觀清晰的可視化分析體驗,各類圖上的多維定製化查詢及豐富的圖算法,輕鬆實現多度查詢、路徑分析、聚類、推薦等;
- 快速導入:支持File/HDFS/MySQL/PG/Oracle/SQLServer等數據源,輕鬆實現百億數據的快速導入使用,並提供可視化的導入操作,使用門檻低,方便業務人員操作;
- 完善的工具鏈:提供一鍵部署、備份、圖數據統計、升級、運維等周邊工具,並可與SparkX等大數據平臺集成進行離線分析。