【Python + ArcGIS】数据分析实战
——静安教育资源数据获取、处理及空间可视化分析
0 前言
最近,由于科研需要,基于 百度地图API 和 GitHub wandergis 提供的开源源码,面向对象,自主设计地理编码和坐标转换工具。基于此,本文以静安教育资源数据获取、处理及空间可视化分析为例,结合爬虫与GIS,介绍地理数据获取、处理及可视化分析的实现方法。
由于本人对于爬虫不甚擅长,因此借用了现成的工具(八爪鱼采集器)进行数据爬取。
目 录
1 数据需求
1.1 数据内容
本文基于静安门户网站数据所提供的“学校名称”、“学校地址”等信息进行数据获取、处理及空间可视化分析。
1.2 分析需求
- 利用爬虫工具获取静安门户网站教育数据,分析数据结构,并简要分析数据结果
- 利用基于 百度地图API 和 GitHub wandergis 自主设计的地理编码和坐标转换工具进行地理编码与坐标转换
- 利用ArcGIS进行数据可视化分析
2 工具准备
- 八爪鱼采集器
- Microsoft Office Excel
- Python
涉及的库:requests, urllib, pandas, math, os - 百度地图API
- ArcGIS
3 数据处理流程
4 数据处理
4.1 数据爬取与数据格式分析
由于本人对于爬虫不甚擅长,通过利用八爪鱼采集器,导入网页地址(学-便民直通车-公共服务 -上海静安门户网站欢迎您!http://www.jingan.gov.cn/ggfw/004014/004014008/bmztcmoreinfo.html),通过自动识别网页生成采集流程并加以修改,采集网页数据并导出。
导出结果如下:
数据与数据类型:
数据表头 | 实际含义 | 数据类型 | 实际利用 |
---|---|---|---|
标题 | 学校名称 | 字符串 | 空间可视化 |
标题链接 | 链接地址 | 字符串 | |
bmdz | 学校地址 | 字符串 | 地理编码 |
编号 | 电话 | 字符串 |
根据分析需求,对学校数据进行整理,并新增“学校类型”这一属性(字符串及各级各类学校二元表),形成如下表格:
经个人统计,静安区共有各级各类学校210所(含分校),详细类别如下:
4.2 地理编码与坐标转换
对于“学校地址”这一字符串类型的数据,无法通过直接的方法进行空间数据可视化,必须先通过地理编码,获取经纬度坐标,然后再进行空间数据可视化。本文采用百度地图API进行地理编码。
由于百度地图API获取的地理编码为百度经纬度坐标bd09ll,是在国测局坐标gcj02ll(火星坐标系)对实际坐标/谷歌地图坐标WGS84的非线性加密的基础上进行二次线性加密的结果,因此需要进行坐标转换,根据GIS底图的不同使用相应的结果。本文采用GitHub wandergis 提供的开源源码进行地理编码。
本人结合二者功能,面向对象,自主设计地理编码和坐标转换工具,进行地理编码与坐标转换工具。
4.2.1 代码分析
- 在案例中,程序运行的流程是在使用百度地图API进行地理编码后,再进行从bd09ll到WGS84坐标的转换。而坐标转换工具具有WGS84、gcj02ll、bd09ll三相转换功能,因此,程序将坐标转换工具作为父类首先进行设计。
import requests,urllib
import pandas,math
import os
class Coordinate_Transform(object):
x_pi = 3.14159265358979324 * 3000.0 / 180.0
pi = 3.1415926535897932384626 # π
a = 6378245.0 # 长半轴
ee = 0.00669342162296594323 # 偏心率平方
def __init__(self,file_path,lng_collumn,lat_collumn,from_type,to_type):
self.file_path=file_path
self.lng_collumn,self.lat_collumn=lng_collumn-1,lat_collumn-1
self.from_type,self.to_type=from_type,to_type #wgs,gcj,bd
def _create(self):
if os.path.splitext(self.file_path)[-1] == '.csv':
dataframe = pandas.read_csv(self.file_path, header=0, index_col=None)
if os.path.splitext(self.file_path)[-1] == '.xlsx':
dataframe = pandas.read_excel(self.file_path, header=0, index_col=None)
if os.path.splitext(self.file_path)[-1] == '.xls':
dataframe = pandas.read_excel(self.file_path, header=0, index_col=None)
if self.to_type == 'wgs':
df_to_columns=pandas.DataFrame(columns=['lng_wgs', 'lng_wgs'])
if self.to_type == 'gcj':
df_to_columns=pandas.DataFrame(columns=['lng_gcj', 'lat_gcj'])
if self.to_type == 'bd':
df_to_columns=pandas.DataFrame(columns=['lng_bd', 'lat_bd'])
#df_log = pandas.DataFrame(columns=['response', 'answer'])
dataframe = pandas.concat([dataframe, df_to_columns], axis=1)
return dataframe
def _transform(self,lng,lat): # 三相转换函数
if self.from_type=='gcj' and self.to_type=='bd':
return self.gcj02_to_bd09(lng, lat)
elif self.from_type=='bd' and self.to_type=='gcj':
return self.bd09_to_gcj02(lng, lat)
elif self.from_type=='wgs' and self.to_type=='gcj':
return self.wgs84_to_gcj02(lng, lat)
elif self.from_type=='gcj' and self.to_type=='wgs':
return self.gcj02_to_wgs84(lng, lat)
elif self.from_type=='bd' and self.to_type=='wgs':
return self.bd09_to_wgs84(lng, lat)
elif self.from_type=='wgs' and self.to_type=='bd':
return self.wgs84_to_bd09(lng, lat)
def do_transform(self):
dataframe=self._create()
for i in range(len(dataframe)):
lng,lat = dataframe.iloc[i, self.lng_collumn],dataframe.iloc[i,self.lat_collumn]
dataframe.iloc[i,-2],dataframe.iloc[i,-1]=self._transform(lng,lat)
dataframe.iloc[:,:-2].to_excel(os.path.splitext(self.file_path)[0]+'_result.xlsx')
print('Success')
# 以下为GitHub wandergis 设计的坐标转换核心代码,输出为两个浮点数(float),详见源代码
def out_of_china(self,lng, lat):
def _transformlat(self,lng, lat):
def _transformlng(self,lng, lat):
def bd09_to_gcj02(self, bd_lng, bd_lat):
def gcj02_to_wgs84(self,gcj_lng, gcj_lat):
def bd09_to_wgs84(self,bd_lng, bd_lat):
def wgs84_to_gcj02(self,wgs_lng, wgs_lat):
def gcj02_to_bd09(self,lng, lat):
def wgs84_to_bd09(self,wgs_lng, wgs_lat):
代码应用方法:
from Coordinate_Transform import Coordinate_Transform
test_task=BaiduMapAPI_Geocoder(file_path,lng_collumn,lat_collumn,from_type,to_type)
# 例如:test_task=BaiduMapAPI_Geocoder('C:/Users/TEST/Desktop/静安教育分析/静安区学校列表.xlsx',14,15,'bd','wgs')
# file_path:可以使用.xls、.xlsx、.csv等三种文件格式
# lng_collumn,lat_collumn:为经度列、纬度列位置序号
# from_type,to_type:'bd'、'gcj'、'wgs'
test_task.do_transform()
- 由此,基于坐标转换父类进行设计百度地图地理编码及转换工具。
注意: 需要准备好在百度地图开放平台(http://lbsyun.baidu.com/) 申请的AK
import requests
import urllib
import os
import pandas
from Coordinate_Transform import Coordinate_Transform
class BaiduMapAPI_Geocoder(Coordinate_Transform):
ak=''# 此处填入您在 百度地图开放平台(http://lbsyun.baidu.com/)申请的AK
def __init__(self,file_path,address_collumn,city):
self.file_path=file_path
self.address_collumn=address_collumn
self.city=city
def _create(self):
if os.path.splitext(self.file_path)[-1]=='.csv':
dataframe = pandas.read_csv(self.file_path, header=0, index_col=None)
if os.path.splitext(self.file_path)[-1]=='.xlsx':
dataframe = pandas.read_excel(self.file_path, header=0, index_col=None)
if os.path.splitext(self.file_path)[-1]=='.xls':
dataframe = pandas.read_excel(self.file_path, header=0, index_col=None)
df_bd = pandas.DataFrame(columns=['lng_bd', 'lat_bd'])
df_gcj=pandas.DataFrame(columns=['lng_gcj', 'lat_gcj'])
df_wgs=pandas.DataFrame(columns=['lng_wgs', 'lng_wgs'])
df_log = pandas.DataFrame(columns=['confidence','comprehension','response', 'answer'])
dataframe = pandas.concat([dataframe,df_bd,df_gcj,df_wgs, df_log], axis=1,ignore_index=True)
return dataframe
def _api_bd09ll(self,address):
url_base='http://api.map.baidu.com/geocoding/v3/?'
params= {'address':address,
'city': self.city,
'ret_coordtype':'bd09ll',
'ak': self.ak,
'output': 'json'}
url=url_base+urllib.parse.urlencode(params)
response = requests.get(url)
answer = response.json()
bd_lng,bd_lat=float(answer['result']['location']['lng']),float(answer['result']['location']['lat'])
return response,answer,bd_lng,bd_lat
def do_BaiduMapAPI_Geocoder(self):
dataframe=self._create()
for i in range(len(dataframe)):
response,answer,bd_lng,bd_lat = self._api_bd09ll(dataframe.iloc[i, self.address_collumn-1])
dataframe.iloc[i,-10],dataframe.iloc[i,-9]=bd_lng,bd_lat
dataframe.iloc[i,-8],dataframe.iloc[i,-7]=Coordinate_Transform.bd09_to_gcj02(self,dataframe.iloc[i,-10],dataframe.iloc[i,-9])
dataframe.iloc[i,-6],dataframe.iloc[i,-5]=Coordinate_Transform.bd09_to_wgs84(self,dataframe.iloc[i,-10],dataframe.iloc[i,-9])
dataframe.iloc[i,-4],dataframe.iloc[i,-3],dataframe.iloc[i,-2],dataframe.iloc[i,-1]=answer['result']['confidence'],answer['result']['comprehension'],str(response),str(answer)
dataframe.iloc[:,:-2].to_excel(os.path.basename(self.file_path)+'_result.xlsx')
dataframe.to_excel(os.path.basename(self.file_path)+'_logfile.xlsx')
print('Success')
代码应用方法:
from BaiduMapAPI_Geocoder import BaiduMapAPI_Geocoder
test_task=BaiduMapAPI_Geocoder(file_path,address_collumn,city)
# 例如:test_task=BaiduMapAPI_Geocoder('C:/Users/TEST/Desktop/静安教育分析/静安区学校列表.xlsx',3,'上海')
# file_path:可以使用.xls、.xlsx、.csv等三种文件格式
# address_collumn:为地址列位置序号
# city:地址所在的城市名。用于指定上述地址所在的城市,当多个城市都有上述地址时,该参数起到过滤作用,但不限制坐标召回城市。
test_task.do_BaiduMapAPI_Geocoder()
4.2.2 结果导出
通过运行代码,可以导出如下Excel表格,获取地理编码的百度坐标、经转换后的国测局和WGS84坐标,以及日志信息。
4.3 空间可视化分析
- 在ArcGIS中新建地图并定义工作环境
2. 导入Excel中的数据
3. 可以看到,在三种不同的坐标体系下,点的位置存在着不同程度偏移,这也为后续的应用打下基础。
4. 加载 天地图·上海 地图服务底图,进行点数据可视化。
5. 经观察,WGS84图层数据的偏移程度最低,因此利用该数据进行进一步分析。
(1) 核密度分析(密度)
(2) 欧氏距离(影响范围)
(3) 各级各类学校数据分类可视化
4 分析结果·需求达成
对静安教育资源数据及可视化结果进行初步分析,可知静安区教育资源丰富,各级各类学校计210所,详细信息如下表所示:
在空间分布上,以吴淞江(苏州河)和中环线-铁路南何支线为界分为三部分:
原静安区吴淞江(苏州河)以南地区,教育资源分布密集,学校面积普遍较小,与静安区狭小的面积与密集的人口相关;
原闸北区吴淞江(苏州河)以北地区至中环线地区,教育资源分布较为密集,其分布与人口居住分布密切相关,呈南密疏;
中环线-铁路南何支线附近布局较多工业与交通用地,居住人口较少的同时环境亦较差,教育资源呈空白;
原闸北区铁路南何支线以北地区,位于两区交界,面积较小而人口较少,教育资源密度较低。
4.1 进一步研究
在此研究的基础上,可以利用学校面积数据等进行进一步的教育土地资源分析,利用道路数据进行进一步的成本距离分析、网络分析等。