DBSCAN用于聚类地理位置数据

时间:2016-01-03 17:09:02

标签: python cluster-analysis dbscan

我有一个纬度和经度对的数据框。

这是我的数据框架。

    order_lat  order_long
0   19.111841   72.910729
1   19.111342   72.908387
2   19.111342   72.908387
3   19.137815   72.914085
4   19.119677   72.905081
5   19.119677   72.905081
6   19.119677   72.905081
7   19.120217   72.907121
8   19.120217   72.907121
9   19.119677   72.905081
10  19.119677   72.905081
11  19.119677   72.905081
12  19.111860   72.911346
13  19.111860   72.911346
14  19.119677   72.905081
15  19.119677   72.905081
16  19.119677   72.905081
17  19.137815   72.914085
18  19.115380   72.909144
19  19.115380   72.909144
20  19.116168   72.909573
21  19.119677   72.905081
22  19.137815   72.914085
23  19.137815   72.914085
24  19.112955   72.910102
25  19.112955   72.910102
26  19.112955   72.910102
27  19.119677   72.905081
28  19.119677   72.905081
29  19.115380   72.909144
30  19.119677   72.905081
31  19.119677   72.905081
32  19.119677   72.905081
33  19.119677   72.905081
34  19.119677   72.905081
35  19.111860   72.911346
36  19.111841   72.910729
37  19.131674   72.918510
38  19.119677   72.905081
39  19.111860   72.911346
40  19.111860   72.911346
41  19.111841   72.910729
42  19.111841   72.910729
43  19.111841   72.910729
44  19.115380   72.909144
45  19.116625   72.909185
46  19.115671   72.908985
47  19.119677   72.905081
48  19.119677   72.905081
49  19.119677   72.905081
50  19.116183   72.909646
51  19.113827   72.893833
52  19.119677   72.905081
53  19.114100   72.894985
54  19.107491   72.901760
55  19.119677   72.905081

我想聚集这些彼此最近的点(距离200米)以下是我的距离矩阵。

from scipy.spatial.distance import pdist, squareform
distance_matrix = squareform(pdist(X, (lambda u,v: haversine(u,v))))

array([[ 0.        ,  0.2522482 ,  0.2522482 , ...,  1.67313071,
     1.05925366,  1.05420922],
   [ 0.2522482 ,  0.        ,  0.        , ...,  1.44111548,
     0.81742536,  0.98978355],
   [ 0.2522482 ,  0.        ,  0.        , ...,  1.44111548,
     0.81742536,  0.98978355],
   ..., 
   [ 1.67313071,  1.44111548,  1.44111548, ...,  0.        ,
     1.02310118,  1.22871515],
   [ 1.05925366,  0.81742536,  0.81742536, ...,  1.02310118,
     0.        ,  1.39923529],
   [ 1.05420922,  0.98978355,  0.98978355, ...,  1.22871515,
     1.39923529,  0.        ]])

然后我在距离矩阵上应用DBSCAN聚类算法。

 from sklearn.cluster import DBSCAN

 db = DBSCAN(eps=2,min_samples=5)
 y_db = db.fit_predict(distance_matrix)

我不知道如何选择eps& min_samples值。它在一个星团中聚集了太远的点。(距离约2公里)是因为它在聚类时计算欧氏距离?请帮忙。

5 个答案:

答案 0 :(得分:28)

您可以使用scikit-learn的DBSCAN对空间纬度 - 经度数据进行聚类,而无需预先计算距离矩阵。

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值仍为2km,但它除以6371以将其转换为弧度。另请注意,.fit()以半径为单位获取半正弦度量的坐标。

答案 1 :(得分:11)

DBSCAN 意味着用于原始数据,具有加速的空间索引。我知道加速地理距离的唯一工具是ELKI(Java) - 不幸的是,scikit-learn仅支持欧几里德距离之类的一些距离(参见sklearn.neighbors.NearestNeighbors)。 但显然,您可以预先计算成对距离,因此这不是一个问题。

然而,你没有仔细阅读文档,并且你认为DBSCAN使用距离矩阵是错误的:

from sklearn.cluster import DBSCAN
db = DBSCAN(eps=2,min_samples=5)
db.fit_predict(distance_matrix)

在距离矩阵行上使用欧几里德距离,这显然没有任何意义。

请参阅DBSCAN的文档(重点已添加):

  

class sklearn.cluster.DBSCAN(eps = 0.5,min_samples = 5, metric ='euclidean',algorithm ='auto',leaf_size = 30,p = None,random_state = None)< / p>      

指标:字符串或可调用

     

