使用nparray、pdist和squareform创建距离矩阵

5
我正在尝试使用DBSCAN(scikit learn实现)和位置数据进行聚类。我的数据以np数组格式存在,但是要使用Haversine公式与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。(import scipy as sp)


请注意,ELKI在DBSCAN中使用R * -trees对haversine距离进行索引加速。这不需要O(n ^ 2)的时间和内存。它还具有OPTICS,类似于DBSCAN 2.0。 - Has QUIT--Anony-Mousse
3个回答

10
使用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()))

这里报告一下我根据这个链接上的代码编写的版本:

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的第一列为经度值,第二列为用十进制度数表示的纬度值。


3
请参考@TommasoF的答案。这个回答是错误的:pdist允许选择自定义距离函数。一旦它不再被选为正确答案,我将删除这个答案。
简单地说,scipypdist不允许传递自定义距离函数。正如您可以在文档中所读到的那样,您有一些选项,但haverside距离不在支持的度量列表中。
(Matlab pdist支持该选项,请参见此处
您需要手动进行计算,即使用循环,类似以下内容即可:
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.        ]]

仅供参考,Python中Haverside的实现可以在此处找到。


显然,scipy允许自定义距离函数,正如这个其他答案所示。 - shrgm
感谢@shreyasgm指出这一点,在回答后我没有阅读其他答案,也没有回来。TheBaywatchKid,请选择TommasoF的答案作为正确答案,这样我就能删除我的答案了。 - gg349

0
现在,您可以使用scikit-learn的DBSCAN和haversine度量来对空间经纬度数据进行聚类,而无需预先计算距离矩阵,这是通过scipy实现的。
db = DBSCAN(eps=2/6371., min_samples=5, algorithm='ball_tree', metric='haversine').fit(np.radians(coordinates))

这篇文章来自于关于使用 scikit-learn DBSCAN 进行空间数据聚类的教程(译者注:英文链接)。特别要注意,eps 的值是 2 公里除以 6371(地球半径,单位为公里),用于将其转换为弧度。同时,请注意,.fit() 使用haversine 度量时需要输入弧度单位的坐标。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接