如何在scikit学习中使用核密度估计作为一维聚类方法?

Ale*_*man 23 cluster-analysis machine-learning data-mining kernel-density scikit-learn

我需要将一个简单的单变量数据集聚集到预设数量的集群中.从技术上讲,它更接近于分类或排序数据,因为它只有1D,但我的老板称它为聚类,所以我将坚持使用该名称.我所使用的系统使用的当前方法是K-means,但这看起来有点过分.

有没有更好的方法来执行此任务?

其他一些帖子的答案提到了KDE(核密度估计),但这是一种密度估算方法,它会如何工作?

我看到KDE如何返回密度,但是如何告诉它将数据拆分成箱?

我如何拥有与数据无关的固定数量的箱(这是我的要求之一)?

更具体地说,如何使用scikit学习来解决这个问题?

我的输入文件如下:

 str ID     sls
 1           10
 2           11 
 3            9
 4           23
 5           21
 6           11  
 7           45
 8           20
 9           11
 10          12
Run Code Online (Sandbox Code Playgroud)

我想将sls编号分组成簇或箱,这样:

Cluster 1: [10 11 9 11 11 12] 
Cluster 2: [23 21 20] 
Cluster 3: [45] 
Run Code Online (Sandbox Code Playgroud)

我的输出文件将如下所示:

 str ID     sls    Cluster ID  Cluster centroid
    1        10       1               10.66
    2        11       1               10.66
    3         9       1               10.66 
    4        23       2               21.33   
    5        21       2               21.33
    6        11       1               10.66
    7        45       3               45
    8        20       2               21.33
    9        11       1               10.66 
    10       12       1               10.66
Run Code Online (Sandbox Code Playgroud)

Ano*_*sse 42

自己编写代码.那么它最适合你的问题!

Boilerplate:永远不要假设您从网上下载的代码是正确的或最佳的......在使用之前一定要完全理解它.

%matplotlib inline

from numpy import array, linspace
from sklearn.neighbors.kde import KernelDensity
from matplotlib.pyplot import plot

a = array([10,11,9,23,21,11,45,20,11,12]).reshape(-1, 1)
kde = KernelDensity(kernel='gaussian', bandwidth=3).fit(a)
s = linspace(0,50)
e = kde.score_samples(s.reshape(-1,1))
plot(s, e)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

from scipy.signal import argrelextrema
mi, ma = argrelextrema(e, np.less)[0], argrelextrema(e, np.greater)[0]
print "Minima:", s[mi]
print "Maxima:", s[ma]
> Minima: [ 17.34693878  33.67346939]
> Maxima: [ 10.20408163  21.42857143  44.89795918]
Run Code Online (Sandbox Code Playgroud)

因此你的集群是

print a[a < mi[0]], a[(a >= mi[0]) * (a <= mi[1])], a[a >= mi[1]]
> [10 11  9 11 11 12] [23 21 20] [45]
Run Code Online (Sandbox Code Playgroud)

在视觉上,我们做了这个分裂:

plot(s[:mi[0]+1], e[:mi[0]+1], 'r',
     s[mi[0]:mi[1]+1], e[mi[0]:mi[1]+1], 'g',
     s[mi[1]:], e[mi[1]:], 'b',
     s[ma], e[ma], 'go',
     s[mi], e[mi], 'ro')
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我们切入红色标记.绿色标记是我们对集群中心的最佳估计.

  • 你不必知道k.您不仅可以获得更好的中心(受异常值影响较小),还可以获得*声音*分裂点(不仅仅是一半).关于带宽的文献很多,比如西尔弗曼的规则.也.谁在乎计算50密度估算?您可以预先计算内核并在快速卷积中执行此操作. (5认同)

Muh*_*oni 9

@Has QUIT--Anony-Mousse 接受的答案中有一点错误(由于我的声誉,我无法发表评论或建议进行编辑)。

线路:

print(a[a < mi[0]], a[(a >= mi[0]) * (a <= mi[1])], a[a >= mi[1]])
Run Code Online (Sandbox Code Playgroud)

应编辑为:

print(a[a < s[mi][0]], a[(a >= s[mi][0]) * (a <= s[mi][1])], a[a >= s[mi][1]])
Run Code Online (Sandbox Code Playgroud)

那是因为miandma是一个索引,其中s[mi]ands[ma]是值。如果您mi[0]用作限制,如果您的上下 linspace >> 您的上下数据,您将面临拆分的风险和错误。例如,运行此代码并查看拆分结果的差异:

import numpy as np
from numpy import array, linspace
from sklearn.neighbors import KernelDensity
from matplotlib.pyplot import plot
from scipy.signal import argrelextrema

a = array([10,11,9,23,21,11,45,20,11,12]).reshape(-1, 1)
kde = KernelDensity(kernel='gaussian', bandwidth=3).fit(a)
s = linspace(0,100)
e = kde.score_samples(s.reshape(-1,1))
mi, ma = argrelextrema(e, np.less)[0], argrelextrema(e, np.greater)[0]

print('Grouping by HAS QUIT:')
print(a[a < mi[0]], a[(a >= mi[0]) * (a <= mi[1])], a[a >= mi[1]])
print('Grouping by yasirroni:')
print(a[a < s[mi][0]], a[(a >= s[mi][0]) * (a < s[mi][1])], a[a >= s[mi][1]])
Run Code Online (Sandbox Code Playgroud)

结果:

Grouping by Has QUIT:
[] [10 11  9 11 11 12] [23 21 45 20]
Grouping by yasirroni:
[10 11  9 11 11 12] [23 21 20] [45]
Run Code Online (Sandbox Code Playgroud)