使用Spark Graphx 探索你不知道的金庸武俠世界

GitHub地址:https://github.com/weijie-he/jinyong

一痹筛、緣起

2018年10月30日莺治,金庸在香港逝世廓鞠,享年94歲。

知道這個(gè)消息之后谣旁,我的情緒很低落床佳,講臺(tái)上老師在講什么仿佛也聽(tīng)不見(jiàn)了,腦海中一直在回想著先生寫(xiě)過(guò)的關(guān)于離別的句子榄审。

程英道:“三妹砌们,你瞧這些白云聚了又散,散了又聚搁进,人生離合浪感,亦復(fù)如斯。你又何必?zé)辣剩俊?她話雖如此說(shuō)影兽,卻也忍不住流下淚來(lái)。

卻聽(tīng)得楊過(guò)朗聲說(shuō)道:“今番良晤匆瓜,豪興不淺赢笨,他日江湖相逢,再當(dāng)杯酒言歡驮吱。咱們就此別過(guò)茧妒。”

金庸先生告訴了我什么是“俠”左冬。作為先生的忠實(shí)讀者桐筏,我覺(jué)得自己該做點(diǎn)什么來(lái)緬懷先生,以我自己的方式拇砰。

正好梅忌,我在學(xué)Spark,便想到了利用Spark Graphx 做金庸小說(shuō)人物關(guān)系分析圖除破。

二牧氮、需求分析

金庸先生給我們留下了什么呢?最著名的無(wú)非是“飛雪連天射白鹿瑰枫,笑書(shū)神俠倚碧鴛”這14本小說(shuō)了踱葛。最容易想到的便是對(duì)這14本書(shū)做一張人物關(guān)系分析圖。但這一來(lái)人物太多光坝,最后畫(huà)出的圖會(huì)很大尸诽;二來(lái)不同書(shū)之間的人物很多也沒(méi)什么關(guān)聯(lián),硬把他們放在同一張圖里并不妥當(dāng)盯另。最終我決定只選取人物聯(lián)系最緊密的“射雕三部曲”(《射雕英雄傳》性含、《神雕俠侶》、《倚天屠龍記》)來(lái)進(jìn)行分析鸳惯。

但是只分析人物又感覺(jué)略顯單薄商蕴。金庸小說(shuō)中還有一些其他的元素叠萍,比如如雷貫耳的稱(chēng)號(hào)(東邪西毒)蛮位、高深莫測(cè)的武功(黯然銷(xiāo)魂掌)碎捺、神兵利器(倚天劍其馏、屠龍刀)拆宛。我想把這些元素也加入到分析之中茂嗓。

同時(shí)還要考慮怎么利用Spark Graphx 的圖計(jì)算功能凶异,做一些有意義的分析改抡。

最終確立了以下需求:

分析人物之間的親密度關(guān)系

找出“專(zhuān)屬昵稱(chēng)”

(很多人物之間的交流并不會(huì)直呼其名伟众,比如黃蓉會(huì)叫郭靖“靖哥哥”理张,我想找出類(lèi)似的“專(zhuān)屬昵稱(chēng)”)

探索小說(shuō)人物中“孤島群體”(即“小圈子”)

有沒(méi)有誰(shuí)經(jīng)常被某種武功/兵器揍

三赫蛇、工作流程

3.1 獲取"射雕三部曲"小說(shuō)原文、人物名冊(cè)雾叭、稱(chēng)號(hào)\武功\武器大全等初始數(shù)據(jù)

? ? 小說(shuō)原文很容易獲取悟耘,人物名冊(cè)、稱(chēng)號(hào)\武功\武器大全 等也可以在網(wǎng)上搜到织狐。

3.2 數(shù)據(jù)預(yù)處理暂幼,將數(shù)據(jù)轉(zhuǎn)換成Graphx需要的格式

? ? Graphx需要的是頂點(diǎn)集和邊集的信息。

? ? 在人物親密度圖中移迫,我將人名旺嬉、昵稱(chēng)作為頂點(diǎn);在人物—武器關(guān)系圖中厨埋,我將人名邪媳、武器、武功作為頂點(diǎn)荡陷。

? 至于邊集信息雨效,是這樣確定的:以原文中每一句話為單位。如果在這句話中废赞,出現(xiàn)了兩個(gè)上述的“頂點(diǎn)”徽龟,則認(rèn)為他們產(chǎn)生了一次聯(lián)系。如果在這句話中唉地,出現(xiàn)了三個(gè)“頂點(diǎn)”据悔,則認(rèn)為他們兩兩之間都有一次聯(lián)系。以此類(lèi)推渣蜗。

處理完的結(jié)果保存在resources文件夾中屠尊。結(jié)果如下所示

