哇標(biāo)題看起來挺厲害的,其實沒有很難啦挖胃,而且非常有趣。
我為什么要做這個
我本人就很討厭看到這種標(biāo)題——我為什么xxx梆惯?喵喵喵冠骄?你為什么xxx關(guān)我什么事?唉加袋,人總是會變成自己討厭的人嘛。
換個說法抱既,說說做這個的初衷职烧,其實沒有什么初衷,被逼的防泵。
為了盡早完成畢業(yè)設(shè)計可以出去打工蚀之,我選擇提前聯(lián)系導(dǎo)師。于是就被提供了兩個題目捷泞,我決定做水利相關(guān)的知識圖譜足删。你看,多么河海特色锁右!水水水失受,離開水就不能活了讶泰,沒毛病。
于是先從圖數(shù)據(jù)庫入手拂到,neo4j是個好東西痪署,就決定是你了。在構(gòu)建復(fù)雜的水水水相關(guān)實體前兄旬,我先拿中國行政區(qū)劃數(shù)據(jù)試個水狼犯,邏輯相對簡單,就一種隸屬關(guān)系领铐。
獻(xiàn)上美圖:
起步
首先悯森,準(zhǔn)備一下原料,中國行政區(qū)劃元數(shù)據(jù)绪撵,不求完全結(jié)構(gòu)化瓢姻,起碼半結(jié)構(gòu)化,不然會很痛苦莲兢,畢竟有70W+數(shù)據(jù)汹来。
Neo4j一套,python或者什么腳本語言一款改艇,腦子一坨收班,足夠了。
首先我拿到70W+的XML文件谒兄,哎喲我去摔桦,ls
一下都卡半天,我都害怕python會不會應(yīng)付不來承疲,跑一遍幾小時可能就太慘了邻耕。
首先用python列出幾個文件:
files = os.listdir(datas_path)
print(files[0])
哈哈哈,我先搞個文件名燕鸽,然后直接打開這個文件兄世,避免了「我不能ls就不知道文件名,不知道文件名就不能看內(nèi)容」的問題啊研∩髟可以看到里面的大致結(jié)構(gòu)恍飘,這里取一部分:
<mdExtInfo>
<obj_att>
<AD_GRAD>
<key>行政區(qū)劃級別</key>
<value>村</value>
</AD_GRAD>
<AD_CODE>
<key>行政區(qū)劃代碼</key>
<value>441323121207</value>
</AD_CODE>
<UP_AD_NAME>
<key>上級行政區(qū)劃名稱</key>
<value>白盆珠鎮(zhèn)</value>
</UP_AD_NAME>
<AD_FULL_NAME>
<key>行政區(qū)劃全稱</key>
<value>廣東省-惠州市-惠東縣-白盆珠鎮(zhèn)-布心村民委員會</value>
</AD_FULL_NAME>
<AD_NAME>
<key>行政區(qū)劃名稱</key>
<value>布心村民委員會</value>
</AD_NAME>
</obj_att>
</mdExtInfo>
枚舉行政區(qū)劃級別
唉额湘,幾十萬文件营勤,我都不知道有幾種行政區(qū),先跑一遍看看有哪些區(qū)劃級別沟娱。
核心代碼:
cates = set()
for file in files:
doc = parse(datas_path + file)
for item in doc.iterfind('mdExtInfo/obj_att/AD_GRAD'):
title = item.findtext('value')
cates.add(title)
print(title, file, cates)
大概不到十分鐘就能跑完氛驮,最后得到一共有6種級別:縣級、村济似、國家級矫废、鄉(xiāng)級盏缤、地市級、省級磷脯。
給這些文件分類
每次都跑十分鐘太過分了蛾找,而且我發(fā)現(xiàn),不同的級別赵誓,XML里的結(jié)構(gòu)不同打毛,比如省級有十幾個字段,鄉(xiāng)級只有五個俩功。所以分類是必須的幻枉。
按照不同級別,將同級別的文件名放在一個文件里诡蜓,方便以后遍歷熬甫。
到這里,我突然明白了一些道理蔓罚,來來來椿肩,給自己加點(diǎn)戲!
本來以為都是非結(jié)構(gòu)化數(shù)據(jù)豺谈,剛好有在看機(jī)器學(xué)習(xí)郑象,哇可以學(xué)以致用了,把數(shù)據(jù)扔進(jìn)去茬末,自動聚類厂榛,開心的當(dāng)上調(diào)參男孩…… 然而我發(fā)現(xiàn)數(shù)據(jù)格式化的挺不錯,就開始自己去找他們的特征丽惭,這特喵不就是——人肉學(xué)習(xí)嘛击奶!然而再反過來想,機(jī)器學(xué)習(xí)到底是什么呢责掏?
構(gòu)造 Neo4j 需要的 CSV
抽取屬性
這么一大坨數(shù)據(jù)要導(dǎo)入的柜砾,用CQL實在太慢,生成指定格式的CSV文件就OK啦~真簡單~
AD_CODE,AD_NAME,AD_GRAD,AD_AREA,AD_FULL_NAME,AD_ABBR_NAME,LOW_LEFT_LONG,UP_RIGHT_LONG,UP_RIGHT_LAT,AD_STAT_LAT,AD_STAT,LOW_LEFT_LAT,AD_STAT_LONG
610000000000,陜西省,省級,197025.84,陜西省,陜西,105.4872,111.2422,39.58533,34.26645358,西安市新城區(qū)新城大院,31.70674,108.94952476
650000000000,新疆維吾爾自治區(qū),省級,1660000,新疆維吾爾自治區(qū),新疆,73.49989,96.38728,49.18006,43.79179105,烏魯木齊市中山路479號,34.33374,87.62484437
130000000000,河北省,省級,187700,河北省,河北,113.4551,119.8485,42.61558,38.03705206,石家莊市長安區(qū)裕華東路113號,36.04881,114.52429283
...
哈哈哈哈换衬,先把它們有用的屬性都拿出來局义,按級別分類放到6個CSV文件中,結(jié)構(gòu)如上冗疮,從此就可以舒服很多了,畢竟不用遍歷幾十萬個文件了檩帐。
構(gòu)建行政單位實體
等…… 等一下……
其實在這之前术幔,我發(fā)現(xiàn)了很多坑爹的地方,比如地市級單位有14個屬性湃密,但有個別數(shù)據(jù)只有幾個屬性诅挑,所以遍歷的時候一定要判斷四敞,不存在的話要用空字符串替代,不然CSV對不齊后面就會更坑拔妥。
首先做個屬性映射忿危,我不想存到數(shù)據(jù)庫里字段都是瞎眼的大寫:
prop_maps = {
'AD_CODE': 'code:ID(AD)',
'AD_NAME': 'name',
# 'AD_GRAD': 'level',
'AD_AREA': 'area:double',
'AD_FULL_NAME': 'full_name',
'AD_ABBR_NAME': 'abbr_name',
'LOW_LEFT_LONG': 'low_left_longtitude:double',
'UP_RIGHT_LONG': 'up_right_longtitude:double',
'UP_RIGHT_LAT': 'up_right_latitude:double',
'AD_STAT_LAT': 'station_latitude:double',
'AD_STAT': 'station',
'LOW_LEFT_LAT': 'low_left_latitude:double',
'AD_STAT_LONG': 'station_longtitude:double'
# 'UP_AD_NAME': 'father_name'
}
根據(jù) Neo4j Import Tool 上的格式,我們要注意ID字段没龙,以及LABEL字段铺厨,我決定給它們兩個標(biāo)簽,一個是級別硬纤,比如Province解滓,一個是AD,表示Administrative Division 行政區(qū)劃筝家,畢竟這個數(shù)據(jù)庫還會有很多水水水的數(shù)據(jù)進(jìn)來洼裤。
code:ID(AD),name,area:double,full_name,abbr_name,low_left_longtitude:double,up_right_longtitude:double,up_right_latitude:double,station_latitude:double,station,low_left_latitude:double,station_longtitude:double,:LABEL
610000000000,陜西省,197025.84,陜西省,陜西,105.4872,111.2422,39.58533,34.26645358,西安市新城區(qū)新城大院,31.70674,108.94952476,AD;Province
650000000000,新疆維吾爾自治區(qū),1660000,新疆維吾爾自治區(qū),新疆,73.49989,96.38728,49.18006,43.79179105,烏魯木齊市中山路479號,34.33374,87.62484437,AD;Province
130000000000,河北省,187700,河北省,河北,113.4551,119.8485,42.61558,38.03705206,石家莊市長安區(qū)裕華東路113號,36.04881,114.52429283,AD;Province
...
最后的數(shù)據(jù)如上所示(省級部分)。直接看一坨CSV體驗很差對吧溪王,其實不用看全腮鞍,看到主要的幾個字段就是可以了,比如code:ID(AD)
莹菱,表示code
字段移国,行政代碼,ID
表示我要用于之后導(dǎo)入關(guān)系的主鍵芒珠,(AD)
表示這個ID不是全局的桥狡,是一個叫AD
的group,可以理解命名空間皱卓,詳情請看官方文檔裹芝。還有LABEL
字段,表示標(biāo)簽……廢話…… area:double
表示用雙精度娜汁,否則默認(rèn)為字符串嫂易,你不希望數(shù)字都變成字符串吧。
構(gòu)建行政區(qū)上下級關(guān)系
在這之前掐禁,必須要驗證很多細(xì)節(jié)怜械,比如真的所有數(shù)據(jù)都有上級這個屬性嘛?所有數(shù)據(jù)都是完美的符合規(guī)則的嘛傅事?果然不是缕允。。蹭越。
甚至障本,數(shù)據(jù)還有一些錯誤的,比如有一條就是陜西省-嘉峪關(guān)市-市轄區(qū)
,為什么我發(fā)現(xiàn)他是錯誤的呢驾霜?我是陜西人嘛案训?我地理及格了嘛?NONONO粪糙!因為我想驗證一下所有地名的全稱强霎,去掉最后一段,即上級行政區(qū)全稱蓉冈,是否存在城舞。哈哈哈不存在的,結(jié)果就是沒找到甘肅省-嘉峪關(guān)市-市轄區(qū)
這條數(shù)據(jù)洒擦,仿佛鏈條都斷掉了椿争。
于是我跑了一下元數(shù)據(jù),看看嘉峪關(guān)市到底是哪里的熟嫩,發(fā)現(xiàn)陜西和甘肅都有秦踪,見鬼了,去網(wǎng)上搜了一下掸茅,不存在的椅邓。要把這條數(shù)據(jù)的「陜西」替換為「甘肅」,不想動元數(shù)據(jù)昧狮,所以寫在了清理數(shù)據(jù)的腳本中景馁。
用 UP_AD_NAME 字段找上級
用幾個字典,存下地名和代號逗鸣,然后拿 UP_AD_NAME 去匹配上一級的字典合住。
我太天真了哈哈哈,測試一下有多少重名的撒璧,果然到了縣級就沒法看了透葛,什么「西城區(qū)」,哪個城市都有卿樱。
所以干脆拿全名前綴來匹配
直接把所有行政區(qū)存到一個字典僚害,全名為鍵,代號為值繁调。找一個行政區(qū)的上級時萨蚕,其實就是去掉最后一段。比如 江蘇省-南京市-江寧區(qū)
的上級就是 江蘇省-南京市
蹄胰,我驗證了一下是不是字典里都存在岳遥。果然又?叒叕有幾個不聽話的數(shù)據(jù)。
比如 新疆生產(chǎn)建設(shè)兵團(tuán)-農(nóng)四師-兵團(tuán)七十六團(tuán)--三連
最后居然兩個-
裕寨,中間是空的寒随,我發(fā)現(xiàn)有這樣的 新疆生產(chǎn)建設(shè)兵團(tuán)-農(nóng)四師-兵團(tuán)七十六團(tuán)-兵團(tuán)七十六團(tuán)
數(shù)據(jù),就嘗試給他們都加上,但結(jié)果是有些又找不到了妻往。所以我干脆刪了,后果是某些村級直接屬于縣級试和,算了算了沒毛病讯泣,畢竟數(shù)據(jù)沒給全,至少這樣沒什么大問題阅悍。
最后好渠!居然發(fā)現(xiàn)有相同全名的,無話可說节视,仔細(xì)觀察可以發(fā)現(xiàn)拳锚,都是村級的,名字一樣寻行,代號卻不同霍掺,只能認(rèn)為是干擾數(shù)據(jù),隨緣選一個吧拌蜘。
完成CSV
:START_ID(AD),:END_ID(AD),:TYPE
610000000000,000000000000,BELONGS_TO
650000000000,000000000000,BELONGS_TO
130000000000,000000000000,BELONGS_TO
660000000000,000000000000,BELONGS_TO
140000000000,000000000000,BELONGS_TO
520000000000,000000000000,BELONGS_TO
360000000000,000000000000,BELONGS_TO
630000000000,000000000000,BELONGS_TO
230000000000,000000000000,BELONGS_TO
...
最后就是生成這樣的數(shù)據(jù)杆烁,再寫個導(dǎo)入 neo4j 的腳本即可。
怎么玩 Neo4j
算了下一篇文章再寫吧简卧,累死了……