所以你有一个数组
1
2
3
60
70
80
100
220
230
250
为了更好的理解:
背景:纵坐标是频率,横坐标是数字。这些数字是由它们的频率表示的十个最高振幅。我想从中创建三个离散的数字以进行模式识别。可能会有更多点,但它们都被相对较大的频率差异所分组,就像您在此示例中看到的在约50和约0之间以及在约100和约220之间。请注意,什么是大的,什么是小的会改变,但聚类之间的差异与组/聚类元素之间的差异相比仍然显着。
所以你有一个数组
1
2
3
60
70
80
100
220
230
250
如果 x
只是代表索引,那么请注意你的数据点实际上是一维的。你可以使用Scipy的 cluster.vq
模块进行点聚类,该模块实现了k-means算法。
>>> import numpy as np
>>> from scipy.cluster.vq import kmeans, vq
>>> y = np.array([1,2,3,60,70,80,100,220,230,250])
>>> codebook, _ = kmeans(y, 3) # three clusters
>>> cluster_indices, _ = vq(y, codebook)
>>> cluster_indices
array([1, 1, 1, 0, 0, 0, 0, 2, 2, 2])
这个结果的意思是:前三个点形成一个聚类1
(任意标签),接下来的四个点形成聚类0
,最后的三个点形成聚类2
。根据索引对原始点进行分组留给读者作为练习。
如果想了解更多Python中的聚类算法,请查看scikit-learn。
array=list(range(15))
进行处理。K-means算法不适用于一维数据,特别是在不知道k
的情况下。事实上,关于k-means唯一值得说的好话就是它非常容易实现。 - Has QUIT--Anony-Mousse这是一个用Python实现的简单算法,用于检查一个值是否与聚类的平均值相比过于偏离(以标准差为度量):
from math import sqrt
def stat(lst):
"""Calculate mean and std deviation from the input list."""
n = float(len(lst))
mean = sum(lst) / n
stdev = sqrt((sum(x*x for x in lst) / n) - (mean * mean))
return mean, stdev
def parse(lst, n):
cluster = []
for i in lst:
if len(cluster) <= 1: # the first two values are going directly in
cluster.append(i)
continue
mean,stdev = stat(cluster)
if abs(mean - i) > n * stdev: # check the "distance"
yield cluster
cluster[:] = [] # reset cluster to the empty list
cluster.append(i)
yield cluster # yield the last cluster
这将返回您在示例中所期望的结果,其中5 < n < 9
:>>> array = [1, 2, 3, 60, 70, 80, 100, 220, 230, 250]
>>> for cluster in parse(array, 7):
... print(cluster)
[1, 2, 3]
[60, 70, 80, 100]
[220, 230, 250]
n
的sum(lst) / n
结果为整数,因此mean
为1
而不是1.5
。将len(lst)
转换为float
即可解决问题(我已编辑代码)。 - Rik Poggin
呢?一个固定的数字并不总是能得到正确的结果。例如,假设n=7
在大多数情况下都可以工作,但对于这个数组 [130, 167, 213, 441, 445, 451, 478, 515, 526, 564, 655, 782, 1261]
它无法正确分组。 3,9,1
对我来说是最好的选择,但实际上是3,3,6,1
。 - Ergec我假设您想要一个相当好但简单的算法。
如果您知道您需要N个簇,则可以取输入列表(已排序)中连续成员之间的差异(delta)。例如,在numpy中:
deltas = diff( sorted(input) )
那么你可以将截断点放在发现的前N-2个最大差异处。
如果你不知道N是多少,情况会更加棘手。在这种情况下,您可以在看到大于某个大小的增量时放置截断点。这将成为一个手动调整的参数,虽然不太好,但可能已经足够好了。
k = 2
a = [1, 2, 3, 60, 70, 80, 100, 220, 230, 250]
a.sort()
b=[] # A *heap* would be faster
for i in range(1, len(a)):
b.append( (a[i]-a[i-1], i) )
b.sort()
# b now is [... (20, 6), (20, 9), (57, 3), (120, 7)]
# and the last ones are the best split points.
b = map(lambda p: p[1], b[-k:])
b.sort()
# b now is: [3, 7]
b.insert(0, 0)
b.append(len(a) + 1)
for i in range(1, len(b)):
print a[b[i-1]:b[i]],
# Prints [1, 2, 3] [60, 70, 80, 100] [220, 230, 250]
b.insert(0,0)
,因为 b
保存了“breaker”索引,所以它还需要第一个(0
)来开始。 - Rik Poggi