用约束聚类一维数组?

数据挖掘 scikit-学习 聚类 k-均值 数据库扫描
2022-03-07 00:06:17

我有以下类型的 1-d 数组数据来聚类,但有一些约束:

该数组的长度从 50 到 300,浮动,其中一些接近 0,一些远离。

目标:将数组分成 n 个簇,使接近 0 的值在一个簇中,而其他值在不同的簇中。

例如数组 1

[2.56, 5.02, 4.67, 3.14, 5.46, 3.07, 3.96, 4.21, 5, 2.12, 4.43, 5.51, 3.31, 2.98, 2.9, 4.66, 0.2, 1.24, 0.78, 1.41, 0.15, 0, 0, 1.58, 2.84, 0.9, 0.85, 1.69, 1.14, 0.74, -0.19, -0.38, 0.55, 0.17, -0.52, 0.52, 1.34, 0.19, 0, 1.72, 0.55, 0.98, -0.61, 0, -0.16, 1.53, 0.3, 0.39, 0.6, -0.31, -1.38, 0.39, 1.26, 0.47, -0.38, -0.48, 0, 0, 82.13, 0, 0, 97.17, 184.07, 185.12, 187.8, 167.22, 169.34, 165.76, 162.82, 187.24, 179.31, 189.49, 187.27, 179.29, 157.42, 0.24, -0.7, 1.23]

数组 1 的散点图: 数组 1 的散点图

例如数组 2

[2.7, 3.85, 3.08, 2.94, 2.98, 3.59, 3.13, 3.83, 3.25, 3.34, 3.73, 3.2, 2.77, 3.18, 3.62, 2.17, 3.29, 3.12, 3.98, 3.72, 3.87, 3.45, 3.21, 3.7, 4.5, 3.4, 3.67, 3.65, 3.65, 3.14, 3.94, 3.47, 3.03, 4.38, 3.01, 3.38, 4.06, 3.43, 3.81, 4.01, 2.96, 3.04, 3.51, 2.85, 3.84, 4.33, 2.81, 2.65, 2.66, 3.54, 4.89, 3.17, 3.46, 2.51, 3.36, -15.1, 3.12, 3.12, 3.63, 3.07, 5.48, 4.88, 4.3, 2.91, 0.3, 1.06, -0.1, 0.81, -0.62, 0.58, 1.22]

阵列 2 的散点图:数组 2 的散点图

对于数组 1,切片 [16:60](大约)应该被识别到接近 0 的集群中,并且 [0:16] 和 [60:] 应该是其他集群。

对于数组 2,切片 [64:](大约)应该被识别到接近 0 的集群中,而 [0:64] 是其他集群。

我从 sklearn.cluster lib 尝试了 kmeans 和 dbscan,但无法获得理想的结果。

kmeans:必须给它 n_clusters,这在我的情况下是不确定的。如果 num 小或大,集群的覆盖范围会比我想要的多或少。

dbscan:它受到 eps 和 min_samples 值的影响,这在我的情况下也是不确定的。

我目前的想法是对数组进行排序,找到数组中值的间隙并将每个间隙之间的值聚类。但是我没有得到一个可靠的实现。

对我的想法有任何其他想法或建议吗?

2个回答

根本不要使用集群。

定义一个阈值,并保存数据。完毕。

保持简单易懂。

好吧,您可以通过计算每个聚类中所有点的平均值之间的距离来对数据进行排序并找到距离最远的聚类。然后,使用树递归地执行此操作,将数据拆分成簇。但是,这种方法还需要最少数量的样本(我建议使用最少 2 个样本)。

这是一些关于如何在第一个示例数组上找到最佳拆分的代码 -

from itertools import tee
import numpy as np

def pairwise(iterable):
    """Generates pairwise elements
    s -> (s0,s1), (s1,s2), (s2, s3), ...
    """

    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

# Splits data on a point and returns distance of the means of the two splits
def split(arr, split_on):
        return np.abs(np.mean(arr[arr < split_on]) - np.mean(arr[arr > split_on]))

a = [2.56, 5.02, 4.67, 3.14, 5.46, 3.07, 3.96, 4.21, 5, 2.12, 4.43, 5.51, 3.31, 2.98, 2.9, 4.66, 0.2, 1.24, 0.78, 1.41, 0.15, 0, 0, 1.58, 2.84, 0.9, 0.85, 1.69, 1.14, 0.74, -0.19, -0.38, 0.55, 0.17, -0.52, 0.52, 1.34, 0.19, 0, 1.72, 0.55, 0.98, -0.61, 0, -0.16, 1.53, 0.3, 0.39, 0.6, -0.31, -1.38, 0.39, 1.26, 0.47, -0.38, -0.48, 0, 0, 82.13, 0, 0, 97.17, 184.07, 185.12, 187.8, 167.22, 169.34, 165.76, 162.82, 187.24, 179.31, 189.49, 187.27, 179.29, 157.42, 0.24, -0.7, 1.23]

# Sort the data
x = np.array(sorted(a))
# Find midpoints of data (generates splits for the data)
mids = [np.median(np.array(i)) for i in pairwise(x)]

max_dist = None
index = None
for mid in mids:
    if max_dist is None or max_dist < split_max(x, mid):
        max_dist = split_max(x, mid)
        index = mid
    else:
        pass


In [23]: x[x < index]
Out[23]: 
array([-1.38, -0.7 , -0.61, -0.52, -0.48, -0.38, -0.38, -0.31, -0.19,
       -0.16,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,  0.  ,
        0.15,  0.17,  0.19,  0.2 ,  0.24,  0.3 ,  0.39,  0.39,  0.47,
        0.52,  0.55,  0.55,  0.6 ,  0.74,  0.78,  0.85,  0.9 ,  0.98,
        1.14,  1.23,  1.24,  1.26,  1.34,  1.41,  1.53,  1.58,  1.69,
        1.72,  2.12,  2.56,  2.84,  2.9 ,  2.98,  3.07,  3.14,  3.31,
        3.96,  4.21,  4.43,  4.66,  4.67,  5.  ,  5.02,  5.46,  5.51,
       82.13, 97.17])

In [24]: x[x > index]
Out[24]: 
array([157.42, 162.82, 165.76, 167.22, 169.34, 179.29, 179.31, 184.07,
       185.12, 187.24, 187.27, 187.8 , 189.49])

当您完成构建一棵执行此操作的树时,您基本上只能选择那些均值接近 0 的叶子。这基本上是 a 的不同实现kd-tree这是使用kd-trees进行空间分区的链接。