如何在Python中对这三个区域的数组进行分组/聚类?
你有一个数组
1
2
3
60
70
80
100
220
230
250
为了更好地理解:
你想知道如何在Python(2.6版)中把这三个区域分组/聚类,这样你就能得到三个数组,分别包含:
[1 2 3] [60 70 80 100] [220 230 250]
背景:
这里的y轴是频率,x轴是数字。这些数字代表的是十个最高的幅度,按它们的频率来表示。我想从中创建三个独立的数字,以便进行模式识别。可能还有更多的点,但它们都是根据相对较大的频率差异来分组的,就像这个例子中,频率大约在50和0之间,以及大约在100和220之间的差异。需要注意的是,什么是大,什么是小是会变化的,但不同组之间的差异相对于组内元素之间的差异仍然是显著的。
5 个回答
我想你想要一个简单但效果不错的算法。
如果你已经知道想要分成N个组,那你可以计算输入列表中相邻两个元素之间的差值(也就是增量)。比如在numpy这个库里可以这样做:
deltas = diff( sorted(input) )
然后你可以在找到的N-2个最大差值的位置上设置分割点。
如果你不知道N是多少,那事情就复杂一些。你可以在看到某个增量大于特定值的时候设置分割点。这就需要你自己调整这个参数,虽然这样不是特别理想,但可能对你来说已经足够用了。
注意,如果x
只是表示一个索引,那么你的数据点实际上是一维的。你可以使用Scipy的cluster.vq
模块来对这些点进行聚类,这个模块实现了k-均值算法。
>>> 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。
这是一个用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]