课程
在处理真实世界的数据时,您不能想当然地认为数据集中已经包含解决数据科学项目所需的一切关键信息。多数情况下,您掌握的是不完整的信息,需要通过添加额外特征来丰富数据。
有时数据集包含地址,但同时缺少纬度和经度。此时,丰富数据的第一步就是补充这一对坐标。我们将把把地址转换为纬度和经度的过程称为地理编码(geocoding)。
地理编码在诸多领域都至关重要,例如房地产、金融和物流。没有这项技术,您无法在地图上分析和可视化数据。在本教程中,我们将借助一个名为 Geopy 的“救命稻草”库,用 Python 来执行地理编码。让我们开始吧!
什么是地理编码?
地理编码是一个将地址(如 "1600 Amphitheatre Parkway, Mountain View, CA")转换为地理坐标(如纬度 37.423021、经度 -122.083739)的过程,您可以用这些坐标在地图上放置标记或定位地图。通俗来说,它是将物理地址描述转换为地球表面具体位置的计算过程。
地理编码的过程通常涉及在地图的地理范围内对空间位置(如街道或地块)进行插值。得到的位置会以数值坐标的形式输出,适合用于空间分析或地图可视化。
地理编码使用多种数据源将地址转换为地理位置。主要来源是街道参考数据库,其中包含特定县或区域内的街道名称和门牌范围。其他来源包括邮政编码数据库、城市名称数据库,以及重要地点(如公园或地标)的数据库。
地理编码不仅仅是为给定地址获取坐标。它还用于空间分析、数据挖掘和数据清洗。例如,企业使用地理编码来分析人口统计信息,以推动目标营销活动或优化配送路线。在公共安全领域,地理编码用于绘制紧急事件位置,以便快速调度最近的应急服务。
在数据科学的语境中,地理编码是创建地理空间可视化过程中的关键一步,并可用于多种应用,如房价预测、配送路线优化和灾害响应规划。
什么是 Geopy?

Geopy 是一个开源的 Python 库,擅长通过多种地理编码服务(如 Google Maps、Open StreeMap 和 ArcGIS)为数据添加位置信息。
您可能会问,为什么不直接使用各家的地理编码服务,而要用这个 Python 库?Geopy 的妙处在于,您只需寥寥数行代码就能调用这些服务。实际上,Geopy 为每种服务都实现了相应的类,您可以在不同服务的 API 之间轻松切换。
上手实战:地理编码项目示例
本文将分析来自 Kaggle 的 加拿大博物馆数据集,其中提供了位于加拿大的博物馆信息。这个博物馆集合在预测多伦多 Airbnb 住宿价格时可能会有用;例如,您或许注意到,一个房源距离博物馆、餐厅、咖啡馆等兴趣点越近,租金往往越高。
先来看看数据集:
import pandas as pd
import geopy
l_cols= ['Name','Street Address','City','State','Zipcode']
df = pd.read_csv('/kaggle/input/canada-museums/museums list CAN.csv', encoding = "ISO-8859-1",usecols=l_cols)
df = df[df.City=='Toronto']
df.head()

从这个概览可以看到,不同的列都包含地址信息,例如博物馆名称、街道地址、城市、省/州以及邮编。
接下来的步骤中,我们需要一个将上述信息合并后的唯一列。如何实现?连接两列以上时,最高效的方式是使用 pandas.series.str.cat(),它允许我们指定各列之间的分隔符:
df['Country'] = 'Canada'
l_cols_concat = ['Street Address','City','State','Zipcode','Country']
df['unique_address'] = df['Name'].str.cat(others=df[l_cols_concat], sep=',',na_rep='')
df.head()

我们可以创建一个字符串变量“address1”,其中包含第一行的唯一地址:
address1 = df['unique_address'].iloc[0]
print(address1)
# Bizune Event Gallery,452 Richmond St W,Toronto,ON,M5V 1Y1,Canada
接下来我们会用它在单个地址上试验不同的地理编码服务。一旦弄清 Geopy 如何提取位置,就可以推广到 pandas 数据框的一整列。我们将尝试以下主要提供商:
- Google Maps
- OpenStreetMap
- ArcGIS
使用 Google Maps API 进行地理编码
将地址转换为坐标的最常见方法是使用 Google Maps API。
尽管 Google Maps 提供的是付费服务,但首次创建账户会赠送 200 美元的免费额度。
要获取服务访问权限,您需要在 Google Maps Platform 上创建新账户。这里有一篇很棒的教程可帮助您生成 API 密钥。
GM_API_KEY = 'your_api_key'
from geopy.geocoders import GoogleV3
geolocator = GoogleV3(api_key=GM_API_KEY)
GoogleV3 是为使用 Google Maps v3 API 而实现的类。首先,我们可以尝试从单个地址中提取位置:
location = geolocator.geocode(address1)
print('Latitude: '+str(location.latitude)+', Longitude: '+str(location.longitude))

