R 技術文 — 區議會選區分界變動及人口地圖

R 技術文 — 區議會選區分界變動及人口地圖EricBlockedUnblockFollowFollowingMay 3¾ 技術文 — 用 R 製作區議會選區資料地圖 (上)以 R 製作互動式網頁地圖顯示香港區議會選區分界 (附完整原始碼)medium.

com上文用簡單例子講點用 R 製作區議會選區地圖,相信唔少人(點解會咁天真覺得有唔少人睇呢啲技術文) 到喉唔到肺,今日深入少少,首先講吓用 leaflet 繪製地圖嘅常用進階選項,再講點解讀 PDF 檔同埋將人口數據加返落各個選區。(完整即食原始碼放喺各章節底,已合併前文相關代碼,唔駛前抄後抄咁辛苦)A.

區議會選區分界變遷上文提到點用 leaflet 繪製一年嘅 DCCA 分界地圖,而依家我地有齊 2003 年至今各屆選區分界數據,想進一步比較唔同選舉年嘅分界,又可以點做呢?如果有玩開 Photoshop 之類東西,相信對圖層 (Layer) 呢個概念唔會陌生,而喺 leaflet 一樣用到類似概念。簡而言之,可以睇成將印有不同內容嘅透明膠片疊起來,而你就要決定哪些放頂,哪些放底,咁有啲內容就會遮住見唔到,而喺 leaflet ,會用 zIndex 去表達放置不同 layers 嘅先後次序,數值越大就越接近表面,說明文件建議數值應介乎 400 與 500 之間。建立設置不同 zIndex 的 MapPane 後,將不同年度的選區分界按次序加至相應的 MapPane,然後再設定外觀(如顏色、透明度、邊框粗幼等等)。當然少不了為選區加上名稱標籤,以及與鼠標互動的反白功能,不然定必會給四百多個選區弄得一頭霧水。最後就加上 LayersControl,供用家決定顯示哪些年度的分界,方便比較選區分界歷年變遷 (例如究竟會否有些疑似 gerrymandering 的小動作呢?)比較 2019、2015 年選區分界完整原始碼 (Part A)B.

區議會選區人口偏離標準人口基數幅度地圖標題水蛇春咁長,1999 咁,睇完都唔知講乜,唔緊要,唔明講到你明為止。現行選區標準人口基數為 16,599人,法例規定選區人口應盡量接近基數,並且唔應該偏離基數超過 25%,故此政府會不時重新劃分甚或增刪選區,但實際上不少選區嘅偏離幅度礙於諸多因素遠超 25%,而 2019 年區議會選區預計人口可參閱選管會文件 (按此)。究竟邊區偏離基數多啲?除左睇足 18 頁紙,可唔可以一張地圖睇晒呢?B1.

處理 PDF 檔及清理數據一打開,又係討厭嘅 PDF 檔,所以今日會順道講吓點處理 PDF 檔。傳說要成為檸檬廚神有十大要訣,例如一般人單手做嘅,就反手做,而一般人用工具做嘅,就用返手做。雖然網上有唔少快靚正轉換 PDF 做 Excel 檔嘅工具,為咗向檸檬致敬,小弟就自討苦吃拎 R 嘅 tabulizer package 嚟做示範點處理 PDF 檔 (完全費時失事,仲要人手後期執)。拆解 PDF 檔其實只得一行 code:# READ THE PDF FILE AND EXTRACT THE TABLES# https://www.

eac.

hk/pdf/distco/2019dc/final/ch/Appendix_VI(Chi).

pdfout_mat = extract_tables("Appendix_VI(Chi).

pdf", method="stream", encoding='UTF-8')然後就會抽到 18 區各區嘅表格出嚟,但真係三尖八角、岩岩巉巉,個個 table 嘅欄數都唔同,點搞呀?(話咗你用網上啲工具咪好囉,攞苦嚟辛)若然仔細檢查每個表格,會發現頭兩個 columns 同最尾兩個 columns 有用,那就先把呢啲有用 columns 抽出嚟。先用 regular expression (i.

e.

Column 1 有 valid DCCA 編號, e.

g.

A01) 選取有用嘅 rows ,之後再將 Column 2 (中英文名) 分開返做中文、英文名兩個 columns,而 Column 3 人口數字就要移除非數字符號,然後移除埋 Column 4 嘅百分比符號,最後再將 18 個 tables 合埋做一個 dataframe 就接近大功告成了。## CREATE AN EMPTY DATA FRAMEout_df = data.

frame()## READ EACH OF THE 18 TABLESfor (k in 1:18){ ## EXTRACT THE RELEVANT COLUMNS (NOTE: NUMBER OF COLUMNS VARIES) out_temp = out_mat[[k]][,c(1,2,ncol(out_mat[[k]])-1,ncol(out_mat[[k]]))] ## USE REGULAR EXPRESSION TO EXTRACT THE RELEVANT ROWS (WITH VALID DCCA ID) out_temp = out_temp[grep("Ddd", out_temp[,1], perl=TRUE),] ## SPLIT THE NAME INTO 2 COLUMNS (CHINESE NAME AND ENGLISH NAME) out_temp = cbind(out_temp, str_match(as.

character(out_temp[,2]),"([^s]+)s+(.

