數模必備:利用ggplot2在地圖上連線(不使用for迴圈)
最近由於自己的時間關係,很久沒有發一些乾貨了。這次想談談之前我們在進行數學建模時,如何將設計好的最優路徑,利用ggplot2
在地圖上進行繪製與展示。
最簡單的方法是使用plot
繪圖,然後再使用lines
函式一條一條地將線加上去,但是一條一條加上去的過程中,需要使用for迴圈不說,而且繪製出來的圖相對也沒那麼美膩。
下面我們講一講使用ggplot2
且不使用for迴圈,如何快速便捷地完成這樣的操作。
1. 前文回顧
首先可以回顧一下,在沒有梯子的前提下,我們是如何利用ggplot2
繪製一個較為好看的中國地圖:利用R繪製漂亮的中國地圖(無需通過google獲取)。
下面我們基於前面繪製的中國地圖,根據計算(最小生成樹演算法,之後有時間在說說這個,不過目前網上說的很詳細了,而且也有很多程式碼)得到的相應連線貼到我們的圖上。
背景影象
2. 利用ggplot2連線多個點
我們需要選定其中幾個點,並將其進行連線。下面是我們最終想要達到的效果(之前的圖使用Mac畫的,下面這個是Windows,可能質感有些不一樣,望大家理解):
為了達到這樣的效果,我們主要是需要將資料集整理成可以能夠舒適繪製的方式。利用ggplot2
繪圖,整理資料集要佔80%以上的工作。
1) 現有資料
我們的最終目的是連33條線,所以先將需要連的線均一組一組地羅列而出(每個city都對應著一組經緯度,相應地是地圖上的一個點)。
(前面的最終效果圖是前11組城市連線)
下面的資料我們命名為:city_pair
city1 city2
1 北京&天津 上海
2 上海 廣州&深圳
3 廣州&深圳 重慶
4 重慶 成都
5 重慶 西安
6 北京&天津 哈爾濱
7 北京&天津 武漢
8 武漢 鄭州
9 重慶 昆明
10 北京&天津 烏魯木齊
11 北京&天津 拉薩
12 鄭州 西安
13 武漢 重慶
14 北京&天津 鄭州
15 北京&天津 西安
16 鄭州 重慶
17 北京&天津 重慶
18 武漢 廣州&深圳
19 上海 武漢
20 上海 鄭州
21 北京&天津 廣州&深圳
22 上海 重慶
23 昆明 廣州&深圳
24 武漢 成都
25 鄭州 成都
26 西安 成都
27 北京&天津 成都
28 成都 昆明
29 西安 武漢
30 成都 廣州&深圳
31 上海 成都
32 哈爾濱 重慶
33 哈爾濱 廣州&深圳
所以我們還需要對應城市經緯度及人口資訊,這裡的物件為mat.cities
,它長這樣:(這部分的資料生成也在我們前面的部落格中有提及)
names lat long population
1 北京&天津 39.90420 116.40740 32.5506
2 上海 31.23039 121.47370 23.0191
3 鄭州 34.74725 113.62493 8.6265
4 烏魯木齊 43.82660 87.61684 3.1103
5 哈爾濱 45.80218 126.53582 10.6360
6 西安 34.34126 108.93982 8.4678
7 武漢 30.59276 114.30524 9.7854
8 成都 30.57022 104.06477 14.0476
9 拉薩 29.64411 91.11445 0.5594
10 重慶 29.56470 106.55071 28.8462
11 昆明 24.87966 102.83321 6.4320
12 廣州&深圳 23.02067 113.75178 23.0587
有了這樣兩份原始資料,我們就可以開始進行繪圖了。
首先是需要將資料整理成能夠進行繪圖的,為了方便操作,我們直接對原本的33組城市對進行操作。下面是資料預處理的過程。(由於當時數模時間有限,而資料量也不是很大,所以下面採用了大量的for迴圈,希望大家在用R時還是儘可能多用向量化操作,少用for迴圈)
2) 資料預處理
預處理程式碼如下:
dat_plot = matrix(nrow = 66, ncol = 4)
k = 0
for (i in 1:33) {
for (j in 1:2) {
k = k + 1
my.row = mat.cities[city_pair[i, j] == mat.cities$names, ]
dat_plot[k, 1] = unlist(my.row[1])
dat_plot[k, 2] = unlist(my.row[2])
dat_plot[k, 3] = unlist(my.row[3])
dat_plot[k, 4] = i
}
}
colnames(dat_plot) = c('地區', 'lat', 'long', 'group')
dat_plot = as.data.frame(dat_plot)
dat_plot$lat = as.numeric(as.character(dat_plot$lat))
dat_plot$long = as.numeric(as.character(dat_plot$long))
這裡我們的主要思路是:將配對的一組城市變為拆分成兩組,然後再在最後新增一個group
變數,主要是用於連線(兩個城市如果在一個相同的group
中,使用ggplot
繪圖中的引數group
即可將兩個點連線起來)。
在生成完想要的資料集後,記得將經緯度調整為數值型,group
直接為factor
即可。
最後我們得到的資料dat_plot
長下面這樣:
地區 lat long group
1 北京&天津 39.90420 116.40740 1
2 上海 31.23039 121.47370 1
3 上海 31.23039 121.47370 2
4 廣州&深圳 23.02067 113.75178 2
5 廣州&深圳 23.02067 113.75178 3
6 重慶 29.56470 106.55071 3
7 重慶 29.56470 106.55071 4
8 成都 30.57022 104.06477 4
9 重慶 29.56470 106.55071 5
10 西安 34.34126 108.93982 5
11 北京&天津 39.90420 116.40740 6
12 哈爾濱 45.80218 126.53582 6
13 北京&天津 39.90420 116.40740 7
14 武漢 30.59276 114.30524 7
15 武漢 30.59276 114.30524 8
16 鄭州 34.74725 113.62493 8
17 重慶 29.56470 106.55071 9
18 昆明 24.87966 102.83321 9
19 北京&天津 39.90420 116.40740 10
20 烏魯木齊 43.82660 87.61684 10
21 北京&天津 39.90420 116.40740 11
22 拉薩 29.64411 91.11445 11
23 鄭州 34.74725 113.62493 12
24 西安 34.34126 108.93982 12
25 武漢 30.59276 114.30524 13
26 重慶 29.56470 106.55071 13
27 北京&天津 39.90420 116.40740 14
28 鄭州 34.74725 113.62493 14
29 北京&天津 39.90420 116.40740 15
30 西安 34.34126 108.93982 15
31 鄭州 34.74725 113.62493 16
32 重慶 29.56470 106.55071 16
33 北京&天津 39.90420 116.40740 17
34 重慶 29.56470 106.55071 17
35 武漢 30.59276 114.30524 18
36 廣州&深圳 23.02067 113.75178 18
37 上海 31.23039 121.47370 19
38 武漢 30.59276 114.30524 19
39 上海 31.23039 121.47370 20
40 鄭州 34.74725 113.62493 20
41 北京&天津 39.90420 116.40740 21
42 廣州&深圳 23.02067 113.75178 21
43 上海 31.23039 121.47370 22
44 重慶 29.56470 106.55071 22
45 昆明 24.87966 102.83321 23
46 廣州&深圳 23.02067 113.75178 23
47 武漢 30.59276 114.30524 24
48 成都 30.57022 104.06477 24
49 鄭州 34.74725 113.62493 25
50 成都 30.57022 104.06477 25
51 西安 34.34126 108.93982 26
52 成都 30.57022 104.06477 26
53 北京&天津 39.90420 116.40740 27
54 成都 30.57022 104.06477 27
55 成都 30.57022 104.06477 28
56 昆明 24.87966 102.83321 28
57 西安 34.34126 108.93982 29
58 武漢 30.59276 114.30524 29
59 成都 30.57022 104.06477 30
60 廣州&深圳 23.02067 113.75178 30
61 上海 31.23039 121.47370 31
62 成都 30.57022 104.06477 31
63 哈爾濱 45.80218 126.53582 32
64 重慶 29.56470 106.55071 32
65 哈爾濱 45.80218 126.53582 33
66 廣州&深圳 23.02067 113.75178 33
3) 繪圖
下面我們的核心繪圖程式碼如下,想要連線不同的線,我們只是變了資料中選取的行,如:dat_plot[1:22, ]
。
## 11線
ggplot() +
geom_path(data = china, aes(long, lat, group = group), color = '#FD9FA4', show.legend = F) +
geom_point(data = mat.cities, aes(x = long, y = lat, size = population), alpha = 0.8, color = '#8BB6D6') +
geom_line(data = dat_plot[1:22, ], aes(long, lat, group = group), size = 1, alpha = 0.8, color = '#8BB6D6') +
geom_text_repel(data = mat.cities, aes(x = long, y = lat, label = names), family = "STHeiti") +
labs(x = '經度', y = '緯度', title = '十一條連線', size = '人口(百萬)') +
theme_bw() +
theme(panel.border = element_blank(),
text = element_text(family = "STHeiti"),
plot.title = element_text(hjust = 0.5))
## 16線
ggplot() +
geom_path(data = china, aes(long, lat, group = group), color = '#FD9FA4', show.legend = F) +
geom_point(data = mat.cities, aes(x = long, y = lat, size = population), alpha = 0.8, color = '#8BB6D6') +
geom_line(data = dat_plot[1:32, ], aes(long, lat, group = group), size = 1, alpha = 0.8, color = '#8BB6D6') +
geom_text_repel(data = mat.cities, aes(x = long, y = lat, label = names), family = "STHeiti") +
labs(x = '經度', y = '緯度', title = '十六條連線', size = '人口(百萬)') +
theme_bw() +
theme(panel.border = element_blank(),
text = element_text(family = "STHeiti"),
plot.title = element_text(hjust = 0.5))
## 33線
ggplot() +
geom_path(data = china, aes(long, lat, group = group), color = '#FD9FA4', show.legend = F) +
geom_point(data = mat.cities, aes(x = long, y = lat, size = population), alpha = 0.8, color = '#8BB6D6') +
geom_line(data = dat_plot[1:66, ], aes(long, lat, group = group), size = 1, alpha = 0.8, color = '#8BB6D6') +
geom_text_repel(data = mat.cities, aes(x = long, y = lat, label = names), family = "STHeiti") +
labs(x = '經度', y = '緯度', title = '三十三條連線', size = '人口(百萬)') +
theme_bw() +
theme(panel.border = element_blank(),
text = element_text(family = "STHeiti"),
plot.title = element_text(hjust = 0.5))
繪圖過程沒有什麼好說的了,裡面使用的函式與方法都在前面的部落格中提及過:ggplot2:初次見面,請多多關照!,唯一新增的連線所使用的函式:geom_line
,裡面只需注意多了一個引數group
,記得新增即可。
4) 結果展示
最後的16條連線與33條連線的效果圖分別如下所示: