上一篇文章《知識圖譜在大數據中的應用》咱們介紹了知識圖譜的一些概念和應用場景,今天咱們就來看一個具體的應用案例瞭解下知識圖譜的應用。用戶增加對於一個APP的生存起到了相當重要的做用,沒有持續的用戶增加,再好的APP也不會走的長遠,爲了得到更多的用戶,APP運營商每每會鼓勵老用戶拉新並給與獎勵,好比趣頭條的收徒模式,用戶每收一個徒弟就會獲得幾塊到十幾塊的現金返現,可是這種模式同時也會引發廣大黑產團伙的注意,黑產會利用各類手段來薅這些APP運營商的羊毛。node
中國有句老話,叫物以類聚,人以羣分,在反做弊和市場營銷等應用中,若是咱們能根據用戶間的某些聯繫發現社羣,而後對這些社羣進行反做弊分析或商品推薦,每每會起到意想不到的效果。算法
本文就來介紹一個簡單的社羣發現的實踐。構建社羣咱們首先須要找到社羣用戶的某種聯繫,上文提到的收徒模式自己就是用戶間的一個自然聯繫,咱們能夠根據用戶的師徒關係來構建社羣。以下圖所示,根據師徒關係咱們構建了一個社羣,點表示用戶,邊表示師徒關係。
shell
有了這樣的社羣以後,咱們就能夠基於社羣維度分析設備及用戶行爲的異常,好比單個設備登錄過多的用戶,設備一直處於充電狀態,全部用戶行爲高度一致等,同時能夠計算社羣用戶做弊率來經過已知做弊用戶來發現新的做弊用戶。數據庫
理清了需求以後咱們開始着手根據用戶師徒關係構建社羣。對"緊密聯繫"的不一樣理解產生了不少社區發現算法。下圖是幾種經典的社羣發現算法。express
由於本文重點不是講述社羣發現算法,因此這個算法具體的含義此處略過,有感興趣的讀者可自行研究。本文選用了最簡單的連通圖算法來實現社羣發現,即只要兩個節點之間有邊咱們就把它們歸屬爲一個社羣。下面咱們進入根據用戶師徒關係生成社羣階段。編程
Spark Graphx自己就提供了構建圖並生成連通圖的接口,咱們只須要按要求輸入數據就行了。以下圖所示:
segmentfault
咱們構建點和邊,而後調用Graphx接口生成圖,最後調用圖的接口直接獲取連通圖。須要注意的是,Spark Graphx構建點和邊時,id須要用Long類型的數字表示,因此咱們須要維護一張用戶id到數字id的維表。瀏覽器
//構建用戶節點 val users: RDD[(VertexId, String)] = spark.sparkContext.parallelize(Array((3L, "u3"), (7L, "u7"),(5L, "u5"), (2L, "u2"), (4L, "u4"),(6L, "u6"),(8L, "u8"))) //構建用戶邊 val relationships: RDD[Edge[String]] = spark.sparkContext.parallelize(Array(Edge(7L, 3L,""), Edge(5L, 3L,""),Edge(5L, 2L,""), Edge(6L, 4L,""),Edge(8L, 6L,""))) //組合節點和邊構建圖 val graph = Graph(users, relationships) //從圖中抽取出連通圖 val components = graph.connectedComponents() //獲取連通圖中的點,vertices是一個tuple類型,key分別爲全部的頂點id,value爲key所在的連通圖id(連通圖中頂點id最小值) val vertices = components.vertices
獲得的vertices是以下的k-v數據:網絡
/** * vertices: * (6,4) * (8,4) * (3,2) * (7,2) * (5,2) * * 是一個tuple類型,key分別爲全部的頂點id,value爲key所在的連通圖id(連通圖中頂點id最小值) */
而後咱們將邊relationships與vertices求出每條邊所在連通圖裏頂點id最小值。數據結構
val result = relationships.map(x =>{ (x.srcId,x.dstId.toString) }).join(vertices) .map(y =>{ // (7,(3,2)) => (2,(7,3)) (y._2._2,(y._1,y._2._1)) })
咱們將結果存入圖數據Neo4j,可視化後以下所示,能夠看到咱們獲得了兩個社羣。
至此,咱們利用Spark Graphx構建出了社羣,每一個社羣都有本身的一個社羣id,而後咱們就能夠基於社羣作一些具體分析了,好比,我能夠計算社羣做弊率,並取出TOP N的社羣,以下所示。
想及時瞭解更多大數據實踐,請關注個人公衆號《大數據技術進階》
上面只是一個簡單的示例,其實咱們能夠給點和邊加上更多的屬性,利用圖的特性進行檢索,能夠更高效的檢索出更多的信息。爲了更方便的存儲和查詢社羣內的數據,咱們能夠將社羣存儲到圖數據庫Neo4j。上面的社羣圖就是用Neo4j展現的,那麼什麼是Neo4j呢?下面咱們簡單的介紹下。
Neo4j是一個嵌入式的、基於磁盤的、具有徹底的事務特性的圖數據存儲引擎。做爲圖數據庫,Neo4j最大的特色是關係數據的存儲。圖數據庫除了可以像普通的數據庫同樣存儲一行一行的數據以外,還能夠很方便的存儲數據之間的關係信息。
例如,對於一個社交網絡的用戶數據庫,你除了要存儲每一個用戶的姓名、性別、喜愛這些基本信息外,你還須要存儲一個用戶和哪些用戶是朋友,和哪一個用戶是情侶這些關係數據,這個時候Neo4j這樣的圖數據庫就能夠派上用場啦。
經過下圖,你們能夠了解下什麼是圖數據庫以及什麼是關係數據。
在上圖中,包含兩個標籤爲"人"的數據節點,分別表明Ann和Dan兩個用戶。這兩個數據節點還包含姓名、出生地等屬性信息,用於表示兩個用戶的基本信息,就如同常規數據庫中的兩行數據。
除此以外,兩個數據節點之間還包含兩條關係數據,即Ann嫁給了Dan,Ann和Dan同居。利用這些關係數據,你就能夠方便的做出基於關係的查詢,例如你能夠查詢Ann跟誰結婚了,這就是圖數據庫的優點。
可能有人會說,上邊寫的這種關係數據結構,SQL也能夠經過多表join等方法實現,那要Neo4j還有什麼用?但畢竟術業有專攻,對於大量、複雜的關係數據處理,Neo4j在性能和使用方便程度上都是要遠勝於SQL的。下邊給你們簡單總結下Neo4j的特色。
Cypher是Neo4j的圖形查詢語言,關鍵字大小寫不敏感。語法和SQL很像,學起來相對簡單。
MATCH <pattern> WHERE <conditions> RETURN <expressions>
() 表示節點
[] 表示關係,關係是有向的,鏈接的點分爲源點和目標點
{} 表示屬性,每一個屬性經過key:value的形式表示,多個屬性之間用逗號隔開,關係也能夠有屬性
插入一個節點 CREATE (n:Person {name : 'Andres'}); 插入一條邊 MATCH (a:Person),(b:Person) WHERE a.name = 'Node A' AND b.name = 'Node B‘ CREATE (a)-[r:Follow]->(b); 更新節點 MATCH (n:Person { name: 'Andres' }) SET n.name = 'Taylor'; 刪除節點 MATCH (n:Person { name:'Taylor' }) DETACH DELETE n; 刪除邊 MATCH (a:Person)-[r:Follow]->(b:Person) WHERE a.name = 'Node A' AND b.name = 'Node B‘ DELETE r; 查詢一個節點的全部Follow MATCH (:Person { name:'Taylor' })-[r:Follow]->(Person) RETURN Person.name; 查詢一個節點最短路徑 MATCH (ms:Person { name:'Node A' }),(cs:Person { name:'Node B' }), p = shortestPath((ms)-[r:Follow]-(cs)) RETURN p; 清空數據庫 MATCH (n) DETACH DELETE n
經過Neo4j瀏覽器就能夠直接進行圖的查詢。
咱們使用Cypher查詢語言對Neo4j中的一個家庭進行建模,包括年齡,性別和家庭成員之間的關係等我的屬性。咱們建立了一些朋友來擴大咱們的社交圖,而後添加鍵/值對來生成每一個用戶看過的電影列表。最後,咱們查詢了咱們的數據,使用圖形分析來搜索一個用戶沒有看到但可能喜歡的電影。
建立家庭成員節點及關係
CREATE (person:Person {name: "Steven", age: 45}) RETURN person CREATE (person:Person {name: "Michael", age: 16}) RETURN person CREATE (person:Person {name: "Rebecca", age: 7}) RETURN person CREATE (person:Person {name: "Linda",age:40}) RETURN person MATCH (steven:Person {name: "Steven"}), (linda:Person {name: "Linda"}) CREATE (steven)-[:IS_MARRIED_TO]->(linda) return steven, linda MATCH (michael:Person {name: "Michael"}), (rebecca:Person {name: "Rebecca"}) CREATE (michael)-[:IS_SIBLILNG]->(rebecca) return michael, rebecca MATCH (steven:Person {name: "Steven"}), (michael:Person {name: "Michael"}) CREATE (steven)-[:HAS_CHILD]->(michael) return steven, michael MATCH (steven:Person {name: "Steven"}), (rebecca:Person {name: "Rebecca"}) CREATE (steven)-[:HAS_CHILD]->(rebecca) return steven, rebecca MATCH (linda:Person {name: "Linda"}), (michael:Person {name: "Michael"}) CREATE (linda)-[:HAS_CHILD]->(michael) return linda, michael MATCH (linda:Person {name: "Linda"}), (rebecca:Person {name: "Rebecca"}) CREATE (linda)-[:HAS_CHILD]->(rebecca) return linda, Rebecca
添加朋友節點及關係,組成社交網絡
MATCH (michael:Person {name: "Michael"}) CREATE (michael)-[:FRIEND]->(charlie:Person {name: "Charlie", age: 16}) RETURN michael, charlie MATCH (michael:Person {name: "Michael"}) CREATE (michael)-[:FRIEND]->(koby:Person {name: "Koby"}) RETURN michael, koby MATCH (michael:Person {name: "Michael"}) CREATE (michael)-[:FRIEND]->(grant:Person {name: "Grant"}) RETURN michael, grant MATCH (rebecca:Person {name: "Rebecca"}) CREATE (rebecca)-[:FRIEND]->(jordyn:Person {name: "Jordyn"}) RETURN rebecca, jordyn MATCH (rebecca:Person {name: "Rebecca"}) CREATE (rebecca)-[:FRIEND]->(katie:Person {name: "Katie"}) RETURN rebecca, katie
添加電影節點及關係,並攜帶打分屬性
CREATE (movie:Movie {title:"Avengers"}) RETURN movie MATCH (michael:Person {name:"Michael"}), (avengers:Movie {title:"Avengers"}) CREATE (michael)-[:HAS_SEEN {rating:5}]->(avengers) return michael, avengers CREATE (movie:Movie {title:"Batman"}) RETURN movie CREATE (movie:Movie {title:"Gone with the Wind"}) RETURN movie CREATE (movie:Movie {title:"Spongebob Square Pants"}) RETURN movie CREATE (movie:Movie {title:"Avengers 2"}) RETURN movie MATCH (charlie:Person {name:"Charlie"}), (movie:Movie {title:"Batman"}) CREATE (charlie)-[:HAS_SEEN {rating:4}]->(movie) return charlie, movie MATCH (charlie:Person {name:"Charlie"}), (movie:Movie {title:"Gone with the Wind"}) CREATE (charlie)-[:HAS_SEEN {rating:0}]->(movie) return charlie, movie MATCH (koby:Person {name:"Koby"}), (movie:Movie {title:"Batman"}) CREATE (koby)-[:HAS_SEEN {rating:4}]->(movie) return koby, movie MATCH (koby:Person {name:"Koby"}), (movie:Movie {title:"Avengers 2"}) CREATE (koby)-[:HAS_SEEN {rating:5}]->(movie) return koby, movie MATCH (grant:Person {name:"Grant"}), (movie:Movie {title:"Spongebob Square Pants"}) CREATE (grant)-[:HAS_SEEN {rating:1}]->(movie) return grant, movie MATCH (jordyn:Person {name:"Jordyn"}), (movie:Movie {title:"Spongebob Square Pants"}) CREATE (jordyn)-[:HAS_SEEN {rating:5}]->(movie) return jordyn, movie MATCH (michael:Person {name: "Michael"}) SET michael.gender = "male" RETURN michael MATCH (rebecca:Person {name: "Rebecca"}) SET rebecca.gender = "female" RETURN rebecca
最後咱們經過下面語句查詢steven的孩子的男性朋友看過並且打分大於3分的電影
MATCH (steven:Person {name:"Steven"})-[:HAS_CHILD]-(child:Person)-[:FRIEND]-(friend:Person)-[hasSeen:HAS_SEEN]-(movie:Movie) WHERE child.gender = "male" AND hasSeen.rating > 3 RETURN DISTINCT movie.title
本文主要介紹了利用Spark Graphx實現了一個簡單的連通圖社羣發現示例,並將社羣存入到圖數據庫Neo4j中,同時進一步介紹了Neo4j的一些概念和使用,最後用Neo4j演示了一個社交網絡的圖檢索示例。