按距离分组点

时间:2018-04-20 13:58:55

标签: python

我要做的是分组位置彼此相隔最小距离并排除那些不符合该距离的位置。另外,我试图得到一组10人。

一个例子,假设我有4组坐标用于A,B,C和D.我试图将它们分组,使它们彼此相距至少1英里。列表中的第一个点A将是起点。   B距离A:包含在列表中1.5英里   C距离B有2英里,距离A:Include列表有2.2英里   D距离C 0.5英里,距离B 0.8英里,距离A:1.1英里。不包括在内 所以A,B和C将组合在一起。

我有这个工作,但我遇到一个问题,我不能得到一组10分,因为有一点可能会阻止少数其他点符合标准。我该如何前进以实现我的最终目标?谢谢!

import csv
from geopy import distance

flag = 0
LocationIDs = []
Latitudes = []
Longitudes = []

with open('data.csv') as data:
    dataRead = csv.reader(data)
    for row in dataRead:
        LocationIDs.append(row[0])
        Latitudes.append(row[1])
        Longitudes.append(row[2])

group = [LocationIDs[0]]
groupLat = [Latitudes[0]]
groupLong = [Longitudes[0]]
LocationIDs.remove(LocationIDs[0])
Latitudes.remove(Latitudes[0])
Longitudes.remove(Longitudes[0])

for x in range(len(LocationIDs)):
    flag = 0
    for y in range(len(group)):
        currGroupLoc = (groupLat[y], groupLong[y])
        dist = distance.distance(currGroupLoc, (Latitudes[x], Longitudes[x])).miles
        print("Testing Location " + str(LocationIDs[x]) + " against " + str(group[y]))
        print(dist)
        if dist < 15:
            flag = 1

    if flag == 0 and dist < 18:
        print("Adding " + str(LocationIDs[x]))
        group.append(LocationIDs[x])
        groupLat.append(Latitudes[x])
        groupLong.append(Longitudes[x])
        LocationIDs.remove(LocationIDs[x])
        Latitudes.remove(Latitudes[x])
        Longitudes.remove(Longitudes[x])

1 个答案:

答案 0 :(得分:1)

这个问题相当棘手。我可能误解了OP的意图,所以我将在这里陈述我对这项任务的理解:

  

给定一组(3D)坐标,找到所有的点组,其中组中的各个点之间具有最小的欧几里德间隔距离。

我不确定OP是否想要最大的组(这有点容易),或者所有组,但为了将来的参考,我将解决所有组的情况。

首先,我们为每个点找到满足最小距离标准的其他点的索引:

from scipy.spatial.distance import cdist

coords = [[1,2,3],[2,3,4],[3,4,5],[5,4,3],[3,4,5]]
boolean = cdist(coords, coords) > 2
matrix = [[] for __ in range(len(boolean))]
for row_i, row in enumerate(boolean):
    matrix[row_i] = list([i for i, is_true in enumerate(row) if is_true])
print(matrix)    # [[2, 3, 4], [3], [0, 3], [0, 1, 2, 4], [0, 3]]

接下来(这里是棘手的一点),我们找到符合标准的所有可能的组:

from copy import deepcopy

def _remove_from_rows(element, m):
    for key, values in m.items():
        m[key] = list([val for val in values if val != element])
    return m

def _remove_rows_without(value, m):
    m_ = m.copy()
    for key, values in m_.items():
        if value not in values:
            del m[key]
    return m

def _remove_smaller_keys(value, m):
    keys = sorted(m.keys())
    for key in keys:
        if key < value:
            del m[key]
        else:
            break
    return m

def recursive(m, group, clusters):
    key = group[-1]
    m_ = deepcopy(m)

    values = m_.pop(key)
    m_ = _remove_rows_without(key, m_)
    m_ = _remove_from_rows(key, m_)
    m_ = _remove_smaller_keys(key, m_)

    if not m_:
        return group
    for value in values:
        if value not in m_ or value < key:
            continue
        group_ = group + [value]
        clusters.append(recursive(m_, group_, clusters))
    return []

def start(m):
    groups = []
    for i in range(len(m)):
        group = [i]
        output = recursive(m, group, groups)
        del m[i]
        if output:
            groups.append(output)
    unique_groups = [val for val in groups if not
                 any(set(val) < set(i) for i in groups)]
    return unique_groups

