【聚类】一种自适应Eps和Minpts的DBSCAN方法(python实现)

一、算法来源

1、DBSCAN算法原型

       这个算法原型非常简单,有很多博主都有写,大家自己去看看就好了,也不用花太多时间,顶多十分钟就能了解个大概。

2、自适应Eps和Minpts参数

       由于该算法对Eps和Minpts参数十分敏感,所以如何确定这两个参数对于DBSCAN来说是很重要的一步,这篇博文是基于李文杰老师的论文《自适应确定DBSCAN算法参数的算法研究》,通过这篇论文,输入数据集即可大致确定这两个参数,从而可以直接在DBSCAN中应用。

二、大致思想

        参考论文中提出的,根据数据集提取出Eps候选项(按从小到大排列),然后再提取出Minpts候选项,随后用这些候选项尝试使用DBSCAN算法进行聚类,如果连续的候选项聚类的类别数目相同,那么选择Eps相对较大的那个最为最终参数输入到DBSCAN算法中去。

        论文中认为如果连续3个Eps候选项聚类的类别数目相同,那么可以认为数据集在这些参数下逐渐收敛,但是我觉得具体最好看图像是否收敛,所以我就简单的在程序里将聚类数目打印出来,读者可以自行选择聚利时使用的Eps和Minpts参数。

三、程序介绍

0、头文件import

import math
import copy
import numpy as np
from sklearn.cluster import DBSCAN

1、首先定义聚类的“距离”,这里将欧式距离作为聚类距离

def dist(a,b):
    """
    :param a: 样本点
    :param b: 样本点
    :return: 两个样本点之间的欧式距离
    """
    return math.sqrt(math.pow(a[0]-b[0],2) + math.pow(a[1]-b[1],2))

2、根据论文求解Eps和Minpts候选项列表

def returnDk(matrix,k):
    """
    :param matrix: 距离矩阵
    :param k: 第k最近
    :return: 第k最近距离集合
    """
    Dk = []
    for i in range(len(matrix)):
        Dk.append(matrix[i][k])
    return Dk


def returnDkAverage(Dk):
    """
    :param Dk: k-最近距离集合
    :return: Dk的平均值
    """
    sum = 0
    for i in range(len(Dk)):
        sum = sum + Dk[i]
    return sum/len(Dk)


def CalculateDistMatrix(dataset):
    """
    :param dataset: 数据集
    :return: 距离矩阵
    """
    DistMatrix = [[0 for j in range(len(dataset))] for i in range(len(dataset))]
    for i in range(len(dataset)):
        for j in range(len(dataset)):
            DistMatrix[i][j] = dist(dataset[i], dataset[j])
    return DistMatrix


def returnEpsCandidate(dataSet):
    """
    :param dataSet: 数据集
    :return: eps候选集合
    """
    DistMatrix = CalculateDistMatrix(dataSet)
    tmp_matrix = copy.deepcopy(DistMatrix)
    for i in range(len(tmp_matrix)):
        tmp_matrix[i].sort()
    EpsCandidate = []
    for k in range(1,len(dataSet)):
        Dk = returnDk(tmp_matrix,k)
        DkAverage = returnDkAverage(Dk)
        EpsCandidate.append(DkAverage)
    return EpsCandidate


def returnMinptsCandidate(DistMatrix,EpsCandidate):
    """
    :param DistMatrix: 距离矩阵
    :param EpsCandidate: Eps候选列表
    :return: Minpts候选列表
    """
    MinptsCandidate = []
    for k in range(len(EpsCandidate)):
        tmp_eps = EpsCandidate[k]
        tmp_count = 0
        for i in range(len(DistMatrix)):
            for j in range(len(DistMatrix[i])):
                if DistMatrix[i][j] <= tmp_eps:
                    tmp_count = tmp_count + 1
        MinptsCandidate.append(tmp_count/len(dataSet))
    return MinptsCandidate

3、求解出聚类类别数列表

def returnClusterNumberList(dataset,EpsCandidate,MinptsCandidate):
    """
    :param dataset: 数据集
    :param EpsCandidate: Eps候选列表
    :param MinptsCandidate: Minpts候选列表
    :return: 聚类数量列表
    """
    np_dataset = np.array(dataset)  #将dataset转换成numpy_array的形式
    ClusterNumberList = []
    for i in range(len(EpsCandidate)):
        clustering = DBSCAN(eps= EpsCandidate[i],min_samples= MinptsCandidate[i]).fit(np_dataset)
        num_clustering = max(clustering.labels_)
        ClusterNumberList.append(num_clustering)
    return ClusterNumberList

 四、程序(完全版)

import math
import copy
import numpy as np
from sklearn.cluster import DBSCAN


def loadDataSet(fileName, splitChar='\t'):
    """
    输入:文件名
    输出:数据集
    描述:从文件读入数据集
    """
    dataSet = []
    with open(fileName) as fr:
        for line in fr.readlines():
            curline = line.strip().split(splitChar)
            fltline = list(map(float, curline))
            dataSet.append(fltline)
    return dataSet


def dist(a,b):
    """
    :param a: 样本点
    :param b: 样本点
    :return: 两个样本点之间的欧式距离
    """
    return math.sqrt(math.pow(a[0]-b[0],2) + math.pow(a[1]-b[1],2))


