OpenStreetMap 數(shù)據(jù)整理實踐

1. 選定區(qū)域

Hong Kong, China

選取的區(qū)域是香港的一部分璃岳,hong-kong.osm 大小 92.6M。經(jīng)緯度邊界:22.2233, 114.115, 22.3475, 114.275悔捶,以下為選定區(qū)域示意圖:

下載地圖的范圍示意

2. 選擇區(qū)域的原因

香港是一個神奇的城市铃慷,你可以在這里享受購物的快樂、世界各地的美食蜕该,也可以體驗獨特的香港文化犁柜。很喜歡在香港的街頭閑逛,總是會有很多驚喜堂淡。那么馋缅,如果在香港街道數(shù)據(jù)中“閑逛”扒腕,又會發(fā)現(xiàn)什么呢?


3. 地圖數(shù)據(jù)中的問題

我將下載后的 hong-kong.osm 通過 make_a_simple.py 代碼萤悴,轉(zhuǎn)換為一個小型的 sample.osm 文件瘾腰,對數(shù)據(jù)進行審查。

3.1. key 審查

  • 使用 tags.py 審查沒有發(fā)現(xiàn)問題 key(problemchars 類型)稚疹,將 other 類型的 key 打印出來發(fā)現(xiàn)有一部分是因為有 2 個冒號居灯,除去兩個冒號的,其他有:
    • naptan:Bearing: 在 NaPTAN Wiki 中搜索到相關(guān)信息内狗,Direction along street in which vehicle is pointing when stopped at stopping point. 表示的是:車輛停在這個位置時怪嫌,車頭的朝向方位是什么。
    • ref:CBNW: 在 Key:ref Wiki 上可以查到柳沙,ref:CBNW 更通用的寫法應該是 ref:ctb岩灭,可以修正一下,不過對本次查詢沒有影響赂鲤,暫時未修改噪径;
    • socket:bs1363: 在 charging_station Wiki 是充電站正常的 key,表示的是插座的一種類型数初;
    • name:zh-yue: 應該代表的是粵語找爱,但是沒有找到對應的 Wiki,而且中文或者粵語泡孩,文字應該是沒有區(qū)別的车摄,香港都是使用的繁體字,zh-yue 似乎沒有必要仑鸥,只是在下方處理的時候需要注意吮播;
    • boundary_1: 關(guān)于邊界,沒有找到 boundary_1 相關(guān)信息眼俊,暫時忽略意狠;
    • NRG: 沒有找到相關(guān)信息,也暫時忽略疮胖。
  • 整理數(shù)據(jù)后想要探索的相關(guān) key:
    • 街道地址:addr:street
    • 生活設(shè)施:amenity
    • 餐廳:restaurant
    • 菜系:cuisine环戈,這其中發(fā)現(xiàn)一個問題是,cuisine 的值如果有多個澎灸,被用分號分開了院塞,所以需要處理一下。

3.2. 街道地址的審查

  • 因為歷史原因與地域原因击孩,香港是一個國際化的大都市,常用語言有好幾種:中文鹏漆、粵語巩梢、英文创泄、葡文等。所以地址的名稱一般情況下都是中英文結(jié)合并且是先中后英的形式括蝠;
  • 很多 name 的 key 后面都跟著幾個 name:zh, name:en 等區(qū)分語言的名稱(這里多處理一步鞠抑,如果沒有 name:zh,也要搜索一下 name:zh-yue)忌警;
  • 后續(xù)可以整理查看 name:zh, name:en 的值是否都在 name 中搁拙,如果沒有,則將 name 修改為 name:zhname:en 用空格連接的字符串
  • 通過使用 audit.py 進行審查發(fā)現(xiàn)地址的格式還是比較整齊的法绵,只修改了一個以 Roadww 結(jié)尾的地址為 Road 結(jié)尾箕速。

4. 問題數(shù)據(jù)的改進

4.1. cuisine 菜系數(shù)據(jù)的 value 處理

處理方式:

  • 在寫入 csv 文件的時候,查找 cuisine 值中包含分號的值朋譬;
  • 用分號將這個值分割為一個列表盐茎;
  • 對列表中的項進行遍歷,每一項創(chuàng)建一個新的 tag徙赢,key 為 cuisine字柠,value 為正在遍歷的項

以下代碼節(jié)選自 data.py 主要用來處理多個 cuisine 的情況

def multi_cuisine_apart(tag_dict):
    """
    將有多個菜系的 cuisine,用 ; 分開狡赐,整理為一個列表
    如果只有一個菜系窑业,則為一個元素的列表
    最終返回這個菜系列表 cuisine_list
    """
    cuisine_list=[]
    if ";" in tag_dict['value']:
        cuisine_list = tag_dict['value'].split(';')
        # print cuisine_list
    else:
        cuisine_list.append(tag_dict['value'])
    return cuisine_list

…… 在 data.pyshape_element 函數(shù)中,將原來的 tags.append(tag_dict) 改為了以下語句

                if (tag_dict['key'] == 'name') and zh_en_name:    # 如果 key 為 name枕屉,并且 zh 和 en 都不為空
                    tag_dict["value"] = zh_en_name                # 則修改 name 對應的 value
                
                if tag_dict['key'] == 'cuisine':                   # 處理 key 為 cuisine 的數(shù)據(jù)
                    cuisine_list = multi_cuisine_apart(tag_dict)
                    for cuisine in cuisine_list:
                        tag_dict_cuisine = tag_dict.copy()
                        tag_dict_cuisine["value"] = cuisine
                        tags.append(tag_dict_cuisine)
                else:
                    tags.append(tag_dict)

4.2. name 與 name:zh常柄、name:en 的結(jié)合

處理方式:

  • 遍歷 node
  • 判斷該 nodeid 中是否有 name:zh 和 name:en
  • 如果滿足以上條件則給 name key 對應的 value 賦值為:name:zh 的值 + 一個空格 + name:en 的值

以下代碼為 data.pyfind_zh_en_name 函數(shù)

def find_zh_en_name(elem):
    """
    name:zh 和 name:en 將被分別儲存在 zh 和 en 中并最終組合為 zh_en_name 返回
    同時返回值還有 has_name 用來解決有 name:zh 和 name:en 卻沒有 name key 時的情況
    """
    zh = ""
    en = ""
    zh_en_name=""
    has_name = False
    for child in elem:
        if child.tag == "tag":

            if (child.attrib['k'] == "name:zh") or (child.attrib['k'] == "name:zh-yue"):
                # 中文要處理 zh-yue 的情況
                zh = child.attrib['v']
            if child.attrib['k'] == "name:en":
                en = child.attrib['v']
            if child.attrib['k'] == "name":
                has_name = True
    if zh and en:
        zh_en_name = zh + " " + en   
    return zh_en_name, has_name

……在實現(xiàn)了上述步驟之后,debug 時發(fā)現(xiàn)有一些既有 en 又有 zh 名稱的 tag搀庶,可能沒有 name 這個 key拐纱,這種情況下,添加了以下代碼來處理這個問題哥倔,位置在 data.py 下面的 shape_element 函數(shù)中秸架,for child in element: 之前:

    zh_en_name, has_name = find_zh_en_name(element) # 調(diào)用函數(shù)查看是否有 name,zh 和 en
    if (zh_en_name) and (not has_name):
        # 如果沒有 name咆蒿,但是有 zh 和 en东抹,則創(chuàng)建一個新的 name 條目
        tag_dict_name={}
        tag_dict_name["id"] = node_id
        tag_dict_name["key"] = "name"
        tag_dict_name["value"] = zh_en_name
        tag_dict_name["type"] = default_tag_type
        tags.append(tag_dict_name)

4.3. 數(shù)據(jù)整理中遇到的問題

  • 因為對 xml 文件的寫入不熟悉,所以直接在寫入 CSV 文件的時候進行了整理沃测,所以有一個問題是缭黔,如何在 xml 文件中做出這些整理的操作,并寫入到 xml 文件中
  • 關(guān)于 name 的整理中蒂破,在實現(xiàn)了上述功能之后馏谨,瀏覽文件時發(fā)現(xiàn):有一些既有 en 又有 zh 名稱的 tag,可能沒有 name 這個 key附迷,這種情況下惧互,我的解決思路是:需要添加一個 "name" 的 key哎媚,并賦予它 zh + " " + en 這個值,并按照正常 name 一樣喊儡,type 設(shè)置為 regular拨与。這部分內(nèi)容在以上代碼中也有所體現(xiàn)。

5. 用 SQL 查詢數(shù)據(jù)

將整個 hong-kong.osm 整理為 csv 文件艾猜,并導入到 sql 中买喧,代碼文件為:

  • data.py —— 將 xml 寫入 csv 文件
  • trans_db.ipynb —— 將 csv 文件寫入數(shù)據(jù)庫

