(數據科學學習手札82)基於geopandas的空間數據分析——geoplot篇(上)
本文示例代碼和數據已上傳至我的
Github
倉庫//github.com/CNFeffery/DataScienceStudyNotes
1 簡介
在前面的基於geopandas的空間數據分析系列文章中,我們已經對geopandas
的基礎知識、基礎可視化,以及如何科學繪製分層設色地圖展開了深入的學習,而利用geopandas
+matplotlib
進行地理可視化固然能實現常見的地圖可視化,且提供了操縱圖像的極高自由度,但對使用者matplotlib
的熟悉程度要求較高,製作一幅地圖可視化作品往往需要編寫較多的代碼,而geoplot
基於geopandas
,提供了眾多高度封裝的繪圖API,很大程度上簡化了繪圖難度,就像seaborn
之於matplotlib
。

本文是基於geopandas的空間數據分析系列文章的第6篇,通過本文你將學習geoplot
中的基礎繪圖API。
2 geoplot基礎
推薦使用conda install --channel conda-forge geoplot
來安裝geoplot
,可以避免很多惱人的依賴包問題。首先我們從一個簡單的例子來初探一下geoplot
的基礎使用流程:
2.1 從一個簡單的例子出發
我們下面所使用到的數據:nyc-boroughs.geojson
,記錄了紐約的行政區域面文件:
import geopandas as gpd
%matplotlib inline
# 讀入紐約行政區域面文件
nyc_boroughs = gpd.read_file('geometry/nyc-boroughs.geojson')
nyc_boroughs.head()

以及nyc-collision-factors.geojson
,包含了紐約所發生的車禍記錄點以及相關信息數據:
# 讀入紐約車禍記錄點文件
nyc_collision_factors = gpd.read_file('geometry/nyc-collision-factors.geojson')
nyc_collision_factors.head()

首先我們使用geoplot
中的polyplot
來繪製紐約行政區劃,這裡使用geoplot
自帶的Albers等面積投影作為投影:
import geoplot as gplt
import geoplot.crs as gcrs
import matplotlib.pyplot as plt
ax = gplt.polyplot(df=nyc_boroughs,
projection=gcrs.AlbersEqualArea())
plt.savefig("圖4.png", bbox_inches='tight', pad_inches=0, dpi=300)

接着我們使用geoplot
中的pointplot
將點疊加到圖4上:
ax = gplt.polyplot(df=nyc_boroughs,
projection=gcrs.AlbersEqualArea())
ax = gplt.pointplot(df=nyc_collision_factors,
s=2,
color='grey',
alpha=0.2,
linewidth=0, # 設置輪廓粗細為0
ax=ax)
plt.savefig("圖5.png", bbox_inches='tight', pad_inches=0, dpi=300)

為了讓車禍密集的區域更突出,我們將點圖層換成核密度圖層:
ax = gplt.polyplot(df=nyc_boroughs,
projection=gcrs.AlbersEqualArea())
# 疊加核密度圖層
ax = gplt.kdeplot(df=nyc_collision_factors,
cmap='Reds',
shade=True,
shade_lowest=True,
clip=nyc_boroughs,
ax=ax)
plt.savefig("圖6.png", bbox_inches='tight', pad_inches=0, dpi=300)

