本文示例代码、数据及文件已上传至个人
Github
仓库https://github.com/CNFeffery/DataScienceStudyNotescss
1 简介
在上一篇文章中咱们详细学习了geoplot
中较为基础的三种绘图API:pointplot()
、polyplot()
以及webmap()
,而本文将会承接上文的内容,对geoplot
中较为实用的几种高级绘图API进行介绍。html

本文是基于geopandas的空间数据分析系列文章的第7篇,经过本文你将学习geoplot
中的高级绘图API。git
2 geoplot进阶
上一篇文章中的pointplot()
、polyplot
以及webmap()
帮助咱们解决了在绘制散点、基础面以及添加在线地图底图的问题,为了制做出信息量更丰富的可视化做品,咱们须要更强的操纵矢量数据与映射值的能力,geoplot
为咱们封装好了几种常见的高级可视化API。github
2.1 Choropleth
Choropleth图又称做地区分布图或面量图,咱们在系列以前的深刻浅出分层设色篇中介绍过其原理及geopandas
实现,能够经过将指标值映射到面数据上,以实现对指标值地区分布的可视化。web
在geoplot
中咱们能够经过choropleth()
来快速绘制地区分布图,其主要参数以下:json
df:传入对应的
GeoDataFrame
对象apiprojection:用于指定投影坐标系,传入
geoplot.crs
中的对象idehue:传入对应df中指定列名或外部序列数据,用于映射面的颜色,默认为None即不进行设色学习
cmap:和
matplotlib
中的cmap使用方式一致,用于控制色彩映射方案字体alpha:控制全局色彩透明度
scheme:做用相似
geopandas
中的scheme参数,用于控制分层设色,详见本系列文章的分层设色篇,但不一样的是在geoplot
0.4.0版本以后此参数再也不搭配分层数量k共同使用,而是更新为传入mapclassify
分段结果对象,下文中会作具体演示legend:bool型,用于控制是否显示图例
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
hatch:控制填充阴影纹路,详情见本系列文章前做基础可视化篇图7
edgecolor:控制多边形轮廓颜色
linewidth:控制多边形轮廓线型
下面咱们经过实际的例子来学习geoplot.choropleth
的用法,这里使用到的数据为美国新型冠状肺炎各州病例数分布,对应日期为2020年5月14日,来自Github
仓库:https://github.com/CSSEGISandData/COVID-19/tree/master/csse_covid_19_data/csse_covid_19_daily_reports_us;使用到的美国本土各州矢量面数据contiguous-usa.geojson
已上传到文章开头对应的Github
仓库中:


首先咱们将两张表中各自对应的州名数据做为键进行链接(注意pd.merge
返回的结果类型为DataFrame
,须要转换回GeoDataFrame
):
# 按照州名列进行链接 usa_plot_base = pd.merge(left=contiguous_usa, right=usa_covid19_20200513, left_on='state', right_on='Province_State') # 转换DataFrame到GeoDataFrame,注意要加上crs信息 usa_plot_base = gpd.GeoDataFrame(usa_plot_base, crs='EPSG:4326')
接下来咱们将确诊数做为映射值,由于美国各州中纽约州和新泽西州确诊数量分别达到了34万和14万,远远超过其余州,因此这里做为单独的图层进行阴影填充以突出其严重程度:
# 图层1:除最严重两州以外的其余州 ax = gplt.choropleth(df=usa_plot_base.query("state not in ['New York', 'New Jersey']"), projection=gcrs.AlbersEqualArea(), hue='Confirmed', scheme=mc.FisherJenks(usa_plot_base.query("state not in ['New York', 'New Jersey']")['Confirmed'], k=3), cmap='Reds', alpha=0.8, edgecolor='lightgrey', linewidth=0.2, figsize=(8, 8) ) # 图层2:纽约州 ax = gplt.polyplot(df=usa_plot_base.query("state == 'New York'"), hatch='/////', edgecolor='black', ax=ax) # 图层3:新泽西州 ax = gplt.polyplot(df=usa_plot_base.query("state == 'New Jersey'"), hatch='/////', edgecolor='red', extent=usa_plot_base.total_bounds, ax=ax) # 实例化cmap方案 cmap = plt.get_cmap('Reds') # 获得mapclassify中BoxPlot的数据分层点 bp = mc.FisherJenks(usa_plot_base.query("state not in ['New York', 'New Jersey']")['Confirmed'], k=3) bins = [0] + bp.bins.tolist() # 制做图例映射对象列表,这里分配Greys方案到三种色彩时对应的是[0, 0.5, 1]这三个采样点 LegendElement = [mpatches.Patch(facecolor=cmap(_ / 2), label=f'{int(bins[_])}-{int(bins[_+1])}') for _ in range(3)] + \ [mpatches.Patch(facecolor='none', edgecolor='black', linewidth=0.2, hatch='/////', label='New York: {}'.format(usa_plot_base.query("state == \"New York\"").Confirmed.to_list()[0])), mpatches.Patch(facecolor='none', edgecolor='red', linewidth=0.2, hatch='/////', label='New Jersey: {}'.format(usa_plot_base.query("state == \"New Jersey\"").Confirmed.to_list()[0]))] # 将制做好的图例映射对象列表导入legend()中,并配置相关参数 ax.legend(handles = LegendElement, loc='lower left', fontsize=8, title='确诊数量', title_fontsize=10, borderpad=0.6) # 添加标题 plt.title('美国新冠肺炎各州病例数(截至2020.05.14)', fontsize=18) # 保存图像 plt.savefig('图4.png', dpi=300, pad_inches=0, bbox_inches='tight')