计算要素数组中实例之间距离时使用的度量标准。如果metric是字符串或可调用的,则它必须是metrics.pairwise.calculate_distance为其度量参数所允许的选项之一。 如果度量是“预先计算的”,则假定X是距离矩阵,并且必须是正方形。 X可以是稀疏矩阵,在这种情况下,只有“非零”元素可以被视为DBSCAN的邻居。 / p>

fit_predict相似:

  

X :形状的阵列或稀疏(CSR)矩阵(n_samples,n_features)或形状数组(n_samples,n_samples)

     

要素数组,或样本之间的距离数组,如果指标='预先计算'。

换句话说,你需要做

db = DBSCAN(eps=2, min_samples=5, metric="precomputed")

答案 2 :(得分:5)

我不知道你正在使用haversine的实现方式,但看起来它以km为单位返回结果,因此eps应该是0.2,而不是200米。

对于min_samples参数,这取决于您的预期输出。这里有几个例子。我的输出正在使用基于this answerhaversine实现,它提供的距离矩阵与您的距离矩阵相似但不相同。

这是db = DBSCAN(eps=0.2, min_samples=5)

  

[0 -1 -1 -1 1 1 1 -1 -1 1 1 1 2 2 1 1 1 -1 -1 -1 -1 1 -1 -1 -1 -1 -1 1 1 -1 1 1 1 1 1 2 0 -1 1 2 2 0 0 0 -1 -1 -1 1 1 1 -1 -1 1 -1 -1 1]

这会创建三个群集0, 12,并且很多样本不会落入至少包含5个成员的群集中,因此不会分配给群集(显示为{ {1}})。

使用较小的-1值再次尝试:

min_samples

  

[0 1 1 2 3 3 3 4 4 3 3 3 5 5 3 3 3 2 6 6 7 3 2 2 8     8 8 3 3 6 3 3 3 3 3 5 0 -1 3 5 5 0 0 0 6 -1 -1 3 3 3     7 -1 3 -1 -1 3]

此处大多数样本距离至少一个其他样本的200米范围内,因此属于八个群集db = DBSCAN(eps=0.2, min_samples=2)0之一。

已编辑添加

看起来@ Anony-Mousse是对的,虽然我的结果没有看错。为了贡献某些东西,这里是我用来查看集群的代码:

7

答案 3 :(得分:0)

@eos给出了我认为的最佳答案-以及利用Haversine距离(在这种情况下,最相关的距离度量),它避免了生成预先计算的距离矩阵的需要。如果创建距离矩阵,则需要计算每个点组合的成对距离(尽管显然可以利用距离度量是对称的这一事实节省一些时间)。

如果仅向DBSCAN提供距离度量并使用ball_tree算法,则可以避免计算每个可能的距离。这是因为球树算法可以使用三角不等式定理来减少寻找数据点的最近邻居所需检查的候选对象数目(这是DBSCAN中最大的工作)。

三角形不等式定理指出:

|x+y| <= |x| + |y|

...因此,如果某个点p距其邻居x的距离n,而另一个点q是距{{ 1}},如果y大于我们最近的邻居半径,则我们知道px+y的距离必须太远才能被视为邻居,因此我们不需要计算距离。

详细了解scikit-learn documentation中的球树如何工作

答案 4 :(得分:0)

将DBSCAN与GPS数据一起使用可以做三件事。首先是,您可以使用 eps 参数指定要考虑创建集群的数据点之间的最大距离,如其他答案中所指定的那样,您需要考虑距离的范围指标,您选择的是一个有意义的值。然后,您可以使用 min_samples 将此方法用作移动时过滤掉数据点的方法。最后,指标将允许您使用所需的任何距离。

例如,在一个我正在从事的特殊研究项目中,我想从受试者从其智能手机收集的GPS数据位置中提取重要位置。我对主题如何穿越城市不感兴趣,而且我更愿意处理以米为单位的距离,所以我可以做下一个:

from geopy import distance
def mydist(p1, p2):
     return distance.great_circle((p1[0],p1[1],100),(p2[0],p2[1],100)).meters
DBSCAN(eps=50,min_samples=50,n_jobs=-1,metric=mydist)

根据DBSCAN documentation,在 eps 中,“将两个样本之间的最大距离视为另一个附近。” 最小样本数是“将某点视为核心点附近的样本数(或总权重)”。基本上,使用eps可以控制群集中数据点的接近程度,在上面的示例中,我选择了100米。 最小样本只是控制密度的一种方法,在上面的示例中,数据是以大约每秒一个样本的速度捕获的,因为我对人们何时走动而不是静止的位置不感兴趣希望确保我从同一位置至少获得了相当于60秒的GPS数据。

如果这仍然没有意义,请查看此DBSCAN animation