從這個簡單的例子中我們可以大致了解到,geoplot
在geopandas
處理好的數據基礎上,針對不同類型圖層封裝了各自不同的API,由用戶自主傳入對應類型的矢量數據進行圖層疊加,以得到最終結果,且可以兼容matplotlib
,譬如上面我們最終使用plt.savaefig()
對圖片進行保存,下面我們就來詳細學習geoplot
的基礎知識。
2.2 geoplot繪圖API
在geoplot
中內置了功能豐富的繪圖API,只需要傳入GeoDataFrame
格式的矢量數據即可進行繪圖(但切記geoplot中傳入的數據必須為WGS84地理坐標系,所有的投影轉換在geoplot各繪圖函數內部傳參實現即可!)
2.2.1 Pointplot
geoplot
中的pointplot
即為散點圖,其針對點數據進行可視化,其主要參數如下:
df:傳入對應的
GeoDataFrame
對象projection:用於指定投影坐標系,傳入
geoplot.crs
中的對象hue:當需要根據df中的某列或外部的其他序列數據來映射散點的色彩時,可傳入對應df中指定列名或外部序列數據,默認為None即不進行設色
cmap:和
matplotlib
中的cmap使用方式一致,用於控制色彩映射方案scheme:作用類似
geopandas
中的scheme參數,用於控制分層設色,詳見本系列文章的分層設色篇,但不同的是在geoplot
0.4.0版本之後此參數不再搭配分層數量k共同使用,而是更新為傳入mapclassify
分段結果對象,下文中會做具體演示scale:用於設定映射散點大小的序列數據,格式同hue,默認為None即每個散點等大小
limits:元組型,當scale不為None時,用於設定散點大小尺寸範圍,格式為
(min, max)
s:當scale設置為None時,用於控制散點的尺寸大小
color:當hue設置為None時,用於控制散點的填充色彩
marker:用於設定散點的形狀
alpha:控制全局色彩透明度
linewidths:控制散點輪廓寬度
edgecolors:控制散點輪廓顏色
legend:bool型,用於控制是否顯示圖例
legend_var:傳入
'hue'
或scale
,當設定為hue
時圖例顯示色彩映射信息,當設定為'scale'
時圖例顯示大小映射信息legend_values:list型,用於自定義圖例顯示的各個具體數值
legend_labels:list型,用於自定義圖例顯示的各個具體數值對應的文字標籤,與legend_values搭配使用
legend_kwargs:字典,在legend參數設置為True時來傳入更多微調圖例屬性的參數
extent:元組型,用於傳入左下角和右上角經緯度信息來設置地圖空間範圍,格式為
(min_longitude, min_latitude, max_longitude, max_latitude)
figsize:元組型,用於控制畫幅大小,格式為
(x, y)
ax:
matplotlib
坐標軸對象,如果需要在同一個坐標軸內疊加多個圖層就需要用這個參數傳入先前待疊加的ax
知曉了上述主要參數之後,下面我們通過實際案例來學習修改各個參數得到的效果,使用到的數據為波士頓區劃面數據以及波士頓部分地區Airbnb房源點數據:

-
普通散點分佈
首先我們來簡單繪製房源分佈散點圖,對大小、色彩、透明度等基礎屬性進行簡單調整:
# 簡單繪製波士頓行政區劃
ax = gplt.polyplot(df=boston_zip_codes,
projection=gcrs.AlbersEqualArea(),
edgecolor='lightgrey',
linewidths=0.5)
gplt.pointplot(df=boston_airbnb_listings,
ax=ax, # 疊加圖層
s=1,
linewidths=0.1,
color='grey',
alpha=0.4)
plt.savefig("圖8.png", bbox_inches='tight', pad_inches=0, dpi=300)

通過這樣一張簡單的圖我們是看不出太多信息的,只能大致看出哪些地方房源分佈較多。
- 映射房源價格到色彩上
將房源價格列作為色彩映射列,使用mapclassify
中的分位數法將價格區間等分成五段,並使用其他的視覺參數和自定義圖例參數:
import mapclassify as mc
#解決中文顯示問題
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默認字體
plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題
# 簡單繪製波士頓行政區劃
ax = gplt.polyplot(df=boston_zip_codes,
projection=gcrs.AlbersEqualArea(),
edgecolor='lightgrey',
linewidths=0.5)
scheme = mc.Quantiles(boston_airbnb_listings['price'], k=5)
gplt.pointplot(df=boston_airbnb_listings,
ax=ax, # 疊加圖層
s=1, # 散點大小
linewidths=0.1, # 散點輪廓寬度
hue='price', # 以price作為色彩映射列
cmap='Reds', # 色彩方案為Reds
scheme=scheme, # 傳入mapclassify對象
legend=True, # 開啟圖例
legend_kwargs={
'loc': 'lower right', # 圖例位置
'title': '價格區間', # 圖例標題
'title_fontsize': 8, # 圖例標題字體大小
'fontsize': 6, # 圖例非標題外字體大小
'shadow': True, # 添加圖例陰影
},
legend_labels=['80%-100%價格房源',
'60%-80%價格房源',
'40%-60%價格房源',
'20%-40%價格房源',
'前20%價格房源'])
plt.savefig("圖9.png", bbox_inches='tight', pad_inches=0, dpi=300)

- 映射房源價格到尺寸上
看完了如何映射顏色,下面我們來看看如何將值映射到散點大小上,使用scale='price'
來將房源價格映射到散點大小上,再配合一些相關參數進行繪圖:
import numpy as np
# 簡單繪製波士頓行政區劃
ax = gplt.polyplot(df=boston_zip_codes,
projection=gcrs.AlbersEqualArea(),
edgecolor='lightgrey',
linewidths=0.5)
ax = gplt.pointplot(df=boston_airbnb_listings,
ax=ax, # 疊加圖層
linewidths=0.2, # 散點輪廓寬度
scale='price', # 以price作為色彩映射列
color=np.array([0., 0., 0., 0.]), # 設置填充色為透明
edgecolor='grey', # 設置輪廓顏色
limits=(1, 16), # 設置散點的尺寸範圍
legend=True, # 開啟圖例
legend_kwargs={
'loc': 'lower right', # 圖例位置
'title': '價格區間', # 圖例標題
'title_fontsize': 8, # 圖例標題字體大小
'fontsize': 6, # 圖例非標題外字體大小
'shadow': True, # 添加圖例陰影
'markeredgecolor': 'grey', # 圖例標記的輪廓色彩
'markeredgewidth': 0.2 # 圖例標記的輪廓粗細
})
plt.savefig("圖10.png", bbox_inches='tight', pad_inches=0, dpi=300)

