【问题标题】:How to adjust kMeans clustering sensitivity?如何调整 kMeans 聚类敏感度?
【发布时间】:2019-08-23 21:40:09
【问题描述】:

我有以下数据集:

        node        bc cluster
1    russian  0.457039       1
48       man  0.286875       1
155    woman  0.129939       0
3        bit  0.092721       0
5      write  0.065424       0
98       age  0.064347       0
97     escap  0.062675       0
74      game  0.062606       0

然后我按bc 值执行kMeans 聚类,将节点分成两个不同的组。现在使用下面的代码,我得到了上面的结果(聚类结果在cluster 列中)。

    bc_df = pd.DataFrame({"node": bc_nodes, "bc": bc_values})
    bc_df = bc_df.sort_values("bc", ascending=False)
    km = KMeans(n_clusters=2).fit(bc_df[['bc']])
    bc_df.loc[:,'cluster'] = km.labels_
    print(bc_df.head(8))

这很好,但我希望它的工作方式略有不同,将前 4 个节点选择到第一个集群中,然后将其他节点选择到第二个集群中,因为它们彼此更相似。

我可以对 kMeans 进行一些调整,或者您知道sklearn 中的另一种算法可以做到这一点吗?

【问题讨论】:

  • 在我看来,第 3 和第 4 值更接近 0.065..
  • 是的,但是从第 5 个值开始的所有内容都或多或少相同...
  • 如果你只有两个组,也许你可以尝试不使用 sklearn ..
  • 我有一个想法,但我可以先问一下bc 的值。你从哪里弄来的?
  • 太好了,给我一两分钟,我有一个解决方案。只需要清理一下

标签: python cluster-analysis k-means


【解决方案1】:

只需自己选择阈值。

在得到想要的结果之前,不适合破解算法。

如果您希望前五个术语成为一个集群,那么您可以随意标记它们。不要假装它是一个聚类结果。