这样咱们就获得了图4,须要注意的是,geoplot.choropleth()
只能绘制地区分布图,传入面数据后hue
参数必须指定对应映射列,不然会报错,所以这里咱们叠加纽约州和新泽西州单独面图层时使用的是polyplot()
。
2.2 Kdeplot
geoplot
中的kdeplot()
对应核密度图,其基于seaborn
中的kdeplot()
,经过对矢量点数据分布计算核密度估计,从而对点数据进行可视化,可用来展现点数据的空间分布状况,其主要参数以下:
df:传入对应的存放点数据的
GeoDataFrame
对象projection:用于指定投影坐标系,传入
geoplot.crs
中的对象cmap:和
matplotlib
中的cmap使用方式一致,用于控制色彩映射方案clip:
GeoSeries
型,用于为初始生成的核密度图像进行蒙版裁切,下文会举例说明extent:元组型,用于传入左下角和右上角经纬度信息来设置地图空间范围,格式为
(min_longitude, min_latitude, max_longitude, max_latitude)
figsize:元组型,用于控制画幅大小,格式为
(x, y)
ax:
matplotlib
坐标轴对象,若是须要在同一个坐标轴内叠加多个图层就须要用这个参数传入先前待叠加的ax
shade:bool型,当设置为False时只有等值线被绘制出,当设置为True时会绘制核密度填充
shade_lowest:bool型,控制是否对几率密度最低的层次进行填充,下文会举例说明
n_levels:int型,控制等值线数量,即按照几率密度对空间进行均匀划分的数量
下面咱们回到上一篇文章开头的例子——纽约车祸记录数据,在其余参数均为默认的状况下,调用kdeplot
对车祸记录点数据的空间分布进行可视化:
# 图层1:行政边界 ax = gplt.polyplot(df=nyc_boroughs, projection=gcrs.AlbersEqualArea()) # 图层2:默认参数下的kdeplot ax = gplt.kdeplot(df=nyc_collision_factors, cmap='Reds', ax=ax) # 保存图像 plt.savefig('图5.png', dpi=300, pad_inches=0, bbox_inches='tight')

能够看到,在kdeplot()
主要参数均为默认值的状况下,咱们获得了点数据空间分布的几率估计结果及其等高线,譬如图中比较明显能看到的两个点分布较为密集的中心,下面咱们调整n_levles
参数到比较大的数字:
# 图层1:行政边界 ax = gplt.polyplot(df=nyc_boroughs, projection=gcrs.AlbersEqualArea()) # 图层2:kdeplot ax = gplt.kdeplot(df=nyc_collision_factors, cmap='Reds', n_levels=30, ax=ax, figsize=(8, 8)) # 保存图像 plt.savefig('图6.png', dpi=300, pad_inches=0, bbox_inches='tight')

能够看到在增大n_levels
参数后,图中等值线的数量随之增长,下面咱们设置shade=True
:
# 图层1:行政边界 ax = gplt.polyplot(df=nyc_boroughs, projection=gcrs.AlbersEqualArea()) # 图层2:kdeplot ax = gplt.kdeplot(df=nyc_collision_factors, cmap='Reds', shade=True, ax=ax, figsize=(8, 8)) # 保存图像 plt.savefig('图7.png', dpi=300, pad_inches=0, bbox_inches='tight')

这时图像等值线间获得相应颜色的填充,使得点分布中心看起来更加明显,再添加参数shade_lowest=True
,便可对空白区域进行填充:

随之而来的问题是整幅图像都被填充,为了裁切出核密度图像的地区轮廓,将底层行政区面数据做为clip
的参数传入,便获得理想的效果:

2.3 Sankey
桑基图专门用于表现不一样对象之间某个指标量的流动状况,譬如最多见的航线流向状况,其本质是对线数据进行可视化,并将指标值映射到线的色彩或粗细水平上,而geoplot
中的sankey()
能够用来绘制这种图,尴尬的是sankey()
绘制出的OD流向图实在太丑,但sankey()
中将数值映射到线数据色彩和粗细的特性能够用来进行与流量相关的可视化,其主要参数以下:
df:传入对应的
GeoDataFrame
对象projection:用于指定投影坐标系,传入
geoplot.crs
中的对象hue:传入对应df中指定列名或外部序列数据,用于映射线的颜色,默认为None即不进行设色
cmap:和
matplotlib
中的cmap使用方式一致,用于控制色彩映射方案alpha:控制全局色彩透明度
scheme:做用相似
geopandas
中的scheme参数,用于控制分层设色,详见本系列文章的分层设色篇,但不一样的是在geoplot
0.4.0版本以后此参数再也不搭配分层数量k共同使用,而是更新为传入mapclassify
分段结果对象,下文中会作具体演示scale:用于设定映射线要素粗细程度的序列数据,格式同hue,默认为None即每条线等粗
linewidth:当不对线宽进行映射时,该参数用于控制线宽
legend:bool型,用于控制是否显示图例
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
下面咱们以2015年华盛顿街道路网日平均交通流量数据为例,其中每一个要素均为线要素,aadt
表明日均流量:

咱们将其流量列映射到线的粗细程度和颜色上来,为了美观起见咱们选择系列文章分层设色篇中palettable
的SunsetDark
做为配色方案:
# 选择配色方案为SunsetDark_5 from palettable.cartocolors.sequential import SunsetDark_5 gplt.sankey( dc_roads, projection=gcrs.AlbersEqualArea(), scale='aadt', hue='aadt', limits=(0.1, 2), # 控制线宽范围 scheme=mc.NaturalBreaks(dc_roads['aadt']), cmap=SunsetDark_5.mpl_colormap, figsize=(8, 8), extent=dc_roads.total_bounds ) plt.savefig("图11.png", dpi=500, pad_inches=0, bbox_inches='tight')

2.4 geoplot中的坐标参考系
geoplot
中的坐标参考系与geopandas
中管理起来的方式大相径庭,由于geopandas
基于pyproj
管理坐标参考系,而geoplot
中的crs
子模块来源于cartopy
,这一点我跟geoplot
的主要开发者聊过,他表示geoplot
暂时不支持geopandas
中那样自定义任意投影或使用EPSG
投影,而是内置了一系列经常使用的投影,譬如咱们上文中绘制美国区域时频繁使用到的AlbersEqualArea()
即以前咱们在geopandas
中经过proj4自定义的阿尔伯斯等面积投影,其余常见投影譬如Web Mercator、Robinson,或者直接绘制球体地图,如本文开头的图1就来自官方示例(https://residentmario.github.io/geoplot/gallery/plot_los_angeles_flights.html#sphx-glr-gallery-plot-los-angeles-flights-py),关于geoplot
坐标参考系的细节比较简单本文很少赘述,感兴趣的读者能够前往官网(https://residentmario.github.io/geoplot/api_reference.html#projections)查看。
2.5 在模仿中学习
又到了最喜欢的“复刻”环节啦,本文要模仿的地图可视化做品来自https://github.com/Z3tt/30DayMapChallenge/tree/master/contributions/Day26_Hydrology,一样是用R
语言实现,对全球主要河流的形态进行优雅地可视化:

针对其河流宽度方面的可视化,咱们基于上文中的sankey()
来实现,因为原图中南极洲区域其实是夸大了的,其R
源码中设置的纬度范围达到了-110度,这是原做者为了放得下标题内容,因此在图像下部区域虚构了一篇区域,而geoplot
中的extent
参数严格要求经度必须在-180到180度之间,纬度在-90到90度之间。所以在原图的基础上咱们进行微调,将标题移动到居中位置,具体代码以下:
from palettable.cartocolors.sequential import Teal_7_r import matplotlib.font_manager as fm from shapely.geometry import box # 读入世界主要河流线数据 world_river = gpd.read_file('geometry/world_rivers_dSe.geojson') # 读入世界海洋面数据 world_ocean = gpd.read_file('geometry/world_ocean.shp') # 图层1:世界范围背景色,基于shapely.geometry中的bbox来生成矩形矢量 ax = gplt.polyplot(df=gpd.GeoDataFrame({'geometry': [box(-180, -90, 180, 90)]}), facecolor='#000026', edgecolor='#000026') # 图层2:世界海洋面图层 ax = gplt.polyplot(world_ocean, facecolor='#00003a', edgecolor='#00003a', ax=ax) # 图层3:世界主要河流线图层 ax = gplt.sankey(world_river, scale='StrokeWeig', hue='StrokeWeig', scheme=mc.Quantiles(world_river['StrokeWeig'], 7), cmap=Teal_7_r.mpl_colormap, limits=(0.05, 0.4), figsize=(8, 8), extent=(-180, -90, 180, 90), ax=ax) # 添加标题 ax.text(0, 0, 'The Rivers of the World', fontproperties=fm.FontProperties(fname="AlexBrush-Regular.ttf"), # 传入Alex Brush手写字体文件 fontsize=28, color=Teal_7_r.mpl_colors[-1], horizontalalignment='center', verticalalignment='center') # 添加做者信息及数据来源 ax.text(0, -15, 'Visualization by CNFeffery - Data by Natural Earth', fontproperties=fm.FontProperties(fname="AlexBrush-Regular.ttf"), fontsize=8, color='#599bae', horizontalalignment='center', verticalalignment='center') plt.savefig('图13.png', dpi=600, pad_inches=0, bbox_inches='tight')

以上就是本文的所有内容,我将在下一篇文章中继续与你们一块儿探讨学习geoplot
中更高级的绘图API。若有疑问和意见,欢迎留言或在个人Github
仓库中发起issues与我交流。