Python地信专题 | 基于geopandas玩转地图可视化
文章來源于Python大數(shù)據(jù)分析,作者費弗里
本文對應(yīng)代碼和數(shù)據(jù)已上傳至我的Github倉庫https://github.com/CNFeffery/DataScienceStudyNotes[1]
已發(fā)布:
Python地信專題 | 基于geopandas的空間數(shù)據(jù)分析—數(shù)據(jù)結(jié)構(gòu)篇
Python地信專題 | 基于geopandas的空間數(shù)據(jù)分析-坐標(biāo)參考系篇
Python地信專題 | 基于geopandas的空間數(shù)據(jù)分析-文件IO篇
1 簡介
通過前面的文章,我們已經(jīng)對geopandas中的數(shù)據(jù)結(jié)構(gòu)、坐標(biāo)參考系以及文件IO有了較為深入的學(xué)習(xí)。
在拿到一份矢量數(shù)據(jù)開始分析時,對其進(jìn)行可視化無疑是探索了解數(shù)據(jù)階段重要的步驟。
作為基于geopandas的空間數(shù)據(jù)分析系列文章的第四篇,通過本文你將會學(xué)習(xí)到基于geopandas的基礎(chǔ)可視化。
比如下面這樣:
案例地圖
2 基礎(chǔ)可視化
geopandas使用matplotlib作為繪圖后端,使用plot()方法對GeoSeries或GeoDataFrame進(jìn)行可視化,簡簡單單即可完成基本的可視化。
再結(jié)合上matplotlib的一些額外元素補充,便可以創(chuàng)建出更加精美的可視化作品,下面我們分別進(jìn)行介紹。
2.1 GeoSeries
GeoSeries由于僅有單獨一列幾何對象,無對應(yīng)的數(shù)值故不涉及數(shù)值向視覺元素的映射,因此可視化相對簡單。
下面我們先來看看GeoSeries.plot()的常用的參數(shù)有哪些,如果你已經(jīng)對matplotlib有一定了解,想必理解這些參數(shù)起來會更加輕松:
figsize:傳入(寬度, 高度)形式的元組或列表,用于控制繪制出圖像的寬度和高度,單位均為英寸
facecolor:設(shè)置幾何對象的填充色,可接受顏色名稱和十六進(jìn)制色彩,設(shè)置為'none'時不填充顏色
edgecolor:設(shè)置幾何對象的邊界色,對面數(shù)據(jù)和點數(shù)據(jù)效果較為明顯,不建議對線數(shù)據(jù)設(shè)置該參數(shù),傳入格式同facecolor
linewidth:設(shè)置幾何對象邊界寬度,對面數(shù)據(jù)和點數(shù)據(jù)效果較為明顯,不建議對線數(shù)據(jù)設(shè)置該參數(shù)
linestyle:字符串類型,用于設(shè)置幾何對象邊界及線數(shù)據(jù)的線型
markersize:設(shè)置點數(shù)據(jù)的大小
marker:字符串類型,用于設(shè)置點數(shù)據(jù)的形狀
alpha:設(shè)置對應(yīng)幾何對象全局的色彩透明度,0-1,越大越不透明
label:適用于純粹的線數(shù)據(jù)或點數(shù)據(jù),在需要添加圖例時適用,用作各個對象在圖例中顯示的名稱
hatch:字符型,用于設(shè)置面數(shù)據(jù)內(nèi)部的填充線樣式下文的例子中將具體舉例說明
ax:matplotlib坐標(biāo)軸對象,如果需要在同一個坐標(biāo)軸內(nèi)疊加多個圖層就需要用這個參數(shù)傳入先前待疊加的ax
下面我們從實際例子上手,深入理解上述各參數(shù)。
我們使用到的數(shù)據(jù)china-shapefiles.zip為中國國土+南海九段線,你可以在本文開頭列出的Github倉庫對應(yīng)本文的路徑下找到它。
首先利用上一篇文章介紹的讀取.zip文件中數(shù)據(jù)的方法,將我們所需的陸地及九段線數(shù)據(jù)分別讀入:
注:其中由于原始數(shù)據(jù)china.shp中每個要素不是單獨的省份而是面,即有的包含眾多島嶼的省份會由若干行共同構(gòu)成。
因此使用geopandas地理操作中的融合dissolve()按照OWNER列融合分離的面為多面,從而使得每一行是對應(yīng)的完整的省份,關(guān)于更多地理操作將會在后續(xù)的對應(yīng)的文章介紹。
用plot()方法疊加繪制不帶任何個性化參數(shù)的原始地圖(CRS為EPSG:4326即WGS84):
# 初始化圖床 fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.plot(ax=ax) ax = nine_lines.geometry.plot(ax=ax) fig.savefig('圖1.png', dpi=300) 圖1接下來我們一步一步,將適用于GeoSeries.plot()的參數(shù)展示運用:
Step1:選擇合適的投影
在之前關(guān)于坐標(biāo)參考系的文章中我們了解過繪制地圖時投影的重要性,參考超圖對繪制中國地圖投影選用方面的建議[2],我們使用繪制中國地圖常用的Albers Equal Area作為投影,在proj[3]查詢到其信息說明:
圖2將其proj信息傳入to_crs()方法中(注意按照將添加上中央經(jīng)線105度和標(biāo)準(zhǔn)緯度范圍25到47度),統(tǒng)一到所有圖層中:
# 定義CRS albers_proj = '+proj=aea +lat_1=25 +lat_2=47 +lon_0=105'fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax) fig.savefig('圖3.png', dpi=300) 圖3這時的形狀較為接近真實情況,看起來也比較自然。
Step2:修改顏色
下面我們來調(diào)整面數(shù)據(jù)的填充色與輪廓色,線數(shù)據(jù)(九段線)的色彩。
并分別設(shè)置透明度alpha,這里為了美觀,將坐標(biāo)軸順便移除:
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',alpha=0.4) ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖4.png', dpi=300) 圖4Step3:修改線型與線寬
接下來我們在圖4的基礎(chǔ)上,修改線型和線寬。
其中線型參數(shù)linestyle與matplotlib完全一致,不同選擇對應(yīng)樣式如圖5:
圖5參考圖5,我們維持九段線線型不變但適當(dāng)增大其寬度為3,面數(shù)據(jù)的輪廓則設(shè)置為'--':
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖6.png', dpi=300) 圖6Step4:修改面填充陰影線樣式
接下來我們利用hatch參數(shù)來修改面數(shù)據(jù)填充陰影樣式。
主要樣式對應(yīng)如下,如'-'代表橫線填充:
圖7參考圖7,我們設(shè)置面數(shù)據(jù)的填充陰影樣式為'x'。
值得一提的是,hatch參數(shù)對于同一種陰影模式,可以通過增加字符數(shù)量來提高陰影密度。
如下圖是hatch='x'時:
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',hatch='x',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖8.png', dpi=300) 圖8而hatch='xxxx'時繪制出的地圖如下:
圖9更有意思的是,不同陰影模式可以混合在一起。
譬如我們下面設(shè)置hatch='x**':
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',hatch='x**',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖10.png', dpi=300) 圖10Step5:點數(shù)據(jù)個性化
GeoSeries.plot()中的markersize和marker專門針對點數(shù)據(jù)進(jìn)行配置,可是我們的數(shù)據(jù)里并沒有點數(shù)據(jù)。
為了舉例說明,下面我們來從已有的數(shù)據(jù)中生成點數(shù)據(jù),我最開始的想法是為每個面生成重心,作為每個省份的中心點:
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',hatch='xxxx',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax = china.geometry.centroid.to_crs(albers_proj).plot(ax=ax,facecolor='black') ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖11.png', dpi=300) 圖11但是細(xì)心觀察可以發(fā)現(xiàn),有些省份的重心很尷尬地落在外面,譬如甘肅省。
因為它是一個非常典型的非凸多邊形(凸多邊形內(nèi)部任意兩點間連線都不會穿過其邊界),因此計算出來的重心落在了外部。
好在geopandas為我們提供了representative_point()方法,用于求出任意多邊形內(nèi)部的一個典型點:
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',hatch='xxxx',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax = china.geometry.representative_point() \.to_crs(albers_proj) \.plot(ax=ax, facecolor='black') ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖12.png', dpi=300) 圖12這時可以發(fā)現(xiàn)生成的點符合了我們的需求,下面我們?yōu)榇嘶A(chǔ)上,利用marker調(diào)整點數(shù)據(jù)的樣式,參考圖13:
圖13譬如我們將marker修改為'*',并調(diào)整相關(guān)的其他參數(shù)使得點看起來更加明顯,
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',hatch='xxxx',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4) ax = china.geometry.representative_point() \.to_crs(albers_proj) \.plot(ax=ax, facecolor='white',edgecolor='black',marker='*',markersize=200,linewidth=0.5) ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖14.png', dpi=300) 圖14Step6:圖例與文字標(biāo)注
接下來我們來學(xué)習(xí)如何為地圖添加圖例和文字標(biāo)注。
為了看著清楚我們移除陰影填充并降低點的大小,然后為九段線與點數(shù)據(jù)添加參數(shù)label,最后使用ax.legend()添加圖例并設(shè)置相應(yīng)參數(shù):
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4,label='南海九段線') ax = china.geometry.representative_point() \.to_crs(albers_proj) \.plot(ax=ax, facecolor='white',edgecolor='black',marker='*',markersize=100,linewidth=0.5,label='省級單位') # 單獨提前設(shè)置圖例標(biāo)題大小 plt.rcParams['legend.title_fontsize'] = 14# 設(shè)置圖例標(biāo)題,位置,排列方式,是否帶有陰影 ax.legend(title="圖例", loc='lower left', ncol=1, shadow=True)ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖15.png', dpi=300) 圖15接下來我們把標(biāo)記每個省級單位的星星換成名稱文字。
這里使用到matplolib中的text()方法,其以此傳入對應(yīng)循環(huán)到的點的x、y、文字內(nèi)容,ha與va用于調(diào)整文字水平和豎直對齊方式,size調(diào)整文字大小。
更具體的參數(shù)可以去matplotlib官網(wǎng)搜索查看,本文不做重點介紹:
fig, ax = plt.subplots(figsize=(12, 8)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4,label='南海九段線')# 根據(jù)轉(zhuǎn)換過投影的代表點,循環(huán)添加文字至地圖上對應(yīng)位置 for idx, _ in enumerate(china.geometry.representative_point().to_crs(albers_proj)):# 提取省級單位簡稱if ('自' in china.loc[idx, 'OWNER'] or '特' in china.loc[idx, 'OWNER']) \and china.loc[idx, 'OWNER'] != '內(nèi)蒙古自治區(qū)':region = china.loc[idx, 'OWNER'][:2]else:region = china.loc[idx, 'OWNER'].replace('省', '') \.replace('市', '') \.replace('自治區(qū)', '')ax.text(_.x, _.y, region, ha="center", va="center", size=6)# 單獨提前設(shè)置圖例標(biāo)題大小 plt.rcParams['legend.title_fontsize'] = 14# 設(shè)置圖例標(biāo)題,位置,排列方式,是否帶有陰影 ax.legend(title="圖例", loc='lower left', ncol=1, shadow=True)ax.axis('off') # 移除坐標(biāo)軸 fig.savefig('圖16.png', dpi=300) 圖16Step7:添加小地圖
大家平時如果留意會記得,我們一般看到的中國地圖其南海區(qū)域都是單獨在右下角的小地圖里顯示出來的。
在geopandas里制作這種地圖非常簡單,我們只需要結(jié)合matplotlib中添加子圖區(qū)域的add_axes(),即可完成制作。
先來認(rèn)識一下add_axes()的功能,它最重要的參數(shù)是rect,通過傳入形如(bottom, left, width, height)來實現(xiàn)在圖床中開辟子區(qū)域。
讓我們從下面簡單的例子出發(fā)好好理解,首先我們使用plt.figure()創(chuàng)建一個方形畫布,并在畫布上使用add_axes((0, 0, 1, 1)):
圖17發(fā)現(xiàn)原理了嗎?
我們傳入的(0, 0, 1, 1),其前兩位其實代表著子圖區(qū)域左下角坐標(biāo)在整個畫布中的比例坐標(biāo)!
而后兩位則代表則代表著子圖區(qū)域的相對于整個畫布的比例寬度與長度!
接著我們再為fig開辟新的子區(qū)域,并在新開辟的子區(qū)域正中心寫上文字:
圖18新的子圖區(qū)域左下角坐標(biāo)位于畫布的底邊中點,比例長寬均為0.5,所以得到了如圖所示的效果。
搞明白了這些之后,下面我們就可以來畫帶小地圖的中國地圖啦:
首先我們需要分別對中國地圖以及南海插圖的經(jīng)緯度范圍進(jìn)行限定。
因為并沒有找到嚴(yán)格的范圍規(guī)定,所以這里我們大致定義一下中國地圖和南海插圖的最小最大經(jīng)緯度,生成GeoDataFrame并添加矢量信息,最后進(jìn)行合適的投影轉(zhuǎn)換:
接下來的步驟就一目了然了,只需要把前文繪制地圖部分的手法分別移植到兩個子圖上即可:
fig = plt.figure(figsize=(8, 8))# 創(chuàng)建覆蓋整個畫布的子圖1 ax = fig.add_axes((0, 0, 1, 1)) ax = china.geometry.to_crs(albers_proj).plot(ax=ax,facecolor='grey',edgecolor='white',linestyle='--',alpha=0.8) ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4,label='南海九段線')# 單獨提前設(shè)置圖例標(biāo)題大小 plt.rcParams['legend.title_fontsize'] = 14# 設(shè)置圖例標(biāo)題,位置,排列方式,是否帶有陰影 ax.legend(title="圖例", loc='lower left', ncol=1, shadow=True)ax.axis('off') # 移除坐標(biāo)軸 ax.set_xlim(bound.geometry[0].x, bound.geometry[1].x) ax.set_ylim(bound.geometry[0].y, bound.geometry[1].y)# 創(chuàng)建南海插圖對應(yīng)的子圖,這里的位置和大小信息是我調(diào)好的,你可以試著調(diào)節(jié)看看有什么不同 ax_child = fig.add_axes([0.75, 0.15, 0.2, 0.2]) ax_child = china.geometry.to_crs(albers_proj).plot(ax=ax_child,facecolor='grey',edgecolor='white',linestyle='--',alpha=0.8) ax_child = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax_child,edgecolor='grey',linewidth=3,alpha=0.4,label='南海九段線')ax_child.set_xlim(bound.geometry[2].x, bound.geometry[3].x) ax_child.set_ylim(bound.geometry[2].y, bound.geometry[3].y)# 移除子圖坐標(biāo)軸刻度,因為這里的子圖需要有邊框,所以只移除坐標(biāo)軸刻度 ax_child.set_xticks([]) ax_child.set_yticks([])fig.savefig('圖20.png', dpi=300) 圖202.2 GeoDataFrame
介紹完了圍繞GeoSeries展開的繪圖方法,下面我們來學(xué)習(xí)geopandas中圍繞GeoDataFrame展開的可視化方法。
與GeoSeries相比,GeoDataFrame擁有多列數(shù)據(jù),即我們可以將輔助列的數(shù)值信息映射到地圖的視覺元素上,因此在GeoSeries常用參數(shù)的基礎(chǔ)上,新增了更多參數(shù):
column:用于指定映射地圖視覺元素的數(shù)值信息,可以是對應(yīng)GeoDataFrame的列名,或是直接傳入與幾何對象一一對應(yīng)得數(shù)值序列,默認(rèn)為None
cmap:傳入映射視覺元素時的色彩方案,具體使用方式下文中會做詳細(xì)介紹
categorical:bool型,True表示指定映射目標(biāo)列采取離散表示,對于數(shù)值型的列有意義,當(dāng)對應(yīng)目標(biāo)列為類別型時自動變?yōu)門rue
legend:bool型,為True時會為地圖添加圖例
scheme:str型,用于指定地區(qū)分布圖分層設(shè)色的數(shù)值劃分方案,下文中會做詳細(xì)介紹
k:int型,用于指定分層設(shè)色的色階數(shù)量
vmin:None或float,用于指定分層設(shè)色的數(shù)值范圍下限,默認(rèn)為None即以對應(yīng)數(shù)據(jù)中的最小值為下限
vmax:None或float,用于指定分層設(shè)色的數(shù)值范圍上限,默認(rèn)為None即以對應(yīng)數(shù)據(jù)中的最大值為上限
legend_kwds:字典型,傳入與圖例相關(guān)的個性化參數(shù)
classification_kwds:字典型,傳入與分層設(shè)色相關(guān)的個性化參數(shù)
missing_kwds:字典型,傳入與缺失值處理相關(guān)的個性化參數(shù),用于對缺失值部分的視覺映射做個性化設(shè)置
同樣的,我們以實際例子出發(fā),這里我們使用新冠肺炎疫情數(shù)據(jù),數(shù)據(jù)來源[4] 。
同樣地你可以在本文開頭列出的Github倉庫中對應(yīng)本文的路徑下找到下文所使用的數(shù)據(jù)。
首先我們先對原數(shù)據(jù)做一些預(yù)處理,以得到每個省份最新一次更新記錄的數(shù)據(jù):
圖21這樣就得到我們所需的數(shù)據(jù)。
2.2.1 地區(qū)分布圖與分層設(shè)色
地區(qū)分布圖(Choropleth Map),指的是依據(jù)指定屬性進(jìn)行層次劃分,并將對應(yīng)的層次映射到對應(yīng)幾何對象的色彩之上。
下面我們先將上面處理好的表格數(shù)據(jù)與china相關(guān)聯(lián)。
因為geopandas支持pandas的連接操作,所以我們使用pd.merge()以省級單位名稱為鍵來連接兩張表:
注:由于連接之后的表格會變成pandas.DataFrame,所以這里將其轉(zhuǎn)換回GeoDataFrame。
data_with_geometry = pd.merge(left=temp.replace('澳門', '澳門特別行政區(qū)'),right=china,left_on='provinceName',right_on='OWNER',how='right').loc[:, ['provinceName','provinceEnglishName','province_confirmedCount','province_suspectedCount','province_curedCount','province_deadCount','geometry']] # 將數(shù)據(jù)從DataFrame轉(zhuǎn)換為GeoDataFrame data_with_geometry = gpd.GeoDataFrame(data_with_geometry, crs='EPSG:4326') data_with_geometry.head() 圖22有了數(shù)據(jù),我們先很“愚蠢魯莽”地直接將province_confirmedCount即地區(qū)確診數(shù)作為映射值傳入?yún)?shù)column,并選擇cmap為經(jīng)典的Reds紅色漸變配色,以及調(diào)整一些前文中我們已經(jīng)很熟悉的參數(shù)。
看看得到什么樣的結(jié)果:
圖23為什么會得到這樣奇怪的結(jié)果?讓我們逐一來分析一下問題所在:
臺灣省跑哪里去了?
細(xì)心的你一定會發(fā)現(xiàn),我們的寶島臺灣不見了,這并不是我們的幾何對象中缺失了它,一個中國一寸土地都不可缺少。
真正使得它消失的原因在于我們的原始數(shù)據(jù)中其實缺失香港和臺灣的數(shù)據(jù),我們前面連接過程使用的右連接的方法使得我們保留了所有的土地。
但是臺灣和香港由于數(shù)據(jù)缺失,對應(yīng)數(shù)據(jù)位置是NaN,因此在數(shù)值映射到色彩的過程中變成了默認(rèn)的白色,這時候missing_kwds參數(shù)就起到大用處了:
fig, ax = plt.subplots(figsize=(12, 12))# 新增缺失值處理參數(shù) ax = data_with_geometry.to_crs(albers_proj).plot(ax=ax,column='province_confirmedCount',cmap='Reds',missing_kwds={"color": "lightgrey","edgecolor": "black","hatch": ""})ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4)ax.axis('off')fig.savefig('圖24.png', dpi=300) 圖24在字典格式的missing_kwds參數(shù)中,我們用color設(shè)置了缺失值區(qū)域的底色,用edgecolor設(shè)置了缺失值區(qū)域的線條顏色。
并且用hatch設(shè)置了陰影填充樣式,這樣一來哪些地方缺失數(shù)據(jù)記錄就一目了然了。
為什么只有湖北省顏色這么深?
的確,這樣的地圖給我們的感覺就是:湖北省很嚴(yán)重,其他地方?jīng)]什么區(qū)別嘛,我們在圖24的基礎(chǔ)上加上數(shù)值-色彩參考:
fig, ax = plt.subplots(figsize=(12, 12))ax = data_with_geometry.to_crs(albers_proj).plot(ax=ax,column='province_confirmedCount',cmap='Reds',missing_kwds={"color": "lightgrey","edgecolor": "black","hatch": ""},legend=True)ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4)ax.axis('off')fig.savefig('圖25.png', dpi=300) 圖25這下我們搞清楚了,原來是因為湖北省的數(shù)據(jù)過于大,使得數(shù)值在均勻向有序色階上映射時,除湖北省之外的其他數(shù)據(jù)都被壓縮到非常淺色的區(qū)域。
這時就到了本小結(jié)的主題——分層設(shè)色,這里就涉及到相關(guān)的核心參數(shù)scheme以及k,其中scheme決定了數(shù)據(jù)分層的方法。
其通過調(diào)用第三方包mapclassify中用于給數(shù)據(jù)分層的方法),來實現(xiàn)geopandas中的分層設(shè)色。
譬如下面我們在圖25的基礎(chǔ)上,使用我們喜聞樂見的自然斷點法對應(yīng)的'NaturalBreaks'作為參數(shù),選擇分段數(shù)量k=5,來看看會有什么樣的效果:
fig, ax = plt.subplots(figsize=(12, 12))ax = data_with_geometry.to_crs(albers_proj).plot(ax=ax,column='province_confirmedCount',cmap='Reds',missing_kwds={"color": "lightgrey","edgecolor": "black","hatch": ""},legend=True,scheme='NaturalBreaks',k=5)ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4)ax.axis('off')fig.savefig('圖26.png', dpi=300) 圖26這時可以看到,區(qū)域顏色的分布更加溫和,也使得我們看出了不同地區(qū)在疫情嚴(yán)重程度上的區(qū)別。
且因為這時變成了離散的分層,所以圖例也由比色卡變?yōu)楦鼮闃?biāo)準(zhǔn)的分類圖例,但是這個圖例默認(rèn)在右上角,對地圖造成了較為明顯的遮擋。
下面我們在圖26的基礎(chǔ)上,利用參數(shù)legend_kwds,以及missing_kwds參數(shù)下的label,對其進(jìn)行美化:
fig, ax = plt.subplots(figsize=(12, 12))ax = data_with_geometry.to_crs(albers_proj).plot(ax=ax,column='province_confirmedCount',cmap='Reds',missing_kwds={"color": "lightgrey","edgecolor": "black","hatch": "","label": "缺失值"},legend=True,scheme='NaturalBreaks',k=5,legend_kwds={'loc': 'lower left','title': '確診數(shù)量分級','shadow': True})ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4)ax.axis('off')fig.savefig('圖27.png', dpi=300) 圖27至此我們的地圖已經(jīng)比最開始美觀了很多,再為其添加大標(biāo)題、小標(biāo)題和數(shù)據(jù)說明文字。
這樣一張談不上好看但還湊合的疫情地圖便制作完畢:
fig, ax = plt.subplots(figsize=(12, 12))ax = data_with_geometry.to_crs(albers_proj).plot(ax=ax,column='province_confirmedCount',cmap='Reds',missing_kwds={"color": "lightgrey","edgecolor": "black","hatch": "","label": "缺失值"},legend=True,scheme='NaturalBreaks',k=5,legend_kwds={'loc': 'lower left','title': '確診數(shù)量分級','shadow': True})ax = nine_lines.geometry.to_crs(albers_proj).plot(ax=ax,edgecolor='grey',linewidth=3,alpha=0.4)ax.axis('off') plt.suptitle('新型冠狀肺炎累計確診數(shù)量地區(qū)分布', fontsize=24) # 添加最高級別標(biāo)題 plt.title('截至2020年02月27日', fontsize=18) # 添加大標(biāo)題 plt.tight_layout(pad=4.5) # 調(diào)整不同標(biāo)題之間間距 ax.text(-2800000, 1000000, '* 原始數(shù)據(jù)來源:丁香園,\n其中臺灣及香港數(shù)據(jù)缺失') # 添加數(shù)據(jù)說明fig.savefig('圖28.png', dpi=300) 圖282.2.2 搭配matplotlib實現(xiàn)創(chuàng)作
geopandas雖然自帶了如此豐富的地圖繪制功能,但很多時候作圖僅僅靠它是不夠的,想要實現(xiàn)更加個性化的效果,需要結(jié)合matplotlib中豐富的功能。
如下圖是我隨意結(jié)合matplotlib中的若干功能實現(xiàn)的個性化可視化,疊加了較多元素,由于篇幅有限,代碼不在此放出,你可以去文章開頭的Github倉庫查看本文所有代碼,嘗試用你喜歡的顏色來對地圖調(diào)色:
圖292.2.3 在模仿中學(xué)習(xí)
成為數(shù)據(jù)可視化專家不是一件容易的事,但我們可以先從模仿其他大師的優(yōu)秀作品出發(fā)。
譬如圖30來自于Github倉庫[5] ,這個倉庫包含了眾多基于R的優(yōu)秀作品,而圖30就是其中之一,對澳洲大火造成的影響進(jìn)行可視化:
圖30而下面的圖31就是我利用geopandas對圖30的大致模仿。
其中字體部分原始的R腳本中使用ggtext實現(xiàn)方便的富文本生成,而Python中我暫時沒找到類似功能的輪子,所以這里文字部分比較簡陋:
圖31對應(yīng)的代碼如下,其中使用到的矢量數(shù)據(jù)是我搜集到的精度較高的世界地圖數(shù)據(jù):
world = gpd.read_file('world') world['SOVEREI']smoke_list = ['Denmark', 'France', 'Spain', 'Sweden', 'Norway', 'Germany', 'Finland', 'Poland', 'Italy', 'Greenland'] burnt_list = ['Latvia']fig, ax = plt.subplots(figsize=(8, 8))crs = '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs'# 繪制過煙區(qū)域 ax = world[world['SOVEREI'].isin(smoke_list)] \.to_crs(crs) \.plot(ax=ax,facecolor='#d9c09e',edgecolor='#c49c67',linewidth=0.2)# 繪制拉脫維亞 ax = world[world['SOVEREI'].isin(burnt_list)] \.to_crs(crs) \.plot(ax=ax,facecolor='#c82626',edgecolor='#9d1e1e',linewidth=0.2)# 繪制剩余國家 ax = world[-(world['SOVEREI'].isin(smoke_list) | world['SOVEREI'].isin(burnt_list))] \.to_crs(crs) \.plot(ax=ax,facecolor='lightgrey',edgecolor='grey',linewidth=0.05,alpha=0.7)ax.set_xlim([-3200000, 2300000]) ax.set_ylim([4100000, 9000000]) ax.axis('off')# 添加文字 plt.text(-3*10**6, 5.5*10**6, '''由2019/20澳洲大火所導(dǎo)致的灌木叢、森林以及公園焚毀面積比拉脫維亞國土還要大,產(chǎn)生的濃煙也已經(jīng)覆蓋了丹麥全境(包括格陵蘭島和法羅群島)島嶼)、法國、西班牙、瑞典、挪威、德國、芬蘭、波蘭和意大利''', fontdict={'color': 'black','weight': 'bold','size': 13})plt.savefig('圖31.png', dpi=500)以上就是本文的全部內(nèi)容,如有筆誤望指出,接下來的文章我將會繼續(xù)介紹更高級的地圖可視化方法,敬請期待!
參考資料
[1]
https://github.com/CNFeffery/DataScienceStudyNotes: https://github.com/CNFeffery/DataScienceStudyNotes
[2]建議: http://support.supermap.com.cn/datawarehouse/webdochelp/idesktop/features/Visualization/MapSetting/ChooseAMapProjection.htm
[3]proj: https://proj.org/operations/projections/aea.html
[4]數(shù)據(jù)來源: https://github.com/BlankerL/DXY-COVID-19-Data
[5]Github倉庫: https://github.com/Z3tt/TidyTuesday
-END-
往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機器學(xué)習(xí)在線手冊深度學(xué)習(xí)在線手冊AI基礎(chǔ)下載(pdf更新到25集)本站qq群1003271085,加入本站微信群請回復(fù)“加群”獲取一折本站知識星球優(yōu)惠券,請回復(fù)“知識星球”喜歡文章,點個在看
總結(jié)
以上是生活随笔為你收集整理的Python地信专题 | 基于geopandas玩转地图可视化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何打造一支高效的AI团队
- 下一篇: 【小技巧】深度学习中的那些效率提升利器(