【讨论】:

    【解决方案2】:

    看起来您想要的是对一维数据进行聚类。解决这个问题的一种方法是使用 Jenks Natural Breaks(谷歌它以获得它的解释)。

    这个函数不是我写的(很多功劳归功于@Frank 的解决方案here

    鉴于您的数据框:

    import pandas as pd
    
    df = pd.DataFrame([
    ['russian',  0.457039],
    ['man',  0.286875],
    ['woman',  0.129939],
    ['bit',  0.092721],
    ['write',  0.065424],
    ['age',  0.064347],
    ['escap',  0.062675],
    ['game',  0.062606]], columns = ['node','bc'])
    

    带有 Jenks Natural Break 函数的代码:

    def get_jenks_breaks(data_list, number_class):
        data_list.sort()
        mat1 = []
        for i in range(len(data_list) + 1):
            temp = []
            for j in range(number_class + 1):
                temp.append(0)
            mat1.append(temp)
        mat2 = []
        for i in range(len(data_list) + 1):
            temp = []
            for j in range(number_class + 1):
                temp.append(0)
            mat2.append(temp)
        for i in range(1, number_class + 1):
            mat1[1][i] = 1
            mat2[1][i] = 0
            for j in range(2, len(data_list) + 1):
                mat2[j][i] = float('inf')
        v = 0.0
        for l in range(2, len(data_list) + 1):
            s1 = 0.0
            s2 = 0.0
            w = 0.0
            for m in range(1, l + 1):
                i3 = l - m + 1
                val = float(data_list[i3 - 1])
                s2 += val * val
                s1 += val
                w += 1
                v = s2 - (s1 * s1) / w
                i4 = i3 - 1
                if i4 != 0:
                    for j in range(2, number_class + 1):
                        if mat2[l][j] >= (v + mat2[i4][j - 1]):
                            mat1[l][j] = i3
                            mat2[l][j] = v + mat2[i4][j - 1]
            mat1[l][1] = 1
            mat2[l][1] = v
        k = len(data_list)
        kclass = []
        for i in range(number_class + 1):
            kclass.append(min(data_list))
        kclass[number_class] = float(data_list[len(data_list) - 1])
        count_num = number_class
        while count_num >= 2:  # print "rank = " + str(mat1[k][count_num])
            idx = int((mat1[k][count_num]) - 2)
            # print "val = " + str(data_list[idx])
            kclass[count_num - 1] = data_list[idx]
            k = int((mat1[k][count_num] - 1))
            count_num -= 1
        return kclass
    
    
    
    
    
    
    # Get values to find the natural breaks    
    x = list(df['bc'])
    
    # Calculate the break values. 
    # I want 2 groups, so parameter is 2.
    # If you print (get_jenks_breaks(x, 2)), it will give you 3 values: [min, break1, max]
    # Obviously if you want more groups, you'll need to adjust this and also adjust the assign_cluster function below.
    breaking_point = get_jenks_breaks(x, 2)[1]
    
    # Creating group for the bc column
    def assign_cluster(bc):
        if bc < breaking_point:
            return 0
        else:
            return 1
    
    # Apply `assign_cluster` to `df['bc']`    
    df['cluster'] = df['bc'].apply(assign_cluster)
    

    输出:

    print (df)
          node        bc  cluster
    0  russian  0.457039        1
    1      man  0.286875        1
    2    woman  0.129939        1
    3      bit  0.092721        0
    4    write  0.065424        0
    5      age  0.064347        0
    6    escap  0.062675        0
    7     game  0.062606        0
    

    【讨论】:

    • 感谢您的出色回答!是否可以改变其灵敏度,从而降低阈值?还是我应该尝试将其分成更多组?另外,你能记得你大概是从哪里得到它的吗?
    • 我认为你不能降低灵敏度。我想当然,如果你调整休息次数,那可以解决问题。但是你可能只需要玩弄它来看看它在哪里休息
    • 找到了。我将它发布在上面的解决方案中
    • 非常感谢!你是最好的!我实际上需要 javascript 库并使用您发布的链接,我找到了它(我猜它是作者写的):simplestatistics.org/docs/#ckmeans
    • 太棒了!是的,我喜欢这个算法。我真的很好奇为什么 scikit learn 没有将它添加到他们的算法集中。我找到了一些它的包,但只是觉得这个实现效果很好,他也提供了一个我喜欢的可视化脚本。
    【解决方案3】:

    前两个值总是与从索引 3 开始的值不同,因为它们低于约 0.152703 的平均值。由于您的问题也可以解释为简单的两类问题,因此您还可以使用 ~0.0790725 的中位数将这两个类分开:

    idx = df['bc'] > df['bc'].median()
    

    现在您可以使用此索引来选择您的两个类,它们由中位数分隔:

    df[idx]
    

    给予

            node        bc  cluster
    
      1  russian  0.457039        1
     48      man  0.286875        1
    155    woman  0.129939        0
      3      bit  0.092721        0
    

    df[~idx]
    

    给予

         node        bc  cluster
    
     5  write  0.065424        0
    98    age  0.064347        0
    97  escap  0.062675        0
    74   game  0.062606        0
    

    【讨论】:

    • 谢谢!这也是一个很好且简单的解决方案。但是,我实际上有一个大约 150 个节点的数据集,所以 median 会低得多。
    • 如果您想手动设置阈值,您只需将df['bc'].median() 替换为任意数字即可。我不会说中位数会低得多。事实上,如果您给定的数据具有代表性并且可以外推,我认为它会保持不变,大约为 0.063。
    猜你喜欢
    • 2018-08-26
    • 2016-07-05
    • 2013-03-23
    • 2013-11-18
    • 1970-01-01
    • 2015-02-20
    • 2017-06-08
    • 2011-01-28
    • 2017-04-28
    相关资源
    最近更新 更多