5.1. 文件大小

  • hong-kong.osm: 92.6 MB
  • mydb.db: 49.8 MB
  • nodes_csv: 31.6 MB
  • nodes_tags.csv: 3.2 MB
  • ways_csv: 2.86 MB
  • ways_nodes.csv: 11.4 MB
  • ways_tags.csv: 7.02 MB

5.2. node 數(shù)量

SELECT COUNT(*) FROM nodes;
nodes 數(shù)量查詢結(jié)果
400770

5.3. way 數(shù)量

SELECT COUNT(*) FROM ways;
ways 數(shù)量查詢結(jié)果
50217

5.4.唯一用戶數(shù)量

SELECT COUNT(DISTINCT(sq.uid)) as 'Number of unique users'           
FROM (SELECT uid FROM nodes UNION ALL SELECT uid FROM ways) as sq;
唯一用戶數(shù)量查詢結(jié)果
907

5.5. 貢獻前 10 的用戶

SELECT e.user, COUNT(*) as num
FROM (SELECT user FROM nodes UNION ALL SELECT user FROM ways) e
GROUP BY e.user
ORDER BY num DESC
LIMIT 10;
貢獻前 10 用戶查詢結(jié)果
hlaw 150311
KX675 34879
FlyTy 29769
Philip C 21372
R17466 18735
Wrightbus 18673
jc86035 13973
cartogram 12350
eversone 11475
bTonyB 8980

5.6. 排名前 10 的咖啡店

SELECT nodes_tags.value, COUNT(*) as num
FROM nodes_tags
    JOIN (SELECT DISTINCT(id) FROM nodes_tags WHERE value='cafe') i
    ON nodes_tags.id=i.id
WHERE nodes_tags.key='name'
GROUP BY nodes_tags.value
ORDER BY num DESC
LIMIT 10;
前 10 咖啡館查詢結(jié)果
星巴克咖啡 Starbucks Coffee 30
太平洋咖啡 Pacific Coffee 24
Starbucks 23
Pacific Coffee 5
Starbucks Coffee 3
Délifrance 2
Pacific Coffee Company 2
Paradise 2
新釗記 2
18 grams 1

5.7. 規(guī)模前 10 的快餐店

SELECT nodes_tags.value, COUNT(*) as num
FROM nodes_tags
    JOIN (SELECT DISTINCT(id) FROM nodes_tags WHERE value='fast_food') i
    ON nodes_tags.id=i.id
WHERE nodes_tags.key='name'
GROUP BY nodes_tags.value
ORDER BY num DESC
LIMIT 10;
快餐前 10 查詢結(jié)果
麥當勞 McDonald's 63
McDonald's 29
大家樂 Café de Coral 25
吉野家 Yoshinoya 10
肯德基 KFC 10
大快活 Fairwood 9
KFC 8
Maxim MX 5
賽百味 Subway 4
Subway 3

5.8. 最受歡迎的菜系前 10 名

SELECT nodes_tags.value, COUNT(*) as num
FROM nodes_tags 
    JOIN (SELECT DISTINCT(id) FROM nodes_tags WHERE value='restaurant') i
    ON nodes_tags.id=i.id
WHERE nodes_tags.key='cuisine'
GROUP BY nodes_tags.value
ORDER BY num DESC
Limit 10;
前 10 菜系查詢結(jié)果
chinese 73
japanese 39
noodle 25
pizza 20
indian 17
noodles 14
thai 14
american 11
sushi 11
vegetarian 11

6. 額外的想法

6.1. 更多問題與改進方法

在進行了 SQL 查詢之后,發(fā)現(xiàn)了更多問題匆赃。比如:

  1. 有些名稱有不同的版本淤毛,比如 Starbucks Coffee、Starbucks炸庞、Starbucks Reserve 都是星巴克钱床,可以只使用最有標識性的名稱,比如 Starbucks埠居,在數(shù)據(jù)整理時查牌,如果有相關(guān)的需求,也可以回過頭去獲取 name 中包含 Starbucks 的值滥壕,全部修改為一致的名稱纸颜;
  2. name 有的還是存在只有英文的情況,比如 麥當勞 McDonald's 和 McDonald's绎橘、星巴克咖啡 Starbucks Coffee 和 Starbucks Coffee胁孙,已經(jīng)做了一定的改進,比如查找到有 name:zh 和 name_en 的 node称鳞,則將 name 修改為它們的連接字符串涮较,但是很有可能出現(xiàn)沒有細分語言的 name 的情況,冈止;
  3. 甚至一些有規(guī)定樣式的狂票,也有不同的用法,比如 noodles 和 noodle熙暴,在 Key:cuisine-wiki 上可以看到應該統(tǒng)一為 noodle闺属;
  4. cuisine,這個表示的是:For describing the type of food served at an eating place.中文翻譯為:用來記錄一個餐飲場所的食物風味周霉。但是我們看到 cuisine 的值掂器,既有按地域分類的,比如 chinese俱箱、japanese国瓮,又有按照食物種類分類的 noodle、pizza,個人感覺這兩種并不是同一種分類乃摹,比如 noodle 種類厂财,有可能是中餐也有可能是日本拉面,這兩種分類是交叉重合的峡懈。