matrix_ = [[1, 2, 4, 5, 7, 9],
          [0, 2, 5, 6, 8, 9],
          [0, 1, 3, 5, 6, 8],
          [2, 4, 6, 7, 8],
          [0, 3, 5, 6, 8, 9],
          [0, 1, 2, 4, 6, 7],
          [1, 2, 3, 4, 7, 8],
          [0, 3, 5, 6, 9],
          [1, 2, 3, 4, 6, 9],
          [0, 1, 4, 7, 8]]

matrix = dict()
for i, values in enumerate(matrix_):
    matrix[i] = values

print(start(matrix))
# [[0, 1, 2, 5], [0, 1, 9], [0, 4, 5], [0, 4, 9], [0, 5, 7], [0, 7, 9], 
#  [1, 2, 6, 8], [1, 8, 9], [2, 3, 6, 8], [3, 4, 6, 8], [3, 6, 7], [4, 8, 9]]

我使用了更大的合成矩阵数据集来提供足够的测试,并手动验证结果。现在了解详情。

对于每个点(每行),矩阵表示至少具有它们之间的最小距离的其他点的索引。因此存在对称性,因为如果点2(matrix[2])的值为7,则点7(matrix[7])必须具有值2.我在这里使用从零开始的索引(为了清楚起见,存在点0)。

通过首先在纸上编写算法,我发现更容易理解代码的实现。考虑到前面提到的对称性,我通过这种方式接近了分组任务:

对于每个row_i,在matrix.items()中输入行:

[0] => [1, 2, 4, 5, 7, 9]

获取将row_i作为值的所有其他行,并从每行中删除row_i:

1: [2, 5, 6, 8, 9]
2: [1, 3, 5, 6, 8]
4: [3, 5, 6, 8, 9]
5: [1, 2, 4, 6, 7]
7: [3, 5, 6, 9]
9: [1, 4, 7, 8]

现在的小组现在是[0]

接下来,如果矩阵子集中存在与indice对应的行,则将row_i行上的索引分别添加到group。我们看到,对于1,它存在于第0行,并在下面的矩阵子集中表示。因此,我们形成组[0, 1],它现在在第1行中具有索引,但是所有值都小于行indice(1)。这与我们的第1行无关,因为0和1之间的分离是1,但是对于后面的步骤是必要的。

[0, 1] => [2, 5, 6, 8, 9]
2: [3, 5, 6, 8]
5: [2, 4, 6, 7]
9: [4, 7, 8]

可以看出,我们删除了第4行和第7行,因为它们的行中没有1行。这种表示是因为,如果我们为第0和第1点分配一个组,因为第4和第7点比第1点的最小距离更近,第4和第7点不能参与这个特定的组。

我们重复该过程,注意对于row_i行([0, 1] => [2, 5, 6, 8, 9]),只应将对应行在下面的矩阵子集中表示的索引添加到该组中。

为了演示,我将完成整个过程。

[0, 1, 2] => [3, 5, 6, 8]
5: [4, 6, 7]

[0, 1, 2, 5] => [6, 7]

由于矩阵子集现已耗尽,我们已完成组[0, 1, 2, 5]。当矩阵子集用尽时,或者当row_i行中没有索引在matrix_subset中具有相应的表示时,或者当row_i行为空时,该组完成。这一切都对应于相同的物理情况,即没有更多的点可以保持与组中所有现有点的最小距离。请记住,我们必须对row_i行的所有值重复此过程,这意味着我们需要执行[0, 2][0, 4],....但是,您应该发现,当您沿着行向下时,搜索空间至少以二次方式减小,这主要是因为我们丢弃了矩阵子集中行中所有点的小于row_i的步骤(由于如上所述的对称性) )。

一旦很好地理解了算法,代码实现就相当简单(但仍然很难实现)。我不确定哪些部分需要一些解释,所以如果有任何你觉得丢失的地方,请在评论中写一下。尽管先尝试算出算法。

我认为实施是正确的,虽然它绝对不是为效率而设计的,如果您发现任何错误请做评论,谢谢!

思考:我有一种强烈的感觉,在某个模糊(或可能不是)库的某个地方,整个块可以被一个单行代替。哦,井。