使用Python可视化全球人口数据集
很多公共机构,如政府,面临国家背景下的问题,如人口增长。因此,通过可视化全球人口来帮助政府解决这些问题将非常有帮助。要做到这一点,需要利用人工智能和机器学习模型等技术来映射人口分布数据。
进行整个过程所需的几个指标包括
- 测量对灾害和气候变化的脆弱性,
- 对健康等基本服务的获取差距,
- 生态和土地利用约束都取决于捕捉人口的地理分布及其基本特征的能力。
- 对于基于证据的决策制定,需要足够精细和及时的人口统计数据,无论是用于安排、预算还是监管目的。
尤其是在发展中国家,最近一波高分辨率(HR)人口估计普查图层具有对公共部门决策产生重大影响的潜力。这些映射图层的数据是使用非传统方法(如卫星图像)收集的。
因此,他们可以为地球上每个网格单元提供30米分辨率的人口估计。API(应用程序接口)允许用户在线访问其最新的变化,使它们成为依赖数据的决策者潜在重要资源。
这些高分辨率人口地图解决了常规行政或统计普查人口数据的许多重要缺点。
诸如Facebook Research HD Settlement Layer和World Pop之类的数据集可以轻松部署,使用新一代的高分辨率人口估计技术进行各种描述性和指导性分析。
WorldPop项目成立于2013年,旨在使人口和人口统计数据对任何人都可访问,以便促进发展、救灾和健康的应用。它结合了数字化地图、卫星图像和在国家级别进行的小区域微调查。简言之,WorldPop使用机器学习模型(随机森林)来得出优质的国家人口数据。
本教程展示了Facebook提供的人口统计数据和高质量的人口密度图,以尝试使用Python可视化全球人口数据集来可视化越南各行政单位的人口统计数据。
我们还需要描述越南数字边界的信息,即形状文件,以便能够提取各行政单位的预测人口统计数据。
因此,这个分析需要三个数据集:来自GADM的行政边界数据、Facebook的人口数据和WorldPop的人口数据。
数据分析有4个步骤:
- 打开GADM并探索行政边界数据。
- 下载、调查并最终可视化WorldPop的人口统计数据。
- 下载、调查并最终可视化Facebook的人口统计数据。
- 评估并最终总结发现。
数据探索和分析
从GADM加载和探索行政边界数据
数据集来源:GADM(全球行政区划数据库),
它是一个优质的数据库,定义了最新版本的全球38.6775个行政区域。在下载越南国家级数据后,出现了以下组织结构的文件夹。
文件的索引(0,1,2,3)表示可以访问边界的行政级别。
越南有63个多边形处于ADM Level 1,在联邦政府的控制下,分为五十八个省和五个城市。越南省的第二级行政区划再细分为区、地区城市和乡镇。城市再细分为街道,然后进一步划分为城区、农村区和区级镇。因此,越南的686个二级行政实体和7658个三级行政实体都包含在GADM数据中。
源代码片段:
vietnam__administrative__boundaries = geopandas.read__file( 'Data/gadm36__VNM__shp/gadm36__VNM__3.shp' )vietnam__administrative__boundaries[ 'NAME__0' ].unique( )
> Vietnamvietnam__administrative__boundaries[ 'NAME__1' ].nunique( )
> 63vietnam__administrative__boundaries[ 'NAME__2' ].nunique( )
> 686vietnam__administrative__boundaries[ 'NAME__3' ].nunique( )
> 7658
输出:
图1:越南的一级、二级和三级单位
加载和探索WorldPop的人口数据
我们从WorldPop获取到了以像素(PPP)为单位的越南人口数据,采用的是100米分辨率的栅格格式,经过修改以对应联合国的全国估计数据。为了读取获取到的栅格数据(tif文件),我们使用了基于GDAL和numpy的Python包rasterio。
vietnam__worldpop__raster = rasterio.open(‘vnm__ppp__2020__UNadj.tif’)
任何一个具有每个像素分配地理位置的像素化数据都被称为栅格数据。像素的值可以是分类的(如土地利用)或连续的(如海拔)。地理栅格与数字图像唯一的区别在于是否包含了将数据与特定位置关联起来的空间信息。栅格的范围、单元格大小、行列数和坐标参考系统(CRS)都包含在其中。一个或多个被称为波段的图层组成了一个栅格数据集。多光谱图像可能包含多个波段,而彩色图像有三个独立的波段(红色、绿色和蓝色),高程模型有一个波段(存储高程值),彩色图像有三个波段(绿色、蓝色和红色)。
源代码片段
print( 'No. of bands:' , ( vietnam__worldpop__raster.count ) )
> No. of bands: 1
# Reading the 1st band, filtering bad raster numbers and visualize data with matplotlib library
vietnam__worldpop__raster__tot = vietnam__worldpop__raster.read( 1 )
vietnam__worldpop__raster__tot[ vietnam__worldpop__raster__tot < 0 ] = None
plt.rcParams[ 'figure.figsize' ] = 14, 14
plt.imshow( np.log10( vietnam__worldpop__raster__tot + 1 ), )
bar = plt.colorbar( fraction = 0.5 )
输出:
越南的WorldPop栅格图层
源代码片段:
# Calculating total population of Vietnamworldpop__raster__nonzero = vietnam__worldpop__raster__tot[ vietnam__worldpop__raster__tot > 0 ]population__worldpop = worldpop__raster__nonzero[ worldpop__raster__nonzero > 0 ].sum( )print( round( population__worldpop/1000000,2 ),'million' )
> 97.34 million
根据栅格层数据,越南总人口为9754万人。为了确定越南的63个省级市(第3级行政实体)内的人口数量,我们使用从GADM数据集中检索到的多边形对此栅格层进行遮罩处理。以下方法返回向量多边形内的栅格层人口计数。
源代码片段:
def get__population__count( vector__polygon,raster__layer ):
gtraster, bound = rasterio.mask.mask( raster__layer, [ vector__polygon ], crop = True )
pop__estimate = gtraster[ 0 ][ gtraster[ 0 ] > 0 ].sum( )
return ( pop__estimate.round( 2 ) )
vietnam__administrative__boundaries[ 'population__count__wp' ] = vietnam__administrative__boundaries[ 'geometry' ].apply( get__population__count, raster__layer = vietnam__worldpop__raster )
vietnam__administrative__boundaries[ 'population__count__wp__million' ] = round( vietnam__administrative__boundaries[ 'population__count__wp' ] / 1000000 ,2 )
通过包含一个名为population__count__wp的列,其中包含基于WorldPop栅格数据对ADM第一级的人口估计,代码生成了结果。
然后,使用下面的代码片段使用Plotly Choropleth地图来可视化总体人口估计。
源代码片段:
adm__level = vietnam__administrative__boundaries[ [ 'NAME__1' ,'population__count__wp__million' ,'geometry' ] ].round( ).reset__index( )
adm__level[ 'geometry' ] = adm__level[ 'geometry' ].apply( lambda x:gpd.GeoSeries( x ).to__json( ) )
dept__geo = [ ]
# Get data from dataset ( using json.loads to transform string into dict for geoshape column )
cd__depts = adm__level[ 'NAME__1' ].tolist( )
shapes = adm__level[ 'geometry' ].values
# Iterating over lines
for x in range( 0, len( adm__level[ 'geometry' ].values ),1 ):
shape = json.loads( adm__level[ 'geometry' ].values[ x ] )[ 'features' ][ 0 ][ 'geometry' ]
# Mapping informations from data to GeoJSONs file
dept__geo.append( {
'type': 'Feature' ,
'geometry': shape,
'id':cd__depts[ x ]
} )
# Encapsulateing dept__geo in a real GeoJSONs formatted file
dept__geo__ok = { 'type': 'FeatureCollection' , 'features': dept__geo }
fig = px.choropleth( adm__level, geojson = dept__geo__ok, locations = 'NAME__1' , color = 'population__count__wp__million' ,
color__continuous__scale = "Greens",
range__color = ( adm__level[ 'population__count__wp__million' ].min( ), adm__level[ 'population__count__wp__million' ].max( ) ),
labels = { 'population__count__wp__million':'Population WorldPop ( Million )' } )
fig.update__geos( fitbounds = "locations", visible = False )
输出:
图表:越南各地区的人口(以百万计)
从Facebook加载和探索人口数据
您可以在HDX上下载越南的Facebook人口地图,可以选择tif文件或CSV文件格式。它估计了30米网格瓦片内的人口。我们在此提供的CSV文件以与我们预处理tif格式的WorldPop数据相同的格式接收。
源代码片段:
Input: dataframe__google__mobility[ 'country__region' ].nunique( )
输出:
135
dataframe1 = dataframe__google__mobility.groupby( [ 'country__region' ] )[ 'sub__region__1' ].nunique( ).reset__index( ).sort__values( by = 'sub__region__1' )
dataframe2 = dataframe__google__mobility.groupby( [ 'country__region' ] )[ 'sub__region__2' ].nunique( ).reset__index( ).sort__values( by = 'sub__region__2' )
dataframe__data__availability = pd.merge( dataframe1,dataframe2,on = 'country__region' )
dataframe__data__availability[ dataframe__data__availability[ 'sub__region__1' ] > 0 ][ 'country__region' ].nunique( )
输出:
95
输入:
dataframe__data__availability[ dataframe__data__availability[ 'sub__region__1' ] > 0 ][ 'country__region' ].unique( )
输出:
array( [ 'Gabon' , 'Mongolia' , 'Belgium' , 'Réunion' , 'The Bahamas' , 'Uganda' ,
'Cape Verde' , 'Antigua and Barbuda' , 'Benin' , 'Belize' , 'Niger' ,
'Rwanda' , 'Togo' , 'Denmark' , 'Angola' , 'Israel' , 'Kuwait' ,
'Greece' , 'Burkina Faso' , 'Kyrgyzstan' , 'United Arab Emirates' ,
'Australia' , 'Haiti' , 'Slovakia' , 'Pakistan' , 'Lebanon' ,
'Botswana' , 'Barbados' , 'South Africa' , 'Austria' , 'Bolivia' ,
'Mauritius' , 'Zimbabwe' , 'Lithuania' , 'Cameroon' , 'Libya' ,
'Mozambique' , 'Namibia' , 'Oman' , 'Norway' , 'Jordan' ,
"Côte d'Ivoire", 'Netherlands' , 'Senegal' , 'Canada' ,
'Saudi Arabia' , 'France' , 'El Salvador' , 'Jamaica' , 'Czechia' ,
'Estonia' , 'Chile' , 'Malaysia' , 'Poland' , 'Nicaragua' ,
'New Zealand' , 'Germany' , 'Philippines' , 'Paraguay' , 'Honduras' ,
'Finland' , 'Uruguay' , 'Spain' , 'Italy' , 'Hungary' , 'Portugal' ,
'Sweden' , 'Croatia' , 'Guatemala' , 'Ecuador' , 'Argentina' ,
'Tanzania' , 'Cambodia' , 'Ireland' , 'Peru' , 'Switzerland' , 'Brazil' ,
'Egypt' , 'Bulgaria' , 'Dominican Republic' , 'Colombia' , 'Mexico' ,
'Indonesia' , 'India' , 'Nigeria' , 'Kenya' , 'Romania' , 'Japan' ,
'United States' , 'Latvia' , 'Slovenia' , 'Vietnam' , 'Puerto Rico' ,
'Turkey' , 'United Kingdom' ], dtype = object )
输入:
dataframe__data__availability[ dataframe__data__availability[ 'sub__region__2' ] > 0 ][ 'country__region' ].nunique( )
输出:
42
输入:
dataframe__data__availability[ dataframe__data__availability[ 'sub__region__2' ] > 0 ][ 'country__region' ].unique( )
输出:
array( [ 'Belgium' , 'Uganda' , 'Rwanda' , 'Denmark' , 'Israel' , 'Australia' ,
'Slovakia' , 'Lebanon' , 'Austria' , 'Bolivia' , 'Zimbabwe' ,
'Lithuania' , 'Cameroon' , 'Mozambique' , 'Norway' , 'Netherlands' ,
'Canada' , 'France' , 'Czechia' , 'Chile' , 'Poland' , 'Honduras' ,
'Finland' , 'Spain' , 'Italy' , 'Portugal' , 'Sweden' , 'Guatemala' ,
'Ecuador' , 'Argentina' , 'Tanzania' , 'Peru' , 'Brazil' , 'Bulgaria' ,
'Colombia' , 'India' , 'Nigeria' , 'Romania' , 'United States' ,
'Slovenia' , 'Turkey' , 'United Kingdom' ], dtype = object )
输入:
dataframe__google__mobility[ dataframe__google__mobility[ 'country__region' ] = = 'Vietnam' ][ 'sub__region__1' ].unique( )
输出:
array( [ nan, 'An Giang Province' , 'Ba Ria - Vung Tau' , 'Bac Giang' ,
'Bắc K?n Province' , 'Bac Lieu' , 'Bac Ninh Province' , 'Ben Tre' ,
'Binh Dinh Province' , 'Binh Duong' , 'Binh Phuoc' ,
'Bình Thuận Province' , 'Ca Mau' , 'Cần Th?' , 'Cao Bang' , 'Da Nang' ,
'Đắk Lắk Province' , 'Dak Nong' , 'Dien Bien' , 'Dong Nai' ,
'Đồng Tháp Province' , 'Gia Lai' , 'Ha Giang' , 'Hà Nam' , 'Ha Tinh' ,
'Hai Duong' , 'Haiphong' , 'Hanoi' , 'Hau Giang' , 'Ho Chi Minh City' ,
'Hoa Binh' , 'Hung Yen' , 'Khanh Hoa Province' , 'Kien Giang' ,
'Kon Tum Province' , 'Lai Chau' , 'Lâm Đồng' , 'L?ng S?n' , 'Lao Cai' ,
'Long An Province' , 'Nam Dinh' , 'Nghe An' , 'Ninh Bình Province' ,
'Ninh Thuan Province' , 'Phu Tho Province' , 'Phú Yên Province' ,
'Quang Binh Province' , 'Quang Nam Province' , 'Quang Ngai' ,
'Quảng Ninh' , 'Quảng Tr? Province' , 'Soc Trang' , 'Son La' ,
'Tây Ninh Province' , 'Thai Binh' , 'Thai Nguyen' , 'Thanh Hoa' ,
'Thua Thien Hue' , 'Tien Giang' , 'Tra Vinh' , 'Tuyên Quang' ,
'Vinh Long' , 'Vinh Phuc Province' , 'Yên Bái' ], dtype = object )
纬度、经度和2015年和2020年的人口估计数据已包含在CSV文件中。根据Facebook的数据,越南总人口为9816万。
我们必须将这个数据帧转换为具有几何字段的地理数据帧,以便应用于与WorldPop数据一起呈现的地理空间工具和方法。
源代码片段:
def convert__Point( facebook__data__latest ):
return Point( facebook__data__latest[ 'longitude' ],facebook__data__latest[ 'latitude' ] )
facebook__data__latest[ 'geometry' ] = facebook__data__latest[ [ 'latitude' ,'longitude' ] ].apply( convert__Point,axis = 1 )
facebook__data__latest = gpd.GeoDataFrame( facebook__data__latest )
然后,使用多边形对矢量图层进行遮罩算法,我们可以得到每组行政边界的人口统计数据。
源代码片段:
def get__population__count__vector( vector__polygon,vector__layer ):
pip__mask = vector__layer.within( vector__polygon )
pip__data = vector__layer.loc[ pip__mask ]
population__total = round( pip__data[ 'population__2020' ].sum( ),2 )
return( population__total )
vietnam__administrative__boundaries[ 'population__count__fb' ] = vietnam__administrative__boundaries[ 'geometry' ].apply( get__population__count__vector,vector__layer = facebook__data__latest )
vietnam__administrative__boundaries[ 'population__count__fb__million' ] = round( vietnam__administrative__boundaries[ 'population__count__fb' ]/1000000,2 )
使用下面的代码,我们接下来使用Plotly来创建填色地图。
源代码片段:
adm__level = vietnam__administrative__boundaries[ [ 'NAME__1' ,'population__count__fb__million' ,'geometry' ] ].round( 1 ).reset__index( )
adm__level[ 'geometry' ] = adm__level[ 'geometry' ].apply( lambda x:gpd.GeoSeries( x ).to__json( ) )
# Instantiating list of feature
dept__geo = [ ]
# Getting data from datasets
cd__depts = adm__level[ 'NAME__1' ].tolist( )
shapes = adm__level[ 'geometry' ].values
# Iterating over lines
for x in range( 0,len( adm__level[ 'geometry' ].values ),1 ):
shape = json.loads( adm__level[ 'geometry' ].values[ x ] )[ 'features' ][ 0 ][ 'geometry' ]
# Mapping informations from data to GeoJSONs file
dept__geo.append( {
'type': 'Feature' ,
'geometry': shape,
'id':cd__depts[ x ]
} )
# Encapsulating dept__geos in a real GeoJSONs formatted file
dept__geo__ok = { 'type': 'FeatureCollection' , 'features': dept__geo }
adm__level = adm__level[ [ 'NAME__1' ,'population__count__fb__million' ] ]
fig = px.choropleth( adm__level, geojson = dept__geo__ok, locations = 'NAME__1' , color = 'population__count__fb__million' ,
color__continuous__scale = "viridis",
range__color = ( adm__level[ 'population__count__fb__million' ].min( ), adm__level[ 'population__count__fb__million' ].max( ) ),
labels = { 'population__count__fb__million':'Population Facebook ( Million )' } )
fig.update__geos( fitbounds = "locations", visible = False )
fig.update__layout( margin = { "r" : 0,"t" : 0,"l" : 0,"b" : 0 } )
fig.show( )
输出:
结论
现在,通过行政定义的镜头,我们可以通过JPNE比较两个映射层的发现,这对大多数决策者来说更为熟悉。通过下面显示的散点图,我们可以可视化比较Worldpop与Facebook结果之间的比率。45度线意味着结果不受位置的影响而相同。
Facebook和Worldpop在省/市级别的人口数量显示出强连接。
将Facebook的人口估计与WorldPop的二级组织水平作比较,特别是在一些城镇,如平阳省和胡志明市,前者提供了后者更低的估计。最终提出的问题决定了这是否是一个问题。JPNE使得我们能够快速评估选择一种信息源而不是另一种对当前问题产生重大影响的程度。
数字技术平台和数据进展为解决各种政策问题提供了一种结合方式。然而,这需要领域专家与数据科学家、工程师和程序员之间的积极合作。