Python中的线性/顺序保留聚类

时间:2019-01-24 14:57:12

标签: python cluster-analysis k-means

我想根据数字与邻居的比较数字将其分组在一个列表中,但是我想连续地进行此操作,并尽可能通过聚类。为了澄清,让我给你一个例子:

假设您有列表

lst = [10, 11.1, 30.4, 30.0, 32.9, 4.5, 7.2]

然后,如果我们有3个群组,则显而易见如何进行聚类。运行sklearn的k-means算法(参见代码)可以确认这一点。但是,当列表中的数字不是那么“方便”时,我就会遇到麻烦。假设您有列表:

lst = [10, 11.1, 30.4, 30.0, 32.9, 6.2, 31.2, 29.8, 12.3, 10.5]

我的问题现在有两个方面:

  1. 我想要某种“保留顺序的线性”聚类,它考虑了数据的顺序。对于上面的列表,聚类算法应为我提供所需的格式

    lst = [0,0,1,1,1,1,1,1,2,2]
    
  2. 如果您查看上面的输出,还看到我希望值6.2在第二个集群中集群,即,我希望集群算法将其视为异常值,而不是一个全新的集群。

  3. 编辑为澄清起见,我希望能够指定线性聚类过程中的聚类数量,即聚类的“最终总数”。

代码:

import numpy as np
from sklearn.cluster import KMeans

lst = [10, 11.1, 30.4, 30.0, 32.9, 4.5, 7.2]

km = KMeans(3,).fit(np.array(lst).reshape(-1,1))
print(km.labels_)
# [0 0 1 1 1 2 2]: OK output

lst = [10, 11.1, 30.4, 30.0, 32.9, 6.2, 31.2, 29.8, 12.3, 10.5]
km = KMeans(3,).fit(np.array(lst).reshape(-1,1))
print(km.labels_)
# [0 0 1 1 1 2 1 1 0 0]. Desired output: [0 0 1 1 1 1 1 1 2 2]

4 个答案:

答案 0 :(得分:1)

如上所述,我认为获得理想结果的一种直接方法是仅使用常规的K均值聚类,然后根据需要修改生成的输出。
说明:该想法是获取K-means输出,然后对其进行迭代:跟踪上一个项目的集群组和当前集群组,并控制根据条件创建的新集群。代码中的解释。

import numpy as np
from sklearn.cluster import KMeans

lst = [10, 11.1, 30.4, 30.0, 32.9, 4.5, 7.2]

km = KMeans(3,).fit(np.array(lst).reshape(-1,1))
print(km.labels_)
# [0 0 1 1 1 2 2]: OK output

lst = [10, 11.1, 30.4, 30.0, 32.9, 6.2, 31.2, 29.8, 12.3, 10.5]
km = KMeans(3,).fit(np.array(lst).reshape(-1,1))
print(km.labels_)
# [0 0 1 1 1 2 1 1 0 0]. Desired output: [0 0 1 1 1 1 1 1 2 2]


def linear_order_clustering(km_labels, outlier_tolerance = 1):
    '''Expects clustering outputs as an array/list'''
    prev_label = km_labels[0] #keeps track of last seen item's real cluster
    cluster = 0 #like a counter for our new linear clustering outputs
    result = [cluster] #initialize first entry
    for i, label in enumerate(km_labels[1:]):
        if prev_label == label: 
            #just written for clarity of control flow, 
            #do nothing special here
            pass 
        else: #current cluster label did not match previous label
            #check if previous cluster label reappears 
            #on the right of current cluster label position 
            #(aka current non-matching cluster is sandwiched 
            #within a reasonable tolerance)
            if (outlier_tolerance and 
                prev_label in km_labels[i + 1: i + 2 + outlier_tolerance]):                     label = prev_label #if so, overwrite current label
            else:
                cluster += 1 #its genuinely a new cluster
        result.append(cluster)
        prev_label = label
    return result

请注意,我只测试了1个异常值的容差,并且不能保证它在所有情况下都可以开箱即用。但是,这应该可以帮助您入门。

输出:

print(km.labels_)
result = linear_order_clustering(km.labels_)
print(result)
[1 1 0 0 0 2 0 0 1 1]
[0, 0, 1, 1, 1, 1, 1, 1, 2, 2]

答案 1 :(得分:0)

我将通过几步来解决这个问题。首先,我将具有第一个函数/方法来进行分析以确定每个组的聚类中心,并返回这些中心的数组。然后,我会将这些中心与列表一起放入另一个函数/方法中,以组装列表中每个数字的群集ID的列表。然后,我将返回排序后的列表。

答案 2 :(得分:0)

定义一个阈值。

如果x [i]和x [i-1]的值相差太大,请开始一个新的 segment

要获得更好的结果,请查看KDE和CUSUM方法。

不要使用群集。它有不同的目标。

答案 3 :(得分:0)

我遇到了类似的问题,并按如下方式解决了该问题:

  • 给出所有元素之间的距离矩阵,
  • 我要么做一个自下而上的聚类(合并两个“最相似”的元素/子集群),要么做一个自上而下的聚类(将一组元素分成“最不同”的子集群);
  • 要计算子集群之间的距离,我会汇总子集群中所有元素的距离(默认方法是取平均值,也可以使用最小或最大距离)。
  • 这两种方法都会导致分层聚类,然后可以对其进行切割以生成任意数量的聚类。

自下而上的方法似乎提供了更好的结果,但YMMV。

这是自下而上方法的代码(R中)。它建立:

  • 一个merge矩阵,其中每行包括两列,并合并下两件事的索引-元素的负索引和先前创建的子集群的正索引(R使用基于1的索引)
  • 一个height数组,其中包含两个合并的元素/子群集之间的距离。这被添加到合并对象的最大高度中(叶元素的高度为0),因此高度始终在增加(对于树的显示,或者对于R来说,它是“树状图”)。

这可用于创建R hclust个对象,这些对象可以通过各种方式显示和操作。

这不是最有效的实现,但是可以在合理的时间内完成工作。一种更有效的方法是减小距离矩阵的大小(这将需要更多簿记来跟踪较小矩阵和原始元素之间的索引映射):

bottom_up <- function(distances, aggregation) {
    aggregate <- switch(aggregation, mean=mean, min=min, max=max)

    rows_count <- dim(distances)[1]
    diag(distances) <- Inf

    merge <- matrix(0, nrow=rows_count - 1, ncol=2)
    height <- rep(0, rows_count - 1)
    merged_height <- rep(0, rows_count)
    groups <- -(1:rows_count)

    for (merge_index in 1:(rows_count - 1)) {
        adjacent_distances <- pracma::Diag(distances, 1)

        low_index <- which.min(adjacent_distances)
        high_index <- low_index + 1

        grouped_indices <- sort(groups[c(low_index, high_index)])

        merged_indices <- which(groups %in% grouped_indices)
        groups[merged_indices] <- merge_index
        merge[merge_index,] <- grouped_indices

        height[merge_index] <- max(merged_height[merged_indices]) + adjacent_distances[low_index]
        merged_height[merged_indices] <- height[merge_index]

        merged_distances <- apply(distances[,merged_indices], 1, aggregate)
        distances[,merged_indices] <- merged_distances
        distances[merged_indices,] <- rep(merged_distances, each=length(merged_indices))

        distances[merged_indices, merged_indices] <- Inf
    }

    return (list(merge=merge, height=height))
}

pracma::Diag(distances, 1)获取1对角线偏移量(在主对角线上方)。