現在我們可以一眼看出那些半徑較大的圓圈對應着價格較高的房源,值得注意的是在我們映射值到散點大小上時,默認條件下會自動在圖例中按照等間距法分出5段,這樣得到的圖例各個圓圈大小過渡保證了均勻,當然你也可以自由地通過legend_values和legeng_labels這兩個參數自定義圖例內容。
- 同時映射顏色與尺寸
geoplot
允許用戶同時映射色彩和尺寸,但同一張圖中的圖例只能顯示色彩或尺寸其中之一的信息,使用legend_var參數來選擇讓哪一種映射信息顯示在圖例上:
# 簡單繪製波士頓行政區劃
ax = gplt.polyplot(df=boston_zip_codes,
projection=gcrs.AlbersEqualArea(),
edgecolor='lightgrey',
linewidths=0.5)
scheme = mc.Quantiles(boston_airbnb_listings['price'], k=5)
gplt.pointplot(df=boston_airbnb_listings,
ax=ax, # 疊加圖層
scale='price', # 以price作為尺寸映射列
limits=(1, 16), # 設置散點的尺寸範圍
alpha=0.6, # 設置散點透明度
linewidths=0.1, # 散點輪廓寬度
hue='price', # 以price作為色彩映射列
cmap='Reds', # 色彩方案為Reds
scheme=scheme, # 傳入mapclassify對象
legend=True, # 開啟圖例
legend_kwargs={
'loc': 'lower right', # 圖例位置
'title': '價格區間', # 圖例標題
'title_fontsize': 8, # 圖例標題字體大小
'fontsize': 6, # 圖例非標題外字體大小
'shadow': True, # 添加圖例陰影
},
legend_labels=['80%-100%價格房源',
'60%-80%價格房源',
'40%-60%價格房源',
'20%-40%價格房源',
'前20%價格房源'])
plt.savefig("圖11.png", bbox_inches='tight', pad_inches=0, dpi=300)

2.2.2 Polyplot
geoplot
中的polyplot
用於繪製基礎的面數據,並不像pointplot
那樣帶有值映射功能,其主要參數如下:
df:傳入對應的
GeoDataFrame
對象projection:用於指定投影坐標系,傳入
geoplot.crs
中的對象extent:元組型,用於傳入左下角和右上角經緯度信息來設置地圖空間範圍,格式為
(min_longitude, min_latitude, max_longitude, max_latitude)
figsize:元組型,用於控制畫幅大小,格式為
(x, y)
ax:
matplotlib
坐標軸對象,如果需要在同一個坐標軸內疊加多個圖層就需要用這個參數傳入先前待疊加的ax
alpha:控制全局色彩透明度
linewidths:控制線寬度
edgecolors:控制線顏色
facecolor:控制填充顏色
linestyle:控制線樣式,詳情見本系列文章前作基礎可視化篇圖5
hatch:控制填充陰影紋路,詳情見本系列文章前作基礎可視化篇圖7
下面我們就對紐約區劃面數據進行舉例說明:
gplt.polyplot(df=nyc_boroughs,
projection=gcrs.AlbersEqualArea(),
figsize=(10, 10),
linewidths=0.5,
linestyle='-.',
edgecolors='grey',
facecolor='#d9c09e',
hatch='--')
plt.savefig("圖12.png", bbox_inches='tight', pad_inches=0, dpi=300)

