利用机器学习算法,高效分析地图数据中位置的距离关系
利用机器学习算法中sklean库中KNN、高效分析地图位置点的关系。其中KNN模块中主要使用ball-tree数据结构模型,提高运算效率完成各类距离关系分析。k-近邻算法的核心思想是未标记样本的类别,由距离其最近的k个邻居投票决定。
一、计算经纬度间的距离
经纬度计算有测地距离、大圆距离和欧几里得距离,其中测地距离使用地球椭圆模型,大圆距离使用地球未圆形模型、欧几里得直接计算两点间直线距离,精度依次降低但是(随着计算复杂度降低)运算速度依次增加。Geopy可以用 geodesic distance 或 great-circle distance ,默认测地线距离作为函数
geopy.distance.distance
geodesic distance 地表两点之间的最短路径的距离。 有许多类型的椭球体模型,哪一个模型最精确取决于你的点在地球上的位置。默认值是WGS-84椭球体,它是全局最精确的。geopy还包括 distance.ELLIPSOIDS 字典: model major (km) minor (km) flattening ELLIPSOIDS = {‘WGS-84’: (6378.137, 6356.7523142, 1 / 298.257223563), ‘GRS-80’: (6378.137, 6356.7523141, 1 / 298.257222101), ‘Airy (1830)’: (6377.563396, 6356.256909, 1 / 299.3249646), ‘Intl 1924’: (6378.388, 6356.911946, 1 / 297.0), ‘Clarke (1880)’: (6378.249145, 6356.51486955, 1 / 293.465), ‘GRS-67’: (6378.1600, 6356.774719, 1 / 298.25), }
大圆距离( great_circle )使用地球的球形模型,使用国际大地测量学和地球物理学联合会定义的平均地球半径,(2)a + b) /3=6371.0087714150598公里,约6371.009公里(WGS-84),误差高达0.5%。半径值存储在 distance.EARTH_RADIUS ,因此可以对其进行自定义(但是,它应该始终以公里为单位)。
欧几里得距离 使用scipy通过numpy可以高效计算欧几里得距离。
from scipy.spatial.distance import cdist
lonlat1=data_lonlat_1[['lat_1','lon_1']]
lonlat2=data_lonlat_2[['lat_2','lon_2']]
distance=cdist(lonlat1,lonlat2,metric='euclidean')
1、计算任意两个点的距离
利用Geopy库计算测地距离,地球模型使用默认值WGS-84椭球体。语句d=distance.distance(old, new).m 的m(代表米)可以改为km。
def dis_N(xlon,xlat,ylon,ylat):
old=(xlat,xlon)
new=(ylat,ylon)
d=distance.distance(old, new).m
s=round(float(d),2)
return s
2、计算numpy数组的距离。
注意事项:
1、Geopy库的经纬度输入先lat后lon,为了适配数据源中最常见的 经度—纬度 数据结构调整了输入元组中的顺序为 old=(o[1],o[0])。
2、为了适配机器学习中常用的numpy数据,通过以下数据转化数据格式
data_jwd=data[[“经度”,“纬度”]]
#转为numpy 数组
data_p = data_jwd.values
距离函数
def disN(o,n):
# Geopy库先lat后lon
old=(o[1],o[0])
new=(n[1],n[0])
d=distance.distance(old, new).m
s=round(float(d),2)
return s
二、计算距离m内个所有的点。
代码如下
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import NearestNeighbors
from geopy import distance
pd.options.mode.chained_assignment = None
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
距离计算函数
def disN(o,n):
# Geopy库先lat后lon
old=(o[1],o[0])
new=(n[1],n[0])
d=distance.distance(old, new).m
s=round(float(d),2)
return s
逻辑函数
def distance_m(df_s,find_t,dis_m):
#形成待查经纬度表,并清洗NA值
find_n=find_t[["经度","纬度"]]
find_n.dropna(inplace=True)
find_n.reset_index(drop=True, inplace=True)
#形成待查工参表,并清洗NA值
data=df_s[["小区中文名","CGI","经度","纬度"]]
data.dropna(inplace=True)
data.reset_index(drop=True,inplace=True)
#形成numpy数组
data_cgi=data["CGI"].values
data_jwd=data[["经度","纬度"]]
data_p = data_jwd.values
#knn 建立最近邻模型和训练,主要是对data_p中数据建立了'ball_tree'数据模型
nn = NearestNeighbors(metric=disN, algorithm='ball_tree')
nn.fit(data_p)
distances, points = nn.radius_neighbors(find_n, dis_m, return_distance=True)
将索引转换为想要的字段
k = list(points)
r = len(k)
for i in range(r):
z_array = k[i]
z_array = z_array.astype(np.str)
m = len(z_array)
for j in range(m):
n = z_array[j]
n = int(n)
z_array[j] = data_cgi[n]
k[i] = z_array
find_cgi = pd.DataFrame(k)
data_dis = pd.DataFrame(distances)
data_dis = pd.DataFrame(data_dis[0].values.tolist())
df=pd.concat([find_t,data_dis,find_cgi],axis=1)
return df
df_s 为source,对find_t表中的每一个经纬度从df_s表查找距离最近的N个点,
#df_s需要包含[“小区中文名”, “CGI”, “经度”, “纬度”]
df_s=pd.read_csv(r"path\xx.csv",encoding='utf-8')
find_t为需要查找的表 target表,需要包含[“经度”, “纬度”]
find_t=pd.read_csv(r"path\xx.csv",encoding='utf-8')
df=distance_m(df_s,find_t,200)
df.to_csv(r"path\yy.csv",encoding='utf-8')
代码解释及注意事项:
1、wps 的csv格式经常中文乱码,除了需要加encoding='utf-8’外,还需要将csv文件用txt打开然后另存为’utf-8’格式的文件。
2、 knn 建立最近邻模型和训练,主要是对data_p中的经纬度数据建立了’ball_tree’数据模型。然后利用该数据模型高效的计算距离m内个所有的点。其中metric是用来计算距离的函数,既可以是标准的欧氏距离、马氏距离也可以是自定义的距离函数。algorithm=‘ball_tree’ 表示使用的数据结构模型。dis_m是搜索距离m。 返回值distances是一维, numpy数组,points 是元素为不同长度数组的一维数组。
nn = NearestNeighbors(metric=disN, algorithm='ball_tree')
nn.fit(data_p)
distances, points = nn.radius_neighbors(find_n, dis_m, return_distance=True)
3、points 是元素为数组的一维数组。他的元素是形成’ball_tree’数据模型的原始数据的索引,即是被查找的数据的索引。以下为查找该索引对应的字段的方法。由于直接遍历和修改元素是不同长度的数组的方法不熟练,使用转化为list的方法进行查找。
该步常见告警为:
#setting an array element with a sequence:元素长度不一致会报错,因为points是一个一维数组,它的元素是不同长度的数组
数组的修改前后,元素的type必须一致。否则报错invalid literal for int() with base 10。因此再需要查找的字段为str类型时需要先通过z_array = z_array.astype(np.str)将元素(下面例子中是data_cgi[n]的索引)转化为字符串才能进行修改,在需要通过索引(下面例子中即z_array 素组的元素)查找字段时需要n = int(n)转化回来。
#将索引转换为想要的字段
k = list(points)
r = len(k)
for i in range(r):
z_array = k[i]
z_array = z_array.astype(np.str)
m = len(z_array)
for j in range(m):
n = z_array[j]
n = int(n)
z_array[j] = data_cgi[n]
k[i] = z_array
4、数据源与清洗
以基站数据为例子
df_s 为source,对find_t表中的每一个经纬度从df_s表查找距离最近的N个点,df_s需要包含[“小区中文名”, “CGI”, “经度”, “纬度”] ,其中"经度", "纬度"是必选项。
find_t为需要查找的表 target表,需要包含[“经度”, “纬度”]。
由于KNN模型训练不能有空值需要删除NaN,同时index是第三步查找字段索引,必须要重设置,保障索引一致。
find_n=find_t[["经度","纬度"]]
find_n.dropna(inplace=True)
find_n.reset_index(drop=True, inplace=True)
三、查找n个最近的点
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import NearestNeighbors
from geopy import distance
pd.options.mode.chained_assignment = None
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
距离计算函数
def disN(o, n):
# Geopy库先lat后long
old = (o[1], o[0])
new = (n[1], n[0])
d = distance.distance(old, new).m
s = round(float(d), 2)
return s
计算最近n个点,df_s 为source,对find_t表中的每一个经纬度从df_s表查找距离最近的N个点,find_t为需要查找的表 target表
def nearest_n(df_s,find_t,n):
由于输出结果包含要查找的经纬度(原始点),因此必须查找n+1个点其中才包含n个距离最近的点
m=n+1
# 形成待查经纬度表,并清洗NA值
find_n = find_t[["经度", "纬度"]]
find_n.dropna(inplace=True)
find_n.reset_index(drop=True, inplace=True)
形成待查工参表,并清洗NA值
data = df_s[["小区中文名", "CGI", "经度", "纬度"]]
data.dropna(inplace=True)
data.reset_index(drop=True, inplace=True)
形成numpy数组
data_cgi = data["CGI"].values
data_jwd=data[["经度","纬度"]]
data_p = data_jwd.values
knn 建立最近邻模型和训练,主要是对data_p中数据建立了’ball_tree’数据模型
nn = NearestNeighbors(metric=disN, algorithm='ball_tree')
nn.fit(data_p)
distances, points = nn.kneighbors(find_n.values, m, return_distance=True)
将索引转换为cgi
b_n = points.copy()
b_n = b_n.astype(np.str)
for i in np.nditer(b_n, op_flags=['readwrite']):
i[...] = data_cgi[i.astype('int64')]
c = np.concatenate((distances, b_n), axis=1)
data_df = pd.DataFrame(c)
df_r = pd.concat([find_t, data_df], axis=1)
return df_r
剔除结果中的查找点。定义函数后使用
df=df.apply(lambda x:tichubenzhan(x),axis=1)
由于输出结果包含要查找的经纬度(原始点),因此索引和值之间的距离是n + 1
def tichubenzhan1(sr,n):
m=n+1
k=sr[sr.values==sr["CGI"]].index
index_cgi=k[1]
print(index_cgi)
index_dis=index_cgi-m
sr.drop(index_cgi,inplace=True)
sr.drop(index_dis,inplace=True)
return sr
df_s 为source,对find_t表中的每一个经纬度从df_s表查找距离最近的N个点,
df_s需要包含[“小区中文名”, “CGI”, “经度”, “纬度”]
df_s=pd.read_csv(rr"path\xx.csv",encoding=‘utf-8’)find_t为需要查找的表 target表,需要包含[“经度”, “纬度”]
find_t=pd.read_csv(r"D:\aTools\1Jupyter_workspace\find_t.csv",encoding='utf-8')
df=nearest_n(df_s,find_t,3)
df.to_csv(r"D:\aTools\1Jupyter_workspace\find_t2.csv", encoding='utf-8')
print(df)
注意:由于输出结果包含要查找的经纬度(原始点),因此必须查找n+1个点其中才包含n个距离最近的点。
更多推荐

所有评论(0)