?



?

3.3 使用Spark GraphX 生成圖

我想把聯(lián)系的次數(shù)作為邊的權(quán)重旷祸。首先就要統(tǒng)計(jì)同一個(gè)聯(lián)系出現(xiàn)的次數(shù)耕拷。這一步有點(diǎn)像WordCount,由于不想讓一些打醬油的人物出現(xiàn)托享,所以還用了個(gè)filter函數(shù)過(guò)濾骚烧。

/**

* 統(tǒng)計(jì)關(guān)系出現(xiàn)的次數(shù)

* @param sc

* @param path:邊文件

* @param num:關(guān)系數(shù)量閾值

* @return

*/

defedgeCount(sc:SparkContext,path:String,num:Int)={

valtextFile=sc.textFile(path)

valcounts=textFile.map(word=>(word,1))

.reduceByKey(_+_).filter(_._2>num)

// ?? counts.collect().foreach(println)

counts

? }

使用頂點(diǎn)集和邊集構(gòu)建圖

/**

* 構(gòu)建圖

* @param sc

* @param path1:頂點(diǎn)文件

* @param path2:邊文件

* @param num:關(guān)系數(shù)量閾值

*/

defcreatGraph(sc:SparkContext,path1:String,path2:String,num:Int)={

valhero=sc.textFile(path1)

valcounts=edgeCount(sc,path2,num)

?

valverticesAll=hero.map{line=>

valfields=line.split('')

(fields(0).toLong,fields(1))

?? }

?

valedges=counts.map{line=>

valfields=line._1.split(" ")

Edge(fields(0).toLong,fields(1).toLong,line._2)//起始點(diǎn)ID必須為L(zhǎng)ong浸赫,最后一個(gè)是屬性,可以為任意類(lèi)型

?? }

valgraph_tmp=Graph.fromEdges(edges,1L)

// ?? 經(jīng)過(guò)過(guò)濾后有些頂點(diǎn)是沒(méi)有邊赃绊,所以采用leftOuterJoin將這部分頂點(diǎn)去除

valvertices=graph_tmp.vertices.leftOuterJoin(verticesAll).map(x=>(x._1,x._2._2.getOrElse("")))

valgraph=Graph(vertices,edges)

?

graph

? }

至此既峡,需求中的第一點(diǎn):人物親密度關(guān)系圖已經(jīng)生成。

類(lèi)似的碧查,我們更換一下頂點(diǎn)集和邊集运敢,就可以生成人物——武器\武功的關(guān)系圖,從而找出有沒(méi)有誰(shuí)經(jīng)常被某種武功/兵器揍忠售。

3.4 使用Spark GraphX 處理圖

可以通過(guò)找出度為1或2的點(diǎn)传惠,來(lái)尋找“專(zhuān)屬昵稱(chēng)”。

/**

* 找出度為1或2的點(diǎn)

* @param g

* @tparam VD

* @tparam ED

* @return

*/

defminDegrees[VD,ED](g:GraphOps[VD,ED])={

// ?? g.degrees.filter(_._2<3).map(_._1).collect().mkString("\n")

g.degrees.filter(_._2<3).map(_._1).collect().map(a=>a.toInt)

? }

通過(guò)使用內(nèi)置函數(shù)connectedComponents()可以找到小說(shuō)人物中“孤島群體”(即“小圈子”)稻扬。

/**

* 使用連通組件找到孤島人群

* @param g

* @tparam VD

* @tparam ED

* @return

*/

defisolate[VD,ED](g:GraphOps[VD,ED])={

g.connectedComponents.vertices.map(_.swap).groupByKey().map(_._2).collect().mkString("\n")

? }

由于之前我們是每本書(shū)都生成一張圖卦方,最后我們還需要把這幾張圖合并為一張圖。

思路就是先取得所有頂點(diǎn)信息泰佳,去除盼砍,再對(duì)這些頂點(diǎn)重新編號(hào)。再對(duì)這些新生成的點(diǎn)重新構(gòu)建邊逝她。

/**

* 合并2張圖

* @param g1

* @param g2

* @return

*/

defmergeGraphs(g1:Graph[String,Int],g2:Graph[String,Int])={

valv=g1.vertices.map(_._2).union(g2.vertices.map(_._2)).distinct().zipWithIndex()

?

defedgeWithNewVid(g:Graph[String,Int])={

g.triplets.map(et=>(et.srcAttr,(et.attr,et.dstAttr)))

.join(v)

.map(x=>(x._2._1._2,(x._2._2,x._2._1._1)))

.join(v)

.map(x=>newEdge(x._2._1._1,x._2._2,x._2._1._2))

?? }

defreduceEdge(g3:Graph[String,Int],g4:Graph[String,Int])={

edgeWithNewVid(g3).union(edgeWithNewVid(g4)).

map(e=>((e.dstId,e.srcId),e.attr)).

reduceByKey(_+_).

map(e=>Edge(e._1._1,e._1._2,e._2))

?? }

Graph(v.map(_.swap),reduceEdge(g1,g2))

? }