2.2.3 Webmap
geoplot
中的webmap
用來添加在線瓦片地圖底圖,使得我們可以在在線地圖上圖層,但目前暫時只支持疊加基於點要素的圖層。值得注意的是,因為常見在線地圖如谷歌地圖、OpenStreetMap、高德地圖等的投影均為EPSG:3857也就是我們常說的Web Mercator,所以一旦要使用webmap
,則投影鎖死為EPSG:3857,其主要參數如下:
df:傳入對應的
GeoDataFrame
對象extent:元組型,用於傳入左下角和右上角經緯度信息來設置地圖空間範圍,格式為
(min_longitude, min_latitude, max_longitude, max_latitude)
figsize:元組型,用於控制畫幅大小,格式為
(x, y)
ax:
matplotlib
坐標軸對象,如果需要在同一個坐標軸內疊加多個圖層就需要用這個參數傳入先前待疊加的ax
zoom:int型,控制在線地圖底圖的縮放級別,越大越清楚,同時獲取瓦片地圖資源從而渲染地圖所耗費的時間也越多,上限由具體所使用的在線地圖所決定,通常情況最大縮放級別為18
provider:str型,用於指定在線地圖底圖的類型,下面會舉例說明
下面我們將紐約車禍點數據疊加到在線地圖上,這裡我們選擇provider
參數為ST_TERRAIN_LINES,並設置縮放級別為11級:
ax = gplt.webmap(df=nyc_boroughs,
provider='ST_TERRAIN_LINES',
zoom=10)
ax = gplt.pointplot(df=nyc_collision_factors,
color='lightyellow',
edgecolor='yellow',
alpha=0.4,
s=1,
ax=ax)
plt.savefig("圖13.png", bbox_inches='tight', pad_inches=0, dpi=300)

如果想要切換底圖樣式,可以修改provider
參數的輸入,目前為止所有可用的地圖如下圖所示:

2.3 在模仿中學習
在本系列文章基礎可視化篇的最後我們對數據可視化專家用R
繪製的澳大利亞火災影響地圖進行了模仿,從而加深對geopandas
數據可視化的融會貫通,而本文作為geoplot
篇的上半篇,介紹了geoplot
中最基本的幾種數據可視化API,使得我們足以完成較為基礎的數據可視化作品,而同樣為了加深對上文所介紹知識的理解掌握,接下來我們再次對其他優秀的數據可視化作品進行模仿。
這次我們要模仿的作品來自Github
倉庫//github.com/Z3tt/30DayMapChallenge
,是利用R
進行地理空間數據可視化的一個集錦倉庫,要用geoplot
來模仿復現的作品如圖15所示,展示了柏林所有電動汽車充電樁的分佈情況:

我們主要浮現的是圖15中柏林地圖以及內部元素部分,使用到的數據在我的Github
倉庫對應本文路徑下的Berlin
文件夾中,其中ladesaeulen_bnetza_und_be_emobil.xlsx
記錄了EPSG:25833投影坐標系格式下的充電樁經緯度點信息,gis_osm_roads_free_1.shp
記錄了柏林市OSM路網信息,Bezirke__Berlin.shp
記錄了柏林行政區劃信息。
在分析了原圖的R
代碼之後,我們將整幅圖拆解分為四個圖層,1是柏林最邊緣的灰色輪廓,這其實是整個柏林區域面數據向外生成緩衝區之後的效果;2是柏林各行政區區劃,3是柏林內部的部分OSM路網,構成了圖中依稀可見的類似紋路的要素,4是所有的充電樁點數據,即圖中黃色的半透明散點,其中除路網線數據可視化以外的其他圖層我們均使用geoplot
來實現。
數據預處理部分分步驟代碼較多,不便在文章中全盤放出,你可以到文章開頭的Github
倉庫中對應路徑下查看和下載,下面只貼出繪圖部分的代碼以方便理解思想:
# 繪製最底層柏林緩衝區
ax = gplt.polyplot(df=gpd.GeoSeries([GeometryCollection(berlin_area.geometry.tolist())], crs='EPSG:4326') \
.buffer(0.005, resolution=100),
projection=gcrs.WebMercator(),
facecolor='grey',
edgecolor='None',
alpha=0.6)
# 繪製柏林區劃
ax = gplt.polyplot(df=berlin_area,
facecolor='black',
edgecolor='white',
alpha=0.65,
ax=ax)
# 利用geopandas繪製內部OSM路網
ax = intersect_roads.plot(ax=ax,
linewidth=0.1,
edgecolor='black',
alpha=0.25)
# 繪製充電樁散點
ax = gplt.pointplot(df=df_emobil,
ax=ax,
extent=berlin_area.total_bounds,
color='#edc00d',
alpha=0.3,
linewidth=0.2,
s=4.5)
# 繪製充電樁中心點
ax = gplt.pointplot(df=df_emobil,
ax=ax,
extent=berlin_area.buffer(0.01).total_bounds,
color='#d29c14',
edgecolor='#6f603e',
linewidth=0.01,
s=0.7)
plt.savefig("圖16.png", bbox_inches='tight', pad_inches=0, dpi=600)
最終得到的效果如圖16所示:

以上就是本文的全部內容,我將在下一篇文章中繼續與大家一起探討學習geoplot
中更高級的繪圖API。如有疑問和意見,歡迎留言或在我的Github
倉庫中發起issues與我交流。