在进行探索性数据分析(EDA)时,经常需要展示与地理位置相关的信息。例如,在处理COVID-19数据集时,可能想要显示不同地区的病例数量。这时,Python库GeoPandas就派上了用场。本文尝试为读者提供一个关于如何有效使用GeoPandas来可视化地理空间数据的简单介绍。
地理空间数据描述了与地球位置(坐标)相关的对象、事件或其他特征。空间数据由基本的几何对象类型表示(由shapely实现)。
几何体:用于表示点(例如地块中心点)、线(例如道路、溪流)和多边形(例如建筑物、湖泊、州等的边界)。
CRS/坐标参考系统:告诉如何使用投影或数学方程将地球上的位置(坐标)转换为同一位置在平面二维坐标系统上的位置(例如,电脑屏幕或纸质地图)。最常用的CRS是“EPSG:4326”。
GeoPandas基于pandas,它扩展了pandas的数据类型以包含几何列并执行空间操作。因此,熟悉pandas的用户可以轻松地采用GeoPandas。
GeoPandas的主要数据结构是GeoDataFrame,它扩展了pandas的DataFrame。因此,可以在GeoDataFrame上执行所有基础DataFrame操作。GeoDataFrame包含一个或多个GeoSeries(扩展了pandas的Series),每个GeoSeries包含不同投影中的几何体(GeoSeries.crs)。尽管GeoDataFrame可以有多个GeoSeries列,但只有一个将是活动几何体,即所有几何操作都将在该列上进行。
在接下来的部分中,将了解如何使用一些常用函数,如边界、质心和最重要的绘图方法。为了说明地理空间可视化的工作,将使用2021年奥运会的团队数据集。
在导入GeoPandas之前,先读取团队数据集。团队数据集包含团队名称、纪律、NOC(国家)和事件列。在本练习中,将仅使用NOC和纪律列。
import pandas as pd
df_teams = pd.read_excel("Teams.xlsx")
print(df_teams.info())
print(df_teams.head())
按每个国家的纪律总结并绘制条形图。
df_teams_countries_disciplines = df_teams.groupby(by="NOC").agg({'Discipline':'count'}).reset_index().sort_values(by='Discipline', ascending=False)
ax = df_teams_countries_disciplines.plot.bar(x='NOC', xlabel = '', figsize=(20,8))
导入GeoPandas并读取数据。
import geopandas as gpd
df_world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
print(f"{type(df_world)}, {df_world.geometry.name}")
print(df_world.head())
print(df_world.geometry.geom_type.value_counts())
“naturalearth_lowres”是GeoPandas提供的底图,加载了它。df_world是GeoDataFrame类型,包含大陆、国家名称和几何(国家区域)列。几何是GeoSeries类型,是活动几何体,以多边形和多边形类型表示国家区域。
现在来绘制世界地图。
df_world.plot(figsize=(10,6))
合并团队和世界数据集。
df_world_teams = df_world.merge(df_teams_countries_disciplines, how="left", left_on=['name'], right_on=['NOC'])
print("Type of DataFrame : ", type(df_world_teams), df_world_teams.shape[0])
df_world_teams.head()
注意:df_world_teams将有一些NOC和Discipline作为NaN的条目。这是故意这样做的,以便也有未参加的国家。一些国家名称在奥运会和世界数据集之间不一致。因此,尽可能调整了国家名称。详情在源代码中。
显示一个简单的世界地图——边界。
ax = df_world["geometry"].boundary.plot(figsize=(20,16))
接下来,让将参加奥运会的国家着色,颜色的深浅基于国家参加的纪律数量。国家参加的纪律越多,颜色越深,反之亦然。
df_world_teams.plot( column="Discipline", ax=ax, cmap='OrRd',
legend=True, legend_kwds={"label": "Participation", "orientation":"horizontal"})
根据着色,可以快速看出日本、美国、意大利、德国和澳大利亚是参加最多纪律的国家。
注意底部的图例看起来不太好。让修改df_world_teams.plot以使可视化更加美观。
fig, ax = plt.subplots(1, 1, figsize=(20, 16))
divider = make_axes_locatable(ax)
cax = divider.append_axes("right", size="2%", pad="0.5%")
df_world_teams.plot(
column="Discipline", ax=ax,
cax=cax, cmap='OrRd',
legend=True, legend_kwds={"label": "Participation"})
带有整齐的色图,这个可视化是不是更整洁了?
着色未参加的国家。
df_world_teams.plot(
column="Discipline", ax=ax, cax=cax, cmap='OrRd',
legend=True, legend_kwds={"label": "Participation"},
missing_kwds={'color': 'lightgrey'})
未参加奥运会的国家——着色灰色。
df_world_teams.plot(column= 'Discipline', ax=ax, cax=cax, cmap='OrRd',
legend=True, legend_kwds={"label": "Participation"},
missing_kwds={"color": "lightgrey",
"edgecolor": "white", "hatch": "|"})
未参加奥运会的国家——着色灰色并打上斜线。
标记参加最少纪律的国家——绘制点。
df_discipline_countries = df_teams.groupby(by='Discipline').agg({'NOC':'count'}).sort_values(by='NOC', ascending=False)
ax = df_discipline_countries.plot.bar(figsize=(8, 6))
纪律与国家数量。因此,棒球/垒球是参加国家最少的纪律(12个)。现在哪些国家参加了这个纪律?让找出答案。
首先,创建一个只包含参加最少参加纪律的国家的数据集,然后合并这个数据集df_teams_least_participated_disciplines和df_world,然后计算质心。
# 创建一个只包含参加最少参加纪律的国家的数据集
countries_in_least_participated_disciplines = df_discipline_countries[df_discipline_countries['NOC']<13].index.tolist()
print(least_participated_disciplines)
df_teams_least_participated_disciplines = df_teams[df_teams['Discipline'].isin(countries_in_least_participated_disciplines)].groupby(by=['NOC','Discipline']).agg({'Discipline':'count'})
df_teams_least_participated_disciplines.groupby(by=['NOC']).agg({'Discipline':'count'}).sort_values(by='Discipline', ascending=False)
# 合并
df_world_teams_least_participated_disciplines = df_world.merge(df_teams_least_participated_disciplines, how="right", left_on=['name'], right_on=['NOC'])
df_world_teams_least_participated_disciplines['centroid'] = df_world_teams_least_participated_disciplines.centroid
print("Type of DataFrame : ", type(df_world_teams_least_disciplines),df_world_teams_least_participated_disciplines.shape[0])
print(df_world_teams_least_participated_disciplines[:5])
所以澳大利亚、加拿大、多米尼加共和国等参加了最少参加的纪律。
将以下几行添加到之前编写的绘图代码中,以用深蓝色填充的圆圈标记这些国家。
df_world_teams_least_participated_disciplines["centroid"].plot(ax=ax, color="DarkBlue")
and the below to annotate the countries
df_world_teams_least_participated_disciplines.apply(lambda x: ax.annotate(text=x['name'], xy=(x['centroid'].coords[0][0],x['centroid'].coords[0][1]-5), ha='center'), axis=1)
参加了最少参加纪律的国家。
现在在世界地图上展示了奥运会团队。可以进一步扩展这一点,使其包含更丰富的信息。但要注意:不要以牺牲清晰度为代价添加太多细节到地图上。