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:zh
與name: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.py
的 shape_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.py
的 find_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)了更多問題匆赃。比如:
- 有些名稱有不同的版本淤毛,比如 Starbucks Coffee、Starbucks炸庞、Starbucks Reserve 都是星巴克钱床,可以只使用最有標識性的名稱,比如 Starbucks埠居,在數(shù)據(jù)整理時查牌,如果有相關(guān)的需求,也可以回過頭去獲取 name 中包含 Starbucks 的值滥壕,全部修改為一致的名稱纸颜;
- name 有的還是存在只有英文的情況,比如 麥當勞 McDonald's 和 McDonald's绎橘、星巴克咖啡 Starbucks Coffee 和 Starbucks Coffee胁孙,已經(jīng)做了一定的改進,比如查找到有 name:zh 和 name_en 的 node称鳞,則將 name 修改為它們的連接字符串涮较,但是很有可能出現(xiàn)沒有細分語言的 name 的情況,冈止;
- 甚至一些有規(guī)定樣式的狂票,也有不同的用法,比如 noodles 和 noodle熙暴,在 Key:cuisine-wiki 上可以看到應該統(tǒng)一為 noodle闺属;
- cuisine,這個表示的是:For describing the type of food served at an eating place.中文翻譯為:用來記錄一個餐飲場所的食物風味周霉。但是我們看到 cuisine 的值掂器,既有按地域分類的,比如 chinese俱箱、japanese国瓮,又有按照食物種類分類的 noodle、pizza,個人感覺這兩種并不是同一種分類乃摹,比如 noodle 種類厂财,有可能是中餐也有可能是日本拉面,這兩種分類是交叉重合的峡懈。
6.2. 改進后的預期問題
- 前 3 條問題都可以歸納為一個問題,那就是貢獻者的貢獻信息參差不齊与斤。當然可以做出各種規(guī)定性的標準肪康,但是類似 noodles 那一項也可以看出,有時候做出了規(guī)定撩穿,但是貢獻值自身如果不小心還是會提供有差異的信息磷支。如果可以像 Github 那樣,有更多的志愿者來 review 新貢獻者的貢獻食寡, review 通過后才可以被添加進去雾狈。這樣的問題就是可能沒有那么多志愿者維護。
- cuisine抵皱,可以添加另一個分類善榛,菜系和食物種類并不相同,可以加一個食物種類的分類呻畸,就叫 food移盆,這樣可以將菜系和食物分類區(qū)分開,但是有可能的問題是伤为,food咒循,包含的東西太多,可能貢獻者會提供并不像 noodle 和 pizza 這樣有代表性的分類性質(zhì)的詞绞愚,而是添加很多無關(guān)緊要的食物名稱叙甸;
- 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)性畔派!