6.2. 改進后的預期問題

  1. 前 3 條問題都可以歸納為一個問題,那就是貢獻者的貢獻信息參差不齊与斤。當然可以做出各種規(guī)定性的標準肪康,但是類似 noodles 那一項也可以看出,有時候做出了規(guī)定撩穿,但是貢獻值自身如果不小心還是會提供有差異的信息磷支。如果可以像 Github 那樣,有更多的志愿者來 review 新貢獻者的貢獻食寡, review 通過后才可以被添加進去雾狈。這樣的問題就是可能沒有那么多志愿者維護。
  2. cuisine抵皱,可以添加另一個分類善榛,菜系和食物種類并不相同,可以加一個食物種類的分類呻畸,就叫 food移盆,這樣可以將菜系和食物分類區(qū)分開,但是有可能的問題是伤为,food咒循,包含的東西太多,可能貢獻者會提供并不像 noodle 和 pizza 這樣有代表性的分類性質(zhì)的詞绞愚,而是添加很多無關(guān)緊要的食物名稱叙甸;
  3. cuisine,還有一個解決辦法是位衩,在 Key:cuisine-wiki 頁面上也可以看到:“It was suggested [1] to use culture=* instead of cuisine=* for other ethnic, cultural and/or regional services like hairdresser or clothes.”這個解決辦法比上一條要好裆蒸,但是還是有一些不夠明確的地方,個人覺得蚂四,可以只將地域風格作為一個新的 key光戈,這是比較容易區(qū)分和定義的,但是在日常生活中遂赠,我們確實會將中餐和 pizza 當作并列的選項來提供久妆,如果在 OSM 中,提供了另外的分類跷睦,肯定也會引起混亂筷弦。

7. 結(jié)論

經(jīng)過本次整理,數(shù)據(jù)在一致性和完整性上有了一定的提升。雖然整理后的數(shù)據(jù)依然存在問題烂琴,但是也已經(jīng)對數(shù)據(jù)整理進行了深入的實踐和體會了爹殊。

在本次數(shù)據(jù)整理和思考的過程中,無疑可以看出 OpenStreetMap 的數(shù)據(jù)確實有很多不規(guī)范的地方奸绷,這也是開源項目不可避免會出現(xiàn)的問題梗夸。如果真的有需要使用這些數(shù)據(jù)進行數(shù)據(jù)分析工作,一定要進行很多的數(shù)據(jù)整理号醉,并且這些工作還可能會隨著分析的深入需要反復進行反症。第一次窺探到現(xiàn)實世界數(shù)據(jù)分析工作的冰山一角,很有挑戰(zhàn)性畔派!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铅碍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子线椰,更是在濱河造成了極大的恐慌胞谈,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件憨愉,死亡現(xiàn)場離奇詭異烦绳,居然都是意外死亡,警方通過查閱死者的電腦和手機配紫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門爵嗅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笨蚁,你說我怎么就攤上這事睹晒。” “怎么了括细?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵伪很,是天一觀的道長。 經(jīng)常有香客問我奋单,道長锉试,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任览濒,我火速辦了婚禮呆盖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘贷笛。我一直安慰自己应又,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布乏苦。 她就那樣靜靜地躺著株扛,像睡著了一般尤筐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洞就,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天盆繁,我揣著相機與錄音,去河邊找鬼旬蟋。 笑死油昂,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的倾贰。 我是一名探鬼主播秕狰,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼躁染!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起架忌,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤吞彤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后叹放,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饰恕,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年井仰,在試婚紗的時候發(fā)現(xiàn)自己被綠了埋嵌。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡俱恶,死狀恐怖雹嗦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情合是,我是刑警寧澤了罪,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站聪全,受9級特大地震影響泊藕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜难礼,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一娃圆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛾茉,春花似錦讼呢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春窜管,著一層夾襖步出監(jiān)牢的瞬間散劫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工幕帆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留获搏,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓失乾,卻偏偏與公主長得像常熙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碱茁,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容