+)")[,c(2:3)]) ## CONVERT TO A DATAFRAME out_temp = as.

data.

frame(out_temp, stringsAsFactors=FALSE) ## CONVERT THE POPULATION TO INTEGER (REMOVE NON-NUMERIC CHAR) out_temp[,3] = as.

integer(gsub("[^0-9]","",out_temp[,3])) ## CONVERT THE PERCENTAGE TO NUMERIC TYPE out_temp[,4] = as.

numeric(as.

numeric(sub("%","",out_temp[,4]))) ## COMBINE THE DATAFRAME WITH THE CONSOLIDATED ONE out_df = rbind(out_df, out_temp)}## EXPORT THE CONSOLIDATED DATAFRAME TO AN EXCEL FILE## CLEAN THE FILE IN THE EXCEL## EDIT DCCAs E17, F25 (>1 ROW IN THE NAME COLUMN)write.

xlsx(out_df, "out.

xlsx", row.

names=FALSE)## IMPORT THE CLEANED CONSOLIDATED EXCEL FILEimp_df = read.

xlsx("out.

xlsx", sheetIndex = 1, encoding="UTF-8")## RENAME THE COLUMNScolnames(imp_df) = c("CACODE","NAME","Pop","Deviation","CNAME","ENAME")## DROP UNNECESSARY COLUMNSimp_df['NAME'] <- NULLimp_df['ENAME'] <- NULL點解係接近大功告成?因為本身 PDF 檔有兩個 DCCAs 名稱太長以致佔咗兩行,結果解讀時出現錯誤,由於懶得 hard code,直接匯出至 Excel 檔再人手改返啱佢就算,嗰兩個 DCCAs 分別係 E17 同 F25。匯入檔案後再加返 Column 名,移除埋唔要嘅 Columns 就叫做整理好呢堆數據了。(再次提大家,真係上網 convert 算啦)B2.

載入 Shapefile 以及將人口數據併入之後就係大家熟口熟面嘅步驟,將 2019 年區議會選區 Shapefile 匯入 (下刪一百字……睇返前文吧)###################################################################### IMPORT THE SHAPEFILEDC2019 = readOGR(dsn='DCCA_2019.

shp')## SET THE CRS (COORDINATE REF SYSTEM) TO HK1980 GRID SYSTEM (EPSG:2326)## https://spatialreference.

org/ref/epsg/2326/projection(DC2019) = crs("+init=epsg:2326")## TRANSFORMATION INTO WGS 84 SYSTEM (EPSG:4326)## https://spatialreference.

org/ref/epsg/4326/DC2019 = spTransform(DC2019,"+init=epsg:4326")## REMOVE "CNAME", THE SAME AS THE "ENAME" IN THE 2019 DCCA FILEDC2019@data$CNAME <- NULL大家可以望吓 DC2019 呢舊野嘅構造,分做@ data 同埋 @ polygons,前者載住啲名呀、編號,後者就係載住分界座標,下一步就係將頭先千辛萬苦清理好嘅人口數據加入去 @ data 那個 dataframe。left_join() 又出現了 (可參見另文 Section 3), 以 “CACODE” 作為 Key 就可以將兩個 dataframes 合體。然後再定義 palette function (參見另文 Section 4),根據輸入數值 map 返色塊顏色。## ADD THE ADDITIONAL DATA TO THE DC2019 SpatialPolygonsDF USING LEFT_JOINDC2019@data = left_join(DC2019@data, imp_df,by=c("CACODE"="CACODE"))## CREATE A PALETTE FUNCTIONpal <- colorNumeric( palette = "PiYG", domain = DC2019@data$Deviation, reverse = TRUE)B3.

用 leaflet 繪製地圖最後當然係要將所有野顯示喺地圖上面,同前文比,呢段 code 長咗好多,逐舊慢慢解釋:i) Basemap 選項呢度介紹埋如果想俾用家自己選擇不同底圖,其實可以事先放唔同 provider 嘅參數去 call addProviderTiles(),之後再加上 addLayersControl() 即可。用 OpenStreetMap 做底圖ii) 依偏離基數人口嘅幅度填色,再加上標籤等等資料標籤最麻煩應該係標籤,因為要分行以及加上格式,所以要用到 HTML 語法 (e.

g.

<b>, <br>),paste() 會將括號內的東西加上空白 (或在 sep 選項另外指定)串埋,例如 paste(“Apple”, “Pen”) 會變成 “Apple Pen”,而 paste0() 就唔會加間隔字符,例如 paste0(“Apple”, “Pen”) 就會變成 “ApplePen”,最重要係要指明呢堆野係 HTML 碼,而非普通嘅 String。之後加埋 highlightOptions(),鼠標滑到選區之上就會反白整個選區,再彈出剛才加上嘅資料標籤。最後可以喺 RStudio 將地圖匯出成 Webpage,方便大家同其他人分享。一理通百理明,搞掂呢兩篇文,應該已經可以玩到好多人口普查嘅分區數據,如果你有其他 shapefiles,其實步驟都係類似嘅啫。完整原始碼 (Part B)資料來源: 香港政府為原始資料的版權擁有人,使用條款請瀏覽 data.

gov.

hk最後更新:2019.

05.

04.. More details

Leave a Reply