学习日记_20241115_聚类方法(DBSCAN)
前言
提醒:
文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。
其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展及意见建议,欢迎评论区讨论交流。
文章目录
聚类算法
聚类算法在各种领域中有广泛的应用,主要用于发现数据中的自然分组和模式。以下是一些常见的应用场景以及每种算法的优缺点:
经典应用场景
-
市场细分:根据消费者的行为和特征,将他们分成不同的群体,以便进行有针对性的营销。
-
图像分割: 将图像划分为多个区域或对象,以便进行进一步的分析或处理。
-
社交网络分析:识别社交网络中的社区结构。
-
文档分类:自动将文档分组到不同的主题或类别中。
-
异常检测识别数据中的异常点或异常行为。
-
基因表达分析:在生物信息学中,根据基因表达模式对基因进行聚类。
层次聚类
DBSCAN
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法,广泛应用于数据挖掘和模式识别。它具有一些显著的优点和缺点,下面我将分别介绍。优点
无需指定簇的数量:与 K-means 等方法不同,DBSCAN 不需要用户事先指定聚类的数量。它能够根据数据的分布自动识别簇的数量。
处理任意形状的簇:DBSCAN 可以发现任意形状的簇,而不仅限于球形。这使得它在处理复杂数据集时表现优良。
对噪声的鲁棒性:DBSCAN 可以有效识别并处理噪声点,将其标记为离群点,而不是将其归入某个簇中。
适用于大规模数据集:在某些实现中,DBSCAN 可以利用索引结构(如 KD 树或球树)来加速邻域查询,从而在处理大规模数据集时提高效率。
可扩展性:DBSCAN 可以通过调整参数(如 ε 和 MinPts)来适应不同的数据特征,使其具有一定的灵活性。
缺点
参数敏感性:DBSCAN 的性能高度依赖于 ε(邻域半径)和 MinPts(核心点的最小邻居数)的选择。选择不当可能导致聚类效果不佳。
处理不同密度簇的能力:对于具有不同密度的簇,DBSCAN 可能难以找到合适的参数,导致高密度簇合并或低密度簇被错误地分类为噪声。
计算复杂度:虽然 DBSCAN 的平均时间复杂度为 O(n log n),但在最坏情况下,使用暴力搜索时可能达到 O(n^2),尤其是在数据量很大时会影响效率。
高维数据问题:在高维空间中,距离度量的有效性下降,DBSCAN 的性能可能受到影响,可能导致无法找到足够的邻域来形成核心点。
噪声点分类问题:边界点可能会被错误分类为噪声或归入不恰当的簇,从而影响聚类的结果。
不支持在线学习:DBSCAN 主要用于静态数据集,对于动态数据或实时更新的数据,无法有效进行增量更新,必须重新计算整个数据集的聚类。
总结
DBSCAN 是一种强大的聚类算法,适用于发现复杂形状的簇和处理噪声。然而,在使用时需要谨慎选择参数,并注意其对不同密度簇和高维数据的处理能力。根据具体应用场景,可能需要结合其他聚类方法以获得更好的结果。
简单实例(函数库实现)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.cluster import DBSCAN
# 生成示例数据集(两个半月形状)
X, y = make_moons(n_samples=300, noise=0.1, random_state=42)
# 创建 DBSCAN 聚类模型
# 创建 DBSCAN 模型,并指定 eps(邻域的半径)和 min_samples(核心点的最小邻居数)。
dbscan = DBSCAN(eps=0.2, min_samples=5)
# 拟合模型并预测聚类标签
labels = dbscan.fit_predict(X)
# 绘制聚类结果
# 使用 matplotlib 绘制聚类结果,不同的颜色表示不同的簇,黑色表示噪声点。
plt.figure(figsize=(10, 6))
unique_labels = set(labels)
colors = plt.cm.get_cmap('Spectral', len(unique_labels))
for k in unique_labels:
class_member_mask = (labels == k)
xy = X[class_member_mask]
plt.scatter(xy[:, 0], xy[:, 1], s=30, color=colors(k), label=f'Cluster {k}' if k != -1 else 'Noise')
plt.title('DBSCAN Clustering')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.legend()
plt.show()
#数据可视化
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
# 生成数据
X, y = make_moons(n_samples=300, noise=0.1, random_state=42)
# 绘制数据
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis', edgecolor='k', s=50)
plt.title("Moon Dataset")
plt.show()
数据分布:
运行结果:
代码分析
class_member_mask = (labels == k)
class_member_mask = (labels == k)
这一行代码的作用是创建一个布尔数组(mask),用于标识属于特定聚类的样本。让我们逐步解释这行代码的含义和作用。
label=f’Cluster {k}’ if k != -1 else ‘Noise’
label=f'Cluster {k}' if k != -1 else 'Noise'
label
参数用于设置当前散点图的图例标签。f'Cluster {k}'
是一个格式化字符串,用于指示当前绘制的是聚类k
。if k != -1
的条件是为了处理噪声点,噪声点的标签为 -1,因此如果当前聚类是噪声,标签将设置为'Noise'
。
数学解释
DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种基于密度的聚类算法。它的主要思想是通过密度来识别聚类,能够有效处理具有任意形状的聚类,并能够识别噪声点。以下是 DBSCAN 的主要概念和数学公式的详细解释。。
主要概念
- ε(epsilon):ε 是定义邻域的半径,表示一个点周围的邻域范围。
- MinPts:MinPts 是一个参数,表示在 ε 半径内需要至少有多少个点才能被认为是一个核心点。
- 核心点、边界点、噪声点:
- 核心点:如果一个点的 ε 邻域内的点的数量(包括自身)大于或等于 MinPts,那么这个点就是核心点。
- 边界点:如果一个点的 ε 邻域内的点的数量小于 MinPts,但该点在某个核心点的 ε 邻域内,那么该点就是边界点。
- 噪声点:如果一个点既不是核心点,也不是边界点,那么该点就是噪声点。
数学公式
- 邻域定义:
- 对于一个点 P P P,其 ε 邻域是指所有与 P P P 距离小于或等于 ε 的点集合,表示为:
N ϵ ( P ) = { Q ∈ D ∣ dist ( P , Q ) ≤ ϵ } N_\epsilon(P) = \{Q \in D | \text{dist}(P, Q) \leq \epsilon\} Nϵ(P)={Q∈D∣dist(P,Q)≤ϵ}
其中 D D D 是数据集, dist ( P , Q ) \text{dist}(P, Q) dist(P,Q) 是点 P P P 和点 Q Q Q 之间的距离(通常使用欧几里得距离)。
- 核心点的定义:
- 点 P P P 是核心点,当且仅当:
∣ N ϵ ( P ) ∣ ≥ MinPts |N_\epsilon(P)| \geq \text{MinPts} ∣Nϵ(P)∣≥MinPts
这里 ∣ N ϵ ( P ) ∣ |N_\epsilon(P)| ∣Nϵ(P)∣ 表示点 P P P 的 ε 邻域内的点的数量。
- 聚类的形成:
- 从任意一个核心点出发,通过递归地访问其邻域内的核心点,构建一个聚类。这个过程可以用以下方式描述:
- 对于每个核心点 P P P,访问其 ε 邻域内所有的点 Q Q Q:
- 如果 Q Q Q 也是核心点,则继续访问 Q Q Q 的 ε 邻域。实际步骤
- 选择一个未被访问的点 P P P。
- 检查 P P P 的 ε 邻域,如果是核心点,则开始一个新聚类。
- 将所有在 P P P ε 邻域内的点加入聚类,如果这些点是核心点,则继续扩展聚类。
- 重复步骤 1 到 3,直到没有未被访问的核心点。
总结
DBSCAN 是一种基于密度的聚类算法,它通过定义邻域和核心点来识别聚类和噪声,对于具有复杂形状和不同密度的聚类有良好的表现。通过合理选择 ε 和 MinPts 参数,可以使得 DBSCAN 在不同的数据集上获得良好的聚类效果。
手动实现
import numpy as np
from collections import deque
# 计算欧几里得距离
def euclidean_distance(p1, p2):
return np.linalg.norm(p1 - p2)
# 查找邻域点
def region_query(X, point_idx, eps):
neighbors = []
for idx, point in enumerate(X):
if euclidean_distance(X[point_idx], point) <= eps:
neighbors.append(idx)
return neighbors
# DBSCAN 算法
def dbscan(X, eps, min_pts):
n_points = X.shape[0]
labels = np.zeros(n_points) # 0: 未访问,-1: 噪声,其他: 聚类编号
cluster_id = 0
for point_idx in range(n_points):
if labels[point_idx] != 0: # 已经访问过的点
continue
# 查找邻域点
neighbors = region_query(X, point_idx, eps)
if len(neighbors) < min_pts:
labels[point_idx] = -1 # 噪声
else:
cluster_id += 1
labels[point_idx] = cluster_id
queue = deque(neighbors)
while queue:
current_point_idx = queue.popleft()
if labels[current_point_idx] == 0:
labels[current_point_idx] = cluster_id
current_neighbors = region_query(X, current_point_idx, eps)
if len(current_neighbors) >= min_pts:
queue.extend(current_neighbors)
elif labels[current_point_idx] == -1:
labels[current_point_idx] = cluster_id
return labels
# 示例数据生成
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
# 生成示例数据
X, _ = make_blobs(n_samples=300, centers=3, cluster_std=0.60, random_state=0)
# 使用 DBSCAN 进行聚类
eps = 0.5 # 邻域半径
min_pts = 5 # 最小邻居点数
labels = dbscan(X, eps, min_pts)
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1])
plt.title('X data')
# 可视化聚类结果
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis')
plt.title('DBSCAN Result')
plt.show()
结果为:
代码分析
np.linalg.norm(p1 - p2)
np.linalg.norm
函数可以用来计算向量或矩阵的范数即两个点之间的欧几里得距离。范数介绍
范数是数学中用于衡量向量“大小”的一个概念,它是向量空间中的一个函数,将每个向量映射到一个非负实数。在向量空间中,范数具有以下基本性质:
- 非负性:对于所有的向量 v \mathbf{v} v,范数 ∥ v ∥ \|\mathbf{v}\| ∥v∥ 都是非负的,且 ∥ v ∥ = 0 \|\mathbf{v}\| = 0 ∥v∥=0 当且仅当 v \mathbf{v} v 是零向量。
- 齐次性:对于所有的标量 α \alpha α 和向量 v \mathbf{v} v,有 ∥ α v ∥ = ∣ α ∣ ∥ v ∥ \|\alpha \mathbf{v}\| = |\alpha| \|\mathbf{v}\| ∥αv∥=∣α∣∥v∥。
- 三角不等式:对于所有的向量 u \mathbf{u} u 和 v \mathbf{v} v,有 ∥ u + v ∥ ≤ ∥ u ∥ + ∥ v ∥ \|\mathbf{u} + \mathbf{v}\| \leq \|\mathbf{u}\| + \|\mathbf{v}\| ∥u+v∥≤∥u∥+∥v∥。
以下是一些常见的范数类型:
- 欧几里得范数(L2范数)
对于 n n n 维向量 v = ( v 1 , v 2 , . . . , v n ) \mathbf{v} = (v_1, v_2, ..., v_n) v=(v1,v2,...,vn),其欧几里得范数定义为:
∥ v ∥ 2 = ∑ i = 1 n v i 2 \|\mathbf{v}\|_2 = \sqrt{\sum_{i=1}^{n} v_i^2} ∥v∥2=i=1∑nvi2
这是最常用的范数,对应于空间中两点之间的直线距离。- 曼哈顿范数(L1范数)
曼哈顿范数定义为向量各分量绝对值之和:
∥ v ∥ 1 = ∑ i = 1 n ∣ v i ∣ \|\mathbf{v}\|_1 = \sum_{i=1}^{n} |v_i| ∥v∥1=i=1∑n∣vi∣
这个范数对应于在城市街道上从一个点到另一个点的距离(只能沿着街道走)。- 无穷范数(L∞范数)
无穷范数定义为向量各分量绝对值中的最大值:
∥ v ∥ ∞ = max 1 ≤ i ≤ n ∣ v i ∣ \|\mathbf{v}\|_\infty = \max_{1 \leq i \leq n} |v_i| ∥v∥∞=1≤i≤nmax∣vi∣
这个范数表示向量在各个方向上可能达到的最大“延伸”。- p-范数(Lp范数)
更一般地,对于 p ≥ 1 p\geq 1 p≥1,向量 v \mathbf{v} v 的 p p p-范数定义为:
∥ v ∥ p = ( ∑ i = 1 n ∣ v i ∣ p ) 1 / p \|\mathbf{v}\|_p = \left( \sum_{i=1}^{n} |v_i|^p \right)^{1/p} ∥v∥p=(i=1∑n∣vi∣p)1/p
当 p = 2 p = 2 p=2 时, p p p-范数就是欧几里得范数;当 p = 1 p = 1 p=1 时, p p p-范数就是曼哈顿范数;当 p p p 趋向于无穷大时, p p p-范数就是无穷范数。
范数在数学、物理学、工程学、计算机科学等领域中都有广泛的应用。在机器学习中,范数常用于正则化,以防止模型过拟合。例如,L2正则化(岭回归)和L1正则化(Lasso)就是使用L2范数和L1范数来惩罚模型的复杂度。队列
collections
模块中的deque
(双端队列)对象提供了一系列方法,用于在队列的两端进行高效的添加、移除和访问元素的操作。以下是一些基本的deque
操作方法:
- 初始化:
from collections import deque
中d = deque([iterable])
建立队列对象。- 添加元素:
append(x)
:在队列的右侧(尾部)添加一个元素。appendleft(x)
:在队列的左侧(头部)添加一个元素。- 移除元素:
pop()
:移除并返回队列右侧(尾部)的元素,如果队列为空,抛出IndexError
。popleft()
:移除并返回队列左侧(头部)的元素,如果队列为空,抛出IndexError
。- 访问元素:
index(x)
:返回元素x
在队列中的位置(从0开始计数)count(x)
:返回元素x
在队列中出现的次数。- 旋转队列:
rotate(n=1)
:将队列中的元素向右旋转n
步,如果n
是负数,则向左旋转。- 扩展队列:
extend(iterable)
:在队列的右侧(尾部)添加多个元素,这些元素来自一个可迭代对象。extendleft(iterable)
:在队列的左侧(头部)添加多个元素,这些元素来自一个可迭代对象,注意元素会以相反的顺序添加。- 其他操作:
remove(value)
:移除队列中第一个出现的value
,如果不存在,抛出ValueError
。reverse()
:将队列中的元素顺序反转。clear()
:移除队列中的所有元素,使其变为空队列。- 队列长度:
len(d)
:返回队列中的元素数量。- 队列是否为空:
not d
:如果队列为空,返回True
,否则返回False
。- 迭代队列:
- 可以使用
for
循环直接迭代队列中的元素。
原文地址:https://blog.csdn.net/2301_81791289/article/details/143808163
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!