之后,我们可以将该操作扩展到此前创建的 unique_address 字段。
def service_geocode(g_locator, address):
location = g_locator.geocode(address)
if location!=None:
return (location.latitude, location.longitude)
else:
return np.NaN
我们可以使用 apply() 将该函数应用到 unique_address 的所有行:
df['LAT_LON'] = df['unique_address'].apply(lambda x:service_geocode(geolocator,x))
df[['unique_address','LAT_LON']].head()

使用 OpenStreetMap API 进行地理编码
由于 OpenStreetMap 是最大且可编辑的免费地理数据库与项目,其 API 可免费使用。要地理定位单个地址,您只需将 OSM 账户的电子邮箱作为输入传递给 Nominatim 类,而无需 API 密钥:
from geopy.geocoders import Nominatim
geolocator = Nominatim(user_agent="your_email")
location = geolocator.geocode(address1)
print(location)
# None
很遗憾,OpenStreetMap API 返回的 location 为 None,因为它无法识别该地址。
事实上,这个 API 在定位数据集中大多数街道地址时都有困难,因为其在所有区域的地址覆盖并不完善。
因此,我们需要创建另一个信息更少的字段 unique_address_osm:
l_cols_concat = ['City','State','Country']
df['unique_address_osm'] = df['Street Address'].str.cat(others=df[l_cols_concat], sep=',',na_rep='')
address1_osm = df['unique_address_osm'].iloc[0]
随后,我们传入新的地址,再次尝试从街道地址中提取纬度和经度:
address1_osm = df['unique_address_osm'].iloc[0]
location = geolocator.geocode(address1_osm)
print('Latitude: '+str(location.latitude)+', Longitude: '+str(location.longitude))

与之前一样,我们将 service_geocode() 函数应用到整列 unique_address_osm:
df['LAT_LON_osm'] = df['unique_address_osm'].apply(lambda x: service_geocode(geolocator,x))
df[['unique_address_osm','LAT_LON','LAT_LON_osm']].head()

尽管我们并未指定博物馆的名称,OpenStreetMap 返回的坐标与 GoogleMapsAPI 的结果非常接近。
使用 ArcGIS API 进行地理编码
ArcGIS 是一款用于分析和可视化地理空间数据的软件。它也提供类似于 Google Maps 和 OpenStreetMap 的位置服务。
from geopy.geocoders import ArcGIS
geolocator_arcgis = ArcGIS()
location = geolocator_arcgis.geocode(address1)
print('Latitude: '+str(location.latitude)+', Longitude: '+str(location.longitude))

这一次获取坐标对没有任何问题。我们可以继续将该操作扩展至所有地址:
df['LAT_LON_arcgis'] = df['unique_address'].apply(lambda x: service_geocode(geolocator_arcgis,x))
df[['unique_address','LAT_LON','LAT_LON_osm','LAT_LON_arcgis']].head()

从数据集的前几行可以看到,ArcGIS 获得的坐标对与之前的结果相似,只是略有差异。
测量两地之间的距离
Geopy 的另一项功能是可以计算两点之间的距离。这通过两组(纬度、经度)之间的测地距离实现。
from geopy.distance import geodesic
point1 = df.LAT_LON_arcgis.iloc[0]
point2 = df.LAT_LON_arcgis.iloc[1]
distance = geodesic(point1, point2)
print('The distance between {} and {} is {} meters'.format(df.Name.iloc[0],df.Name.iloc[1],distance.meters))

这两座博物馆之间的距离约为 2 公里。
总结
就是这样!您已经学会了使用 Geopy 将地址转换为纬度和经度的三种方法。这是开展更复杂、更高级地理空间数据分析的起点。
如果您想零代码入门地理空间分析,最好的方式是查看 DataCamp 的教程 使用地理空间数据:Power BI 分析指南。
若要构建更高级的可视化与计算,Tableau 会是理想工具。DataCamp 的这门课程Tableau 入门将帮助您创建动态数据可视化并解决各种分析问题。
如果您是 R 语言爱好者,这门优秀课程将带您使用 leaflet 创建交互式地图。如果您更偏爱 Python,不妨学习在 Python 中可视化地理空间数据,它将帮助您使用 geopandas 和 folium 制作出色的可视化。
