DBSCAN算法简介
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)算法是一种基于密度的聚类算法,可以用于将不同密度的数据点划分到不同的类别中。它不需要事先指定聚类的个数,而是通过设置参数来控制聚类的结果。DBSCAN的基本思想是:对于给定半径内的数据点密度达到指定阈值的点被认为是核心点,没有达到阈值但存在于核心点半径范围内的点被认为是边界点,而既不是核心点也不是边界点的点被认为是噪声点。通过对核心点和边界点的相邻点进行扩展,可以将其归为同一类别。
DBSCAN算法的优点是不需要事先知道聚类个数,能够识别任意形状的聚类簇,并且能够将噪声点排除在聚类之外。其缺点是对于密度变化较大的数据集容易出现聚类间距离不连续的情况,参数的选择对聚类结果影响较大。
DBSCAN算法python代码
以下是使用Python实现的DBSCAN聚类算法代码:
import numpy as np
def DBSCAN(data, eps, minPts):
"""
:param data: 输入数据,是一个n行d列的矩阵,其中n表示数据样本的个数,d表示数据的维度
:param eps: 半径阈值
:param minPts: 最小样本数阈值
:return: 各样本所属簇的标记
"""
n = data.shape[0] # 样本个数
visited = np.zeros(n) # 样本是否被访问过
clusters = np.zeros(n) # 各样本所属簇的标记
cluster_idx = 0 # 簇标记
for i in range(n):
if visited[i] == 0:
visited[i] = 1
neighbors = find_neighbors(data, i, eps) # 找到半径内的邻居
if len(neighbors) >= minPts: # 如果邻居数目大于等于阈值,将其加入当前簇
clusters[i] = cluster_idx
expand_cluster(data, neighbors, visited, clusters, cluster_idx, eps, minPts)
cluster_idx += 1 # 新建一个簇
return clusters
def expand_cluster(data, neighbors, visited, clusters, cluster_idx, eps, minPts):
"""
:param data: 输入数据,是一个n行d列的矩阵,其中n表示数据样本的个数,d表示数据的维度
:param neighbors: 核心对象的eps半径内的邻居
:param visited: 样本是否被访问过
:param clusters: 各样本所属簇的标记
:param cluster_idx: 当前簇的标记
:param eps: 半径阈值
:param minPts: 最小样本数阈值
"""
for i in neighbors:
if visited[i] == 0:
visited[i] = 1
neighbors_i = find_neighbors(data, i, eps) # 找到i点半径内的邻居
if len(neighbors_i) >= minPts: # 如果i点邻居数目大于等于阈值,将其加入当前簇
clusters[i] = cluster_idx
neighbors = np.concatenate((neighbors, neighbors_i)) # 将邻居加入列表中
if clusters[i] == 0: # 如果当前点不属于任何簇,加入当前簇
clusters[i] = cluster_idx
def find_neighbors(data, idx, eps):
"""
:param data: 输入数据,是一个n行d列的矩阵,其中n表示数据样本的个数,d表示数据的维度
:param idx: 目标样本的索引
:param eps: 半径阈值
:return: 目标样本的eps半径内的邻居的索引列表
"""
neighbors = []
for i in range(data.shape[0]):
if np.linalg.norm(data[idx] - data[i]) <= eps: # 计算两点间的欧式距离,判断是否在半径内
neighbors.append(i)
return neighbors
其中,主要的函数是DBSCAN函数,它接收输入数据、半径阈值和最小样本数阈值。首先,初始化visited和clusters两个矩阵,并设定簇标记为0。然后,对于每个样本,如果该样本未被访问过,就标记为已访问,找到该点半径内的邻居,并根据邻居数目是否大于等于阈值,将其加入当前簇或新建一个簇。在扩展簇的过程中,将邻居加入列表,如果邻居数目大于等于阈值,将邻居加入当前簇。如果当前点不属于任何簇,加入当前簇。
函数find_neighbors用于找到目标样本的eps半径内的邻居。如果两点间的欧式距离小于等于半径阈值eps,则认为这两个点是邻居。
DBSCAN算法C代码
以下是使用C语言实现DBSCAN聚类算法的示例代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_POINTS 1000
#define EPSILON 2.0
#define MIN_POINTS 5
struct point {
float x, y;
};
int main() {
/* Read input points */
int i, j, k, n;
struct point input[MAX_POINTS];
scanf("%d", &n);
for (i = 0; i < n; i++)
scanf("%f %f", &input[i].x, &input[i].y);
/* Compute distances */
float dist[MAX_POINTS][MAX_POINTS];
for (i = 0; i < n; i++) {
dist[i][i] = 0;
for (j = i+1; j < n; j++) {
dist[i][j] = sqrt(pow(input[i].x - input[j].x, 2) +
pow(input[i].y - input[j].y, 2));
dist[j][i] = dist[i][j];
}
}
/* DBSCAN algorithm */
int visited[MAX_POINTS], clusters[MAX_POINTS];
int clusterCount = 0;
for (i = 0; i < n; i++) {
if (visited[i])
continue;
visited[i] = 1;
int neighbours[MAX_POINTS], neighbourCount = 0;
for (j = 0; j < n; j++) {
if (dist[i][j] < EPSILON)
neighbours[neighbourCount++] = j;
}
if (neighbourCount < MIN_POINTS) {
clusters[i] = -1;
continue;
}
clusters[i] = ++clusterCount;
for (j = 0; j < neighbourCount; j++) {
int neighbour = neighbours[j];
if (visited[neighbour])
continue;
visited[neighbour] = 1;
int neighbourNeighbours[MAX_POINTS], neighbourNeighbourCount = 0;
for (k = 0; k < n; k++) {
if (dist[neighbour][k] < EPSILON)
neighbourNeighbours[neighbourNeighbourCount++] = k;
}
if (neighbourNeighbourCount >= MIN_POINTS) {
for (k = 0; k < neighbourNeighbourCount; k++) {
int nn = neighbourNeighbours[k];
if (clusters[nn] == -1 || clusters[nn] == 0) {
clusters[nn] = clusterCount;
}
}
}
}
}
/* Print results */
printf("Clusters:\n");
for (i = 0; i < n; i++) {
printf("%d ", clusters[i]);
}
printf("\n");
return 0;
}
上面的代码实现了一个简单的DBSCAN聚类算法,假定输入的数据点以二维平面坐标的形式给出,并使用欧几里得距离计算点之间的距离。在输入数据之后,代码会遍历每一个未访问的点,并使用DBSCAN算法将它们归类到簇中。输出结果是一个整数数组,每个元素代表对应数据点所属的簇编号,簇编号为-1表示该点是噪音点。