2.1 分层设色基本介绍
如下代码所示,绘制江苏省地级市GDP地图。
# 读取2019江苏省各市GDP数据
import geopandas as gpd
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams["font.family"] = 'FZSongYi-Z13S'
# 数据来自互联网
gdp = pd.read_csv("2022江苏省各市GDP.csv")
gdp
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
排行 | 地级市 | 2022年GDP(亿元) | |
---|---|---|---|
0 | 1 | 苏州市 | 23958.3 |
1 | 2 | 南京市 | 16907.9 |
2 | 3 | 无锡市 | 14850.8 |
3 | 4 | 南通市 | 11379.6 |
4 | 5 | 常州市 | 9550.1 |
5 | 6 | 徐州市 | 8457.8 |
6 | 7 | 盐城市 | 7079.8 |
7 | 8 | 扬州市 | 6696.4 |
8 | 9 | 泰州市 | 6401.8 |
9 | 10 | 镇江市 | 5017.0 |
10 | 11 | 淮安市 | 4742.4 |
11 | 12 | 宿迁市 | 4112.0 |
12 | 13 | 连云港市 | 4005.0 |
# 读取江苏地图数据,数据来自DataV.GeoAtlas,将其投影到EPSG:4573
data = gpd.read_file('https://geo.datav.aliyun.com/areas_v3/bound/320000_full.json').to_crs('EPSG:4573')
# 合并数据
data = data.join(gdp.set_index('地级市')["2022年GDP(亿元)"],on='name')
# 修改列名
data.rename(columns={'2022年GDP(亿元)':'GDP'},inplace=True)
data.head()
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
adcode | name | childrenNum | level | parent | subFeatureIndex | geometry | GDP | |
---|---|---|---|---|---|---|---|---|
0 | 320100 | 南京市 | 11 | city | {‘adcode’: 320000} | 0 | MULTIPOLYGON (((19828216.260 3681802.361, 1982… | 16907.9 |
1 | 320200 | 无锡市 | 7 | city | {‘adcode’: 320000} | 1 | MULTIPOLYGON (((19892555.472 3541293.638, 1989… | 14850.8 |
2 | 320300 | 徐州市 | 10 | city | {‘adcode’: 320000} | 2 | MULTIPOLYGON (((19736418.457 3894748.096, 1973… | 8457.8 |
3 | 320400 | 常州市 | 6 | city | {‘adcode’: 320000} | 3 | MULTIPOLYGON (((19927182.917 3638819.801, 1992… | 9550.1 |
4 | 320500 | 苏州市 | 9 | city | {‘adcode’: 320000} | 4 | MULTIPOLYGON (((19929872.531 3547724.116, 1993… | 23958.3 |
fig, ax = plt.subplots(figsize=(9, 9))
# legend_kwds设置matplotlib的legend参数
data.plot(ax=ax,column='GDP', cmap='coolwarm', legend=True,legend_kwds={'label': "GDP(亿元)", 'shrink':0.5})
ax.axis('off')
# 设置
fontdict = {'family':'FZSongYi-Z13S', 'size':8, 'color': "black",'weight': 'bold'}
# 设置标题
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
x = data.iloc[index].geometry.centroid.x
y = data.iloc[index].geometry.centroid.y
name = data.iloc[index]["name"]
if name in ["苏州市","无锡市"]:
x = x*1.001
ax.text(x, y, name, ha="center", va="center", fontdict=fontdict)
# 保存图片
fig.savefig('res.png', dpi=300, bbox_inches='tight')
可以看到在上述地图中,由于苏州市的数值太大,其他数据被压缩到浅色区域,无法有效展示数据分布。需要使用地图分层设色来更好地展示数据。地图分层设色是一种常见的地图可视化方式,它可以将地图上的数据按照不同的分类进行分层,并对每一层数据进行不同的颜色设定,以便更加直观地展现地理空间数据的分布情况和特征。
在本文通过Python模块mapclassify用于分层设色和数据可视化。使用mapclassify之前需要输入以下命令安装相关模块:
pip install mapclassify
mapclassify官方仓库见:mapclassify。mapclassify提供了多种分组方法,可以帮助我们更好地理解数据的分布情况,mapclassify提供的方法包括:
-
BoxPlot
: 基于箱线图的分类方法。这种分类方法适用于数据分布比较规律的情况。 -
EqualInterval
: 等距离分类方法。这种分类方法将数据划分为等距离的若干区间。适用于数据分布比较均匀的情况。 -
FisherJenks
: 基于Fisher-Jenks算法的分类方法。这种分类方法将数据划分为若干区间,使得每个区间内部的差异最小,不同区间之间的差异最大。适用于数据分布比较不规律的情况。 -
HeadTailBreaks
: 基于Head-Tail算法的分类方法。这种分类方法将给定的数据集分为两部分:头部和尾部。头部通常包含出现频率最高的值,而尾部包含出现频率较低的值。适用于识别数据集中的异常值和离群值。 -
JenksCaspall
: 基于Jenks-Caspall算法的分类方法。这种分类方法根据数据中发现的自然分组将数据集划分为类。适用于需要将数据分类为几个具有明显含义的区间的情况。 -
JenksCaspallForced
: 强制基于Jenks-Caspall算法的分类方法。与JenksCaspall算法类似,但是它对区间的数量和大小有更强的控制力。适用于需要精确控制区间数量和大小的情况。 -
JenksCaspallSampled
: 采样基于Jenks-Caspall算法的分类方法。该方法对数据进行采样,然后使用Jenks-Caspall算法对采样后的数据进行分类,适用于数据量比较大的情况。 -
MaxP
: 基于最大界限的分类方法。这种分类方法将数据划分为几个区间,使得不同区间之间的差异最大。适用于需要将数据分类为几个具有明显差异的区间的情况。 -
MaximumBreaks
: 基于最大间隔的分类方法。这种分类方法与MaxP算法类似,但是它更加注重区间的可理解性。适用于需要将数据分类为几个具有明显含义的区间的情况。 -
NaturalBreaks
: 基于自然间隔的分类方法。这种分类方法将数据划分为几个区间,使得每个区间内部的差异最小,不同区间之间的差异最大。适用于数据分布比较不规律的情况 -
Quantiles
: 基于分位数的分类方法。 -
Percentiles
: 基于百分位数的分类方法。 -
StdMean
: 基于标准差分组的分类方法。 -
UserDefined
: 基于自定义分组的分类方法。
关于以上常用方法的具体介绍可以看看基于geopandas的空间数据分析——深入浅出分层设色。mapclassify对数据进行分类简单使用方法如下:
示例1
import mapclassify
# 导入示例数据
y = mapclassify.load_example()
print(type(y))
print(y.mean(),y.min(), y.max())
y.head()
<class 'pandas.core.series.Series'>
125.92810344827588 0.13 4111.45
0 329.92
1 0.42
2 5.90
3 14.03
4 2.78
Name: emp/sq km, dtype: float64
mapclassify.EqualInterval(y)
EqualInterval
Interval Count
--------------------------
[ 0.13, 822.39] | 57
( 822.39, 1644.66] | 0
(1644.66, 2466.92] | 0
(2466.92, 3289.19] | 0
(3289.19, 4111.45] | 1
示例2
y = [1,2,3,4,5,6,7,8,9,0]
# 分为四个区间
mapclassify.JenksCaspall(y, k=4)
JenksCaspall
Interval Count
--------------------
[0.00, 2.00] | 3
(2.00, 4.00] | 2
(4.00, 6.00] | 2
(6.00, 9.00] | 3
示例3
y = [1,2,3,4,5,6,7,8,9,0]
# 自定义区间
mapclassify.UserDefined(y, bins=[5, 8, 9])
UserDefined
Interval Count
--------------------
[0.00, 5.00] | 6
(5.00, 8.00] | 3
(8.00, 9.00] | 1
示例4
import mapclassify
import pandas
from numpy import linspace as lsp
demo = [lsp(3,8,num=10), lsp(10, 0, num=10), lsp(-5, 15, num=10)]
demo = pandas.DataFrame(demo).T
demo.head()
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
0 | 1 | 2 | |
---|---|---|---|
0 | 3.000000 | 10.000000 | -5.000000 |
1 | 3.555556 | 8.888889 | -2.777778 |
2 | 4.111111 | 7.777778 | -0.555556 |
3 | 4.666667 | 6.666667 | 1.666667 |
4 | 5.222222 | 5.555556 | 3.888889 |
# 使用apply函数应用分层,rolling表示是否进行滑动窗口计算以消除随机波动
demo.apply(mapclassify.Quantiles.make(rolling=True)).head()
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
0 | 1 | 2 | |
---|---|---|---|
0 | 0 | 4 | 0 |
1 | 0 | 4 | 0 |
2 | 1 | 4 | 0 |
3 | 1 | 3 | 0 |
4 | 2 | 2 | 1 |
2.2 绘图实例之用于地图的分层设色
方法1
GeoPandas中分层设色可以通过plot函数中的scheme参数和k参数设置数据分层方式和分层类别数。如下所示,通过JenksCaspall将GDP数据分为4级,可以直观看到GDP数据在第一梯队的城市有哪些。
fig, ax = plt.subplots(figsize=(9, 9))
# 使用分层设色后,legend_kwds要进行相应修改
data.plot(ax=ax,column='GDP', cmap='coolwarm', legend=True,scheme='JenksCaspall',k=4,legend_kwds={
'loc': 'lower left',
'title': 'GDP数据分级(亿元)',
})
ax.axis('off')
# 设置
fontdict = {'family':'FZSongYi-Z13S', 'size':8, 'color': "black",'weight': 'bold'}
# 设置标题
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
x = data.iloc[index].geometry.centroid.x
y = data.iloc[index].geometry.centroid.y
name = data.iloc[index]["name"]
if name in ["苏州市","无锡市"]:
x = x*1.001
ax.text(x, y, name, ha="center", va="center", fontdict=fontdict)
# 保存图片
fig.savefig('res.png', dpi=300, bbox_inches='tight')
方法2
在GeoPandas中也可以通过mapclassify直接处理数据,生成新的数据列进行展示。通过该种方式可以看到,苏州和南京的GDP领先于其他地级市。
# 创建分类器
classifier = mapclassify.HeadTailBreaks(data['GDP'])
classifier
HeadTailBreaks
Interval Count
----------------------------
[ 4005.00, 9473.76] | 8
( 9473.76, 15329.34] | 3
(15329.34, 20433.10] | 1
(20433.10, 23958.30] | 1
# 赋值数据
data['GDP_class'] = data['GDP'].apply(classifier)
data['GDP_class'] = data['GDP_class'].apply(lambda x : int(x))
data.head()
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
adcode | name | childrenNum | level | parent | subFeatureIndex | geometry | GDP | GDP_class | |
---|---|---|---|---|---|---|---|---|---|
0 | 320100 | 南京市 | 11 | city | {‘adcode’: 320000} | 0 | MULTIPOLYGON (((19828216.260 3681802.361, 1982… | 16907.9 | 2 |
1 | 320200 | 无锡市 | 7 | city | {‘adcode’: 320000} | 1 | MULTIPOLYGON (((19892555.472 3541293.638, 1989… | 14850.8 | 1 |
2 | 320300 | 徐州市 | 10 | city | {‘adcode’: 320000} | 2 | MULTIPOLYGON (((19736418.457 3894748.096, 1973… | 8457.8 | 0 |
3 | 320400 | 常州市 | 6 | city | {‘adcode’: 320000} | 3 | MULTIPOLYGON (((19927182.917 3638819.801, 1992… | 9550.1 | 1 |
4 | 320500 | 苏州市 | 9 | city | {‘adcode’: 320000} | 4 | MULTIPOLYGON (((19929872.531 3547724.116, 1993… | 23958.3 | 3 |
import matplotlib.colors as colors
fig, ax = plt.subplots(figsize=(9, 9))
# 设置分层颜色条
cmap = plt.cm.get_cmap('coolwarm', len(set(data['GDP_class'])))
# vmax和vmin设置是为了让等级值居中
data.plot(ax=ax,column='GDP_class', cmap=cmap, legend=False,vmin=-0.5,vmax=3.5)
ax.axis('off')
# 设置Colorbar的刻度
cbar = ax.get_figure().colorbar(ax.collections[0],shrink=0.5)
cbar.set_ticks([0,1,2,3])
cbar.set_label('GDP数据分级')
cbar.set_ticklabels(['等级0','等级1','等级2','等级3'])
# 隐藏刻度线
ticks = cbar.ax.get_yaxis().get_major_ticks()
for tick in ticks:
tick.tick1line.set_visible(False)
tick.tick2line.set_visible(False)
# 设置
fontdict = {'family':'FZSongYi-Z13S', 'size':8, 'color': "black",'weight': 'bold'}
# 设置标题
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
x = data.iloc[index].geometry.centroid.x
y = data.iloc[index].geometry.centroid.y
name = data.iloc[index]["name"]
if name in ["苏州市","无锡市"]:
x = x*1.001
ax.text(x, y, name, ha="center", va="center", fontdict=fontdict)
# 保存图片
fig.savefig('res.png', dpi=300, bbox_inches='tight')
方法3
分层设色不仅可以设置各区域的颜色,也可以设置各区域的填充图案
# 创建分类器
classifier = mapclassify.MaximumBreaks(data['GDP'], k=3)
classifier
MaximumBreaks
Interval Count
----------------------------
[ 4005.00, 13115.20] | 10
(13115.20, 20433.10] | 2
(20433.10, 23958.30] | 1
# 赋值数据
data['GDP_class'] = data['GDP'].apply(classifier)
data['GDP_class'] = data['GDP_class'].apply(lambda x : int(x))
data.head()
.dataframe tbody tr th:only-of-type { vertical-align: middle } \3c pre>\3c code>.dataframe tbody tr th { vertical-align: top } .dataframe thead th { text-align: right }
adcode | name | childrenNum | level | parent | subFeatureIndex | geometry | GDP | GDP_class | |
---|---|---|---|---|---|---|---|---|---|
0 | 320100 | 南京市 | 11 | city | {‘adcode’: 320000} | 0 | MULTIPOLYGON (((19828216.260 3681802.361, 1982… | 16907.9 | 1 |
1 | 320200 | 无锡市 | 7 | city | {‘adcode’: 320000} | 1 | MULTIPOLYGON (((19892555.472 3541293.638, 1989… | 14850.8 | 1 |
2 | 320300 | 徐州市 | 10 | city | {‘adcode’: 320000} | 2 | MULTIPOLYGON (((19736418.457 3894748.096, 1973… | 8457.8 | 0 |
3 | 320400 | 常州市 | 6 | city | {‘adcode’: 320000} | 3 | MULTIPOLYGON (((19927182.917 3638819.801, 1992… | 9550.1 | 0 |
4 | 320500 | 苏州市 | 9 | city | {‘adcode’: 320000} | 4 | MULTIPOLYGON (((19929872.531 3547724.116, 1993… | 23958.3 | 2 |
import matplotlib.patches as mpatches
# 设置图案列表
patterns = ["///", "", "*", "\\\\",".", "o", "O",]
cmap = plt.cm.get_cmap('coolwarm', len(set(data['GDP_class'])))
color_list = cmap([0,1,2])
fig, ax = plt.subplots(figsize=(9, 9))
# 自定义图示
legend_list = []
# 按层次设置legend
for i in set(data['GDP_class']):
tmp = data[data['GDP_class']==i]
tmp.plot(ax=ax,column='GDP_class', legend=False,hatch=patterns[i],edgecolor='black',color=color_list[i], linestyle='-',linewidth=2)
legend_list.append(
mpatches.Patch(facecolor=color_list[i], edgecolor='black',linestyle='-', linewidth=2,hatch=patterns[i], label='等级{}'.format(i))
)
ax.axis('off')
# 设置标题
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
# 自定义图示
ax.legend(handles = legend_list, loc='best', fontsize=12, title='GDP数据分级', shadow=True)
# 保存图片
fig.savefig('res.png', dpi=300, bbox_inches='tight')
本文转自 https://www.cnblogs.com/luohenyueji/p/17299870.html,如有侵权,请联系删除。