Python:三维空间中的DBSCAN

时间:2014-10-07 21:59:38

标签: python cluster-analysis dbscan

我一直在寻找DBSCAN实现三维点而没有太多运气。有谁知道我的图书馆处理这个或有任何经验这样做?我假设DBSCAN算法可以处理3个维度,通过使e​​值为半径度量和通过欧几里德分离测量的点之间的距离。如果有人试图实现这一点,并希望分享,也将非常感谢,谢谢。

4 个答案:

答案 0 :(得分:4)

您可以将sklearn用于DBSCAN。这是一些适合我的代码 -

from sklearn.cluster import DBSCAN
import numpy as np
data = np.random.rand(500,3)

db = DBSCAN(eps=0.12, min_samples=1).fit(data)
labels = db.labels_
from collections import Counter
Counter(labels)

我得到的输出是 -

Counter({1: 342, 10: 30, 31: 13, 13: 11, 30: 10, 24: 5, 29: 5, 2: 4, 18: 4,
19: 4, 28: 4, 49: 4, 3: 3, 17: 3, 23: 3, 32: 3, 7: 2, 9: 2, 12: 2, 14: 2, 15: 2,
16: 2, 20: 2, 21: 2, 26: 2, 39: 2, 41: 2, 46: 2, 0: 1, 4: 1, 5: 1, 6: 1, 8: 1, 11:
1, 22: 1, 25: 1, 27: 1, 33: 1, 34: 1, 35: 1, 36: 1, 37: 1, 38: 1, 40: 1, 42: 1,
43: 1, 44: 1, 45: 1, 47: 1, 48: 1, 50: 1, 51: 1, 52: 1, 53: 1, 54: 1, 55: 1})

因此,群集识别出55个群集,其中包含每个群集中点数的计数,如上所示。

答案 1 :(得分:3)

所以这就是我提出的,我知道它不是最有效的实现,但它有效;例如,区域查询是算法的主要时间,它不止一次地计算两点之间的距离,而不是仅存储它以供以后使用。

class DBSCAN(object):

def __init__(self, eps=0, min_points=2):
    self.eps = eps
    self.min_points = min_points
    self.visited = []
    self.noise = []
    self.clusters = []
    self.dp = []

def cluster(self, data_points):
    self.visited = []
    self.dp = data_points
    c = 0
    for point in data_points:
        if point not in self.visited:
            self.visited.append(point)
            neighbours = self.region_query(point)
            if len(neighbours) < self.min_points:
                self.noise.append(point)
            else:
                c += 1
                self.expand_cluster(c, neighbours)

def expand_cluster(self, cluster_number, p_neighbours):
    cluster = ("Cluster: %d" % cluster_number, [])
    self.clusters.append(cluster)
    new_points = p_neighbours
    while new_points:
        new_points = self.pool(cluster, new_points)

def region_query(self, p):
    result = []
    for d in self.dp:
        distance = (((d[0] - p[0])**2 + (d[1] - p[1])**2 + (d[2] - p[2])**2)**0.5)
        if distance <= self.eps:
            result.append(d)
    return result

def pool(self, cluster, p_neighbours):
    new_neighbours = []
    for n in p_neighbours:
        if n not in self.visited:
            self.visited.append(n)
            n_neighbours = self.region_query(n)
            if len(n_neighbours) >= self.min_points:
                new_neighbours = self.unexplored(p_neighbours, n_neighbours)
        for c in self.clusters:
            if n not in c[1] and n not in cluster[1]:
                cluster[1].append(n)
    return new_neighbours

@staticmethod
def unexplored(x, y):
    z = []
    for p in y:
        if p not in x:
            z.append(p)
    return z

答案 2 :(得分:0)

在使用第一个答案中提供的代码一段时间之后我得出结论它有重大问题: 1)噪声点可以出现在以后的簇中。 2)它会抛出额外的聚类,这些聚类是先前构建的聚类的子集,因为会计访问和未探测的点导致聚类小于min_points的问题,以及 3)一些点最终可以在两个集群中 - 它们可以从两个集群中到达,并且在这个代码中甚至可以是其中一个集群的核心点。官方DBSCAN算法将任何点作为核心点放置在集群中,它是核心的一部分,但放置的点只能从第一个集群中的两个集群到达,它们是可以从中找到的。这使得这些点的聚类取决于数据中点的顺序,但所有点在输出数据中仅出现一次 - 在单个簇中或作为噪声。某些应用程序希望将可从两个群集访问的这些共享点放置在两个群集中,但核心点应仅出现在一个群集中。

所以这就是我想出来的。它计算两点之间的间隔距离两次并且不使用任何树,但它会立即消除没有近邻的点,并创建一个核心点列表,因此在构建核心时只需要考虑这些点。它利用集合进行包含测试请注意,此实现确实可以在所有可以访问的集群中放置共享点

 class DBSCAN(object):
    def __init__(self, eps=0, min_points=2):
        self.eps = eps
        self.min_points = min_points
        self.noise = []
        self.clusters = []
        self.dp = []
        self.near_neighbours = []
        self.wp = set()
        self.proto_cores = set()

    def cluster(self, points):
        c = 0
        self.dp = points
        self.near_neighbours = self.nn(points)
        while self.proto_cores:
            near_points = set(self.proto_cores)
            for p in near_points:
                c += 1
                core = self.add_core(self.near_neighbours[p])
                complete_cluster = self.expand_cluster(core)
                self.clusters.append(["Cluster: %d" % c, complete_cluster])
                self.proto_cores -= core
                break
        for pt in self.dp:
            flag = True
            for c in self.clusters:
                if pt in c[1]:
                    flag = False
            if flag:
                self.noise.append(pt)

    # set up dictionary of near neighbours,create working_point and proto_core sets
    def nn(self, points):
        self.wp = set()
        self.proto_cores = set()
        i = -1
        near_neighbours = {}
        for p in points:
            i += 1
            j = -1
            neighbours = []
            for q in points:
                j += 1
                distance = (((q[0] - p[0]) ** 2 + (q[1] - p[1]) ** 2
                             + (q[2] - p[2]) ** 2) ** 0.5)
                if distance <= self.eps:
                    neighbours.append(j)
            near_neighbours[i] = neighbours
            if len(near_neighbours[i]) > 1:
                self.wp |= {i}
            if len(near_neighbours[i]) >= self.min_points:
                self.proto_cores |= {i}
        return near_neighbours

    # add cluster core points
    def add_core(self, neighbours):
        core_points = set(neighbours)
        visited = set()
        while neighbours:
            points = set(neighbours)
            neighbours = set()
            for p in points:
                visited |= {p}
                if len(self.near_neighbours[p]) >= self.min_points:
                    core_points |= set(self.near_neighbours[p])
                    neighbours |= set(self.near_neighbours[p])
            neighbours -= visited
        return core_points

    # expand cluster to reachable points and rebuild actual point values
    def expand_cluster(self, core):
        core_points = set(core)
        full_cluster = []
        for p in core_points:
            core |= set(self.near_neighbours[p])
        for point_number in core:
            full_cluster.append(self.dp[point_number])
        return full_cluster

答案 3 :(得分:0)

为什么不直接使用 PCA 将数据展平为 2 维,而使用只有 2 维的 DBSCAN?似乎比尝试自定义构建其他东西更容易。