def returnDk(matrix,k):
    """
    :param matrix: 距离矩阵
    :param k: 第k最近
    :return: 第k最近距离集合
    """
    Dk = []
    for i in range(len(matrix)):
        Dk.append(matrix[i][k])
    return Dk


def returnDkAverage(Dk):
    """
    :param Dk: k-最近距离集合
    :return: Dk的平均值
    """
    sum = 0
    for i in range(len(Dk)):
        sum = sum + Dk[i]
    return sum/len(Dk)


def CalculateDistMatrix(dataset):
    """
    :param dataset: 数据集
    :return: 距离矩阵
    """
    DistMatrix = [[0 for j in range(len(dataset))] for i in range(len(dataset))]
    for i in range(len(dataset)):
        for j in range(len(dataset)):
            DistMatrix[i][j] = dist(dataset[i], dataset[j])
    return DistMatrix


def returnEpsCandidate(dataSet):
    """
    :param dataSet: 数据集
    :return: eps候选集合
    """
    DistMatrix = CalculateDistMatrix(dataSet)
    tmp_matrix = copy.deepcopy(DistMatrix)
    for i in range(len(tmp_matrix)):
        tmp_matrix[i].sort()
    EpsCandidate = []
    for k in range(1,len(dataSet)):
        Dk = returnDk(tmp_matrix,k)
        DkAverage = returnDkAverage(Dk)
        EpsCandidate.append(DkAverage)
    return EpsCandidate


def returnMinptsCandidate(DistMatrix,EpsCandidate):
    """
    :param DistMatrix: 距离矩阵
    :param EpsCandidate: Eps候选列表
    :return: Minpts候选列表
    """
    MinptsCandidate = []
    for k in range(len(EpsCandidate)):
        tmp_eps = EpsCandidate[k]
        tmp_count = 0
        for i in range(len(DistMatrix)):
            for j in range(len(DistMatrix[i])):
                if DistMatrix[i][j] <= tmp_eps:
                    tmp_count = tmp_count + 1
        MinptsCandidate.append(tmp_count/len(dataSet))
    return MinptsCandidate


def returnClusterNumberList(dataset,EpsCandidate,MinptsCandidate):
    """
    :param dataset: 数据集
    :param EpsCandidate: Eps候选列表
    :param MinptsCandidate: Minpts候选列表
    :return: 聚类数量列表
    """
    np_dataset = np.array(dataset)  #将dataset转换成numpy_array的形式
    ClusterNumberList = []
    for i in range(len(EpsCandidate)):
        clustering = DBSCAN(eps= EpsCandidate[i],min_samples= MinptsCandidate[i]).fit(np_dataset)
        num_clustering = max(clustering.labels_)
        ClusterNumberList.append(num_clustering)
    return ClusterNumberList

if __name__ == '__main__':
    dataSet = loadDataSet('788points.txt', splitChar=',')
    EpsCandidate = returnEpsCandidate(dataSet)
    DistMatrix = CalculateDistMatrix(dataSet)
    MinptsCandidate = returnMinptsCandidate(DistMatrix,EpsCandidate)
    ClusterNumberList = returnClusterNumberList(dataSet,EpsCandidate,MinptsCandidate)
    print(EpsCandidate)
    print(MinptsCandidate)
    print('cluster number list is')
    print(ClusterNumberList)

五、相关文件

我会将txt文件和程序放在以下位置:

https://download.youkuaiyun.com/download/liyihao17/11125093

https://download.youkuaiyun.com/download/liyihao17/11125098

另外我也在github上放了

https://github.com/liyihao17/KANN-DBSCAN

需要的读者可以自行下载,相关论文读者可以自行去知网搜索下载

在处理不平衡数据集的分类问题中,我们可以利用DBSCAN (Density-Based Spatial Clustering of Applications with Noise) 聚类算法并结合自适应类别权重来提升对稀少类别的敏感度。DBSCAN一种基于密度而非距离的聚类算法,适合发现任意形状的簇。 为了实现这个目标,我们通常会调整核心半径(`eps`)邻域点数(`min_samples`)等参数,以更好地适应不同类别的分布。同时,可以计算每个类别的样本密度,作为其在聚类过程中的权重。以下是一个简单的Python示例,使用scikit-learn库实现DBSCAN自适应地应用类别权重: ```python from sklearn.cluster import DBSCAN import numpy as np # 假设data是你的数据矩阵,y是标签列表 data = ... # 数据 y = ... # 标签 # 计算类别频率,用于计算权重 class_counts = np.bincount(y) # 获取最大类别数量,这将作为类别权重的倒数 max_class = max(class_counts) weights = [1 / count for count in class_counts] # 自定义DBSCAN函数,包含类别权重 def weighted_DBSCAN(data, eps, min_samples, weights): dbscan = DBSCAN(eps=eps, min_samples=min_samples, metric='euclidean') db_labels = dbscan.fit_predict(data, sample_weight=weights) return db_labels # 设置DBSCAN参数 eps = ... # 核心半径 min_samples = ... # 邻域点数 # 进行聚类 cluster_labels = weighted_DBSCAN(data, eps, min_samples, weights) # 测试分类结果 unique_labels, counts = np.unique(cluster_labels, return_counts=True) print("Unique clusters:", unique_labels) print("Counts per cluster:", dict(zip(unique_labels, counts)))
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值