3.5 導(dǎo)出到Gephi

我們可以把圖像按照gexf格式輸出浇坐,然后在Gephi中打開(kāi),就可以進(jìn)行圖形化展示黔宛。

/**

* 輸出為gexf格式

* @param g:圖

* @tparam VD

* @tparam ED

* @return

*/

deftoGexf[VD,ED](g:Graph[VD,ED])={

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+

"<gexf xmlns=\"http://www.gexf.net/1.2draft\" version=\"1.2\">\n"+

" <graph mode=\"static\" defaultedgetype=\"directed\">\n? "+

"<nodes>\n "+

g.vertices.map(v=>"? <node id=\""+v._1+"\" label=\""+v._2+"\" />\n").collect().mkString+

"</nodes>\n? "+

"<edges>\n"+

g.edges.map(e=>"? <edge source=\""+e.srcId+"\" target=\""+e.dstId+"\" weight=\""+e.attr+"\"/>\n").

collect().mkString+

"</edges>\n ? ? ?? </graph>\n ? ?? </gexf>"

?

? }

四吗跋、結(jié)果展示與分析

以下圖片的高清完整版可在output/pics中找到

4.1 人物親密度關(guān)系分析

可以看出郭靖和黃蓉的顏色是最深的(聯(lián)系是最緊密的)。這是因?yàn)樗麄冊(cè)凇渡涞瘛泛汀渡竦瘛分卸加泻芏鄳蚍菽选跌宛!渡竦瘛分械哪信餍↓埮蜅钸^(guò)聯(lián)系也很緊密。相比之下《倚天》中的男女主張無(wú)忌和趙敏直接的線就淡的多了积仗。一方面疆拘,這是因?yàn)橼w敏的出場(chǎng)時(shí)間太晚(全書(shū)40章,趙敏在第23章才出場(chǎng))寂曹。另一方面哎迄,張無(wú)忌優(yōu)柔寡斷,情感方面也一直在趙敏和周芷若之間猶豫不決隆圆,導(dǎo)致張無(wú)忌的情感線被周芷若分流了許多漱挚。

4.2 “專(zhuān)屬昵稱(chēng)”分析

?

?

由于我只是篩選出了度為1和2的點(diǎn),但有些點(diǎn)是人名渺氧,而不是昵稱(chēng)旨涝,不必看。

我原來(lái)以為“專(zhuān)屬昵稱(chēng)”只出現(xiàn)在情侶之間侣背,但發(fā)現(xiàn)有兩個(gè)例外白华。

洪七公——靖兒

這兩人情同父子慨默。郭靖自幼喪父,洪七公也沒(méi)有子嗣弧腥。俗話說(shuō)厦取,“一日為師終身為父”,我覺(jué)得這兩個(gè)人不是父子管搪,甚是父子虾攻。所以有這樣的“專(zhuān)屬昵稱(chēng)”也不奇怪。

也許江南七怪也和郭靖情同父子更鲁,但可能是因?yàn)槌霈F(xiàn)的頻率不夠高台谢,所以被過(guò)濾掉了,這張圖上并沒(méi)有出現(xiàn)岁经。

陸無(wú)雙——傻蛋

全書(shū)只有陸無(wú)雙一人可以叫楊過(guò)”傻蛋“朋沮,因?yàn)楫?dāng)初楊過(guò)騙陸無(wú)雙自稱(chēng)傻蛋。

那道姑道:“我?guī)讜r(shí)騙過(guò)你了缀壤?喂樊拓,小子,你叫甚么名字塘慕?”楊過(guò)道:“人人都叫我傻蛋筋夏,你不知道么?你叫甚么名字图呢?”那道姑道:“傻蛋条篷,你只叫我仙姑就得啦「蛑”

摘錄了一下原文赴叹,發(fā)現(xiàn)短短幾句話,這道姑(陸無(wú)雙)就笑了2次指蚜,足見(jiàn)他們相處的多么愉快乞巧。過(guò)兒一生孤苦,和陸無(wú)雙在一起的日子也算是為數(shù)不多的快樂(lè)時(shí)光摊鸡。我覺(jué)得他們倆很有成為情侶的可能绽媒,只可惜過(guò)兒心里已經(jīng)有了小龍女。最后他們倆結(jié)為了兄妹免猾,也算是一段“有情人終成兄妹”的悲劇故事是辕。

