使用带有 pdist 和 squareform 的 nparray 创建距离矩阵
Posted
技术标签:
【中文标题】使用带有 pdist 和 squareform 的 nparray 创建距离矩阵【英文标题】:Distance matrix creation using nparray with pdist and squareform 【发布时间】:2014-03-31 15:34:33 【问题描述】:我正在尝试使用 DBSCAN(scikit 学习实现)和位置数据进行集群。我的数据是 np 数组格式,但是要将 DBSCAN 与 Haversine 公式一起使用,我需要创建一个距离矩阵。当我尝试执行此操作时出现以下错误(“模块”不可调用错误。)从我在线阅读的内容来看,这是一个导入错误,但我很确定这不是我的情况。我已经创建了自己的半正弦距离公式,但我确信错误不在于这个。
这是我的输入数据,一个 np 数组(ResultArray)。
[[ 53.3252628 -6.2644198 ]
[ 53.3287395 -6.2646543 ]
[ 53.33321202 -6.24785807]
[ 53.3261015 -6.2598324 ]
[ 53.325291 -6.2644105 ]
[ 53.3281323 -6.2661467 ]
[ 53.3253074 -6.2644483 ]
[ 53.3388147 -6.2338417 ]
[ 53.3381102 -6.2343826 ]
[ 53.3253074 -6.2644483 ]
[ 53.3228188 -6.2625379 ]
[ 53.3253074 -6.2644483 ]]
这是出错的代码行。
distance_matrix = sp.spatial.distance.squareform(sp.spatial.distance.pdist
(ResultArray,(lambda u,v: haversine(u,v))))
这是错误信息:
File "Location.py", line 48, in <module>
distance_matrix = sp.spatial.distance.squareform(sp.spatial.distance.pdist
(ResArray,(lambda u,v: haversine(u,v))))
File "/usr/lib/python2.7/dist-packages/scipy/spatial/distance.py", line 1118, in pdist
dm[k] = dfun(X[i], X[j])
File "Location.py", line 48, in <lambda>
distance_matrix = sp.spatial.distance.squareform(sp.spatial.distance.pdist
(ResArray,(lambda u,v: haversine(u,v))))
TypeError: 'module' object is not callable
我将 scipy 导入为 sp。 (将 scipy 导入为 sp)
【问题讨论】:
请注意,ELKI 使用 R*-trees 对 DBSCAN 中的正弦距离进行索引加速。这不需要 O(n^2) 时间和内存。它也有OPTICS,类似于DBSCAN 2.0 【参考方案1】:使用 Scipy,您可以按照 link 上的文档的建议定义自定义距离函数,并在此处报告以方便起见:
Y = pdist(X, f)
Computes the distance between all pairs of vectors in X using the user supplied 2-arity function f. For example, Euclidean distance between the vectors could be computed as follows:
dm = pdist(X, lambda u, v: np.sqrt(((u-v)**2).sum()))
我在这里报告我的代码版本,灵感来自link:
from numpy import sin,cos,arctan2,sqrt,pi # import from numpy
# earth's mean radius = 6,371km
EARTHRADIUS = 6371.0
def getDistanceByHaversine(loc1, loc2):
'''Haversine formula - give coordinates as a 2D numpy array of
(lat_denter link description hereecimal,lon_decimal) pairs'''
#
# "unpack" our numpy array, this extracts column wise arrays
lat1 = loc1[1]
lon1 = loc1[0]
lat2 = loc2[1]
lon2 = loc2[0]
#
# convert to radians ##### Completely identical
lon1 = lon1 * pi / 180.0
lon2 = lon2 * pi / 180.0
lat1 = lat1 * pi / 180.0
lat2 = lat2 * pi / 180.0
#
# haversine formula #### Same, but atan2 named arctan2 in numpy
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))**2 + cos(lat1) * cos(lat2) * (sin(dlon/2.0))**2
c = 2.0 * arctan2(sqrt(a), sqrt(1.0-a))
km = EARTHRADIUS * c
return km
并通过以下方式调用:
D = spatial.distance.pdist(A, lambda u, v: getDistanceByHaversine(u,v))
在我的实现中,矩阵 A 的第一列是经度值,第二列是以十进制度数表示的纬度值。
【讨论】:
【参考方案2】:请参考@TommasoF 的回答。这个答案是错误的:pdist
允许选择自定义距离函数。一旦不再被选为正确答案,我将删除答案。
只是scipy
的pdist
不允许传入自定义距离函数。正如您在docs 中看到的那样,您有一些选择,但在受支持的指标列表中没有哈弗赛德距离。
(Matlab pdist
确实支持该选项,请参阅 here)
您需要“手动”进行计算,即使用循环,这样的事情会起作用:
from numpy import array,zeros
def haversine(lon1, lat1, lon2, lat2):
""" See the link below for a possible implementation """
pass
#example input (your's, truncated)
ResultArray = array([[ 53.3252628, -6.2644198 ],
[ 53.3287395 , -6.2646543 ],
[ 53.33321202 , -6.24785807],
[ 53.3253074 , -6.2644483 ]])
N = ResultArray.shape[0]
distance_matrix = zeros((N, N))
for i in xrange(N):
for j in xrange(N):
lati, loni = ResultArray[i]
latj, lonj = ResultArray[j]
distance_matrix[i, j] = haversine(loni, lati, lonj, latj)
distance_matrix[j, i] = distance_matrix[i, j]
print distance_matrix
[[ 0. 0.38666203 1.41010971 0.00530489]
[ 0.38666203 0. 1.22043364 0.38163748]
[ 1.41010971 1.22043364 0. 1.40848782]
[ 0.00530489 0.38163748 1.40848782 0. ]]
仅供参考,可以在here找到Haverside的Python实现。
【讨论】:
显然 scipy 确实允许自定义距离函数,如 other answer 所示 感谢@shreyasgm 指出这一点,在回答后我没有阅读其他答案并且再也没有回到这里。 TheBaywatchKid,请您选择 TommasoF 的答案作为正确答案,以便我可以删除我的答案?【参考方案3】:您现在可以使用 scikit-learn 的 DBSCAN 和 hasrsine 度量对空间经纬度数据进行聚类,而无需使用 scipy 预先计算距离矩阵。
db = DBSCAN(eps=2/6371., min_samples=5, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))
这来自 clustering spatial data with scikit-learn DBSCAN 上的本教程。特别是,请注意eps
的值是 2 公里除以 6371(地球的半径,单位为公里)以将其转换为弧度。另请注意,.fit()
采用弧度单位的坐标作为半正弦度量。
【讨论】:
以上是关于使用带有 pdist 和 squareform 的 nparray 创建距离矩阵的主要内容,如果未能解决你的问题,请参考以下文章