4.3 “孤島人群”分析

?

?

發(fā)現(xiàn)只有3個(gè)“孤島人群”(小團(tuán)體)。

簡(jiǎn)捷和薛公遠(yuǎn)是《倚天屠龍記》中被金花婆婆打傷猎提,找胡青牛治病的人获三。和他們有交集的人確實(shí)很少。

李萍被段天德綁架,很長(zhǎng)一段時(shí)間內(nèi)只有他們兩個(gè)在一起石窑,別人都不知道他們?nèi)チ四摹?/p>

術(shù)赤和察合臺(tái)是成吉思汗的兩個(gè)兒子。和他們有交集的人也很少蚓炬。

這三本書(shū)中涉及到的人物松逊,即使過(guò)濾完,也有將近200號(hào)人肯夏。如果在現(xiàn)實(shí)生活中经宏,200人中應(yīng)該會(huì)有更多的小團(tuán)體,而且也不會(huì)全是2人組驯击,可能有3~5人小團(tuán)體烁兰。

以下是我認(rèn)為可能的兩點(diǎn)原因:

小說(shuō)中,配角是為主角服務(wù)的徊都,一般不會(huì)獨(dú)立于主線人物之外去寫(xiě)小團(tuán)體

即便需要沪斟,寫(xiě)2人也夠了,沒(méi)必要花筆墨寫(xiě)

4.4 人物——武功\兵器分析

主要想看誰(shuí)經(jīng)常被哪種武功\兵器揍暇矫。

無(wú)忌——玄冥神掌

無(wú)忌小時(shí)候就因?yàn)橹辛诵ど裾撇铧c(diǎn)死掉主之,長(zhǎng)大后也經(jīng)常和玄冥二老斗。

郭靖——蛤蟆功

蛤蟆功可以說(shuō)是郭靖發(fā)明的李根,就是因?yàn)樗鄹牧恕毒抨幷娼?jīng)》槽奕,寫(xiě)了本“九陰假經(jīng)”,才讓歐陽(yáng)鋒練成了蛤蟆功房轿。后來(lái)也數(shù)次和歐陽(yáng)鋒的蛤蟆功交手粤攒。《神雕》中小楊過(guò)也學(xué)了點(diǎn)蛤蟆功囱持,被郭靖發(fā)現(xiàn)了夯接,這又產(chǎn)生了一次交集。

楊過(guò)——金輪纷妆、浮塵

這是書(shū)中兩大反派金輪法王和李莫愁的武器钻蹬。

五、后記

? 人人都知道金庸凭需,可大多是通過(guò)影視作品问欠,讀過(guò)原著的人少的可憐。做這個(gè)項(xiàng)目粒蜈,在緬懷先生的同時(shí)顺献,也希望有更多的人能去讀一讀原著,體會(huì)一下先生筆下原汁原味的江湖枯怖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末注整,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肿轨,老刑警劉巖寿冕,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異椒袍,居然都是意外死亡驼唱,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)驹暑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)玫恳,“玉大人,你說(shuō)我怎么就攤上這事优俘【┌欤” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵帆焕,是天一觀的道長(zhǎng)惭婿。 經(jīng)常有香客問(wèn)我,道長(zhǎng)叶雹,這世上最難降的妖魔是什么审孽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮浑娜,結(jié)果婚禮上佑力,老公的妹妹穿的比我還像新娘。我一直安慰自己筋遭,他們只是感情好打颤,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著漓滔,像睡著了一般编饺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上响驴,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天透且,我揣著相機(jī)與錄音,去河邊找鬼豁鲤。 笑死秽誊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的琳骡。 我是一名探鬼主播锅论,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼楣号!你這毒婦竟也來(lái)了最易?” 一聲冷哼從身側(cè)響起怒坯,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎藻懒,沒(méi)想到半個(gè)月后剔猿,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嬉荆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年归敬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片员寇。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弄慰,死狀恐怖第美,靈堂內(nèi)的尸體忽然破棺而出蝶锋,到底是詐尸還是另有隱情,我是刑警寧澤什往,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布扳缕,位于F島的核電站,受9級(jí)特大地震影響别威,放射性物質(zhì)發(fā)生泄漏躯舔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一省古、第九天 我趴在偏房一處隱蔽的房頂上張望粥庄。 院中可真熱鬧,春花似錦豺妓、人聲如沸惜互。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)训堆。三九已至,卻和暖如春白嘁,著一層夾襖步出監(jiān)牢的瞬間坑鱼,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工絮缅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鲁沥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓耕魄,卻偏偏與公主長(zhǎng)得像黍析,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子屎开,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359