当前位置:   article > 正文

详解K均值聚类算法(K-means Clustering)简易实例:从空调温度判别使用者_kmeans聚类算法简单例题讲解

kmeans聚类算法简单例题讲解

实例说明

假定某共用办公室有两个人使用,他们使用办公室的时候都会用空调遥控器设置房间的温度。但空调遥控器显示面板坏掉了,只能通过加减温度的方式盲调整。

但已知他们习惯的温度不同。办公室的温度计有记录功能,每天记录12 小时的温度,每15 分钟记录一次,根据一个月所记录的数据,通过聚类算法,算出这两个人的喜好温度,并估算他们各使用办公室多少时间。

可以认为这两个人使用办公室的时候室内温度是不同方差和不同均值的正态分布随机数,生成模拟数据,并用matplotlib 画出数据的直方图。
使用K 均值聚类算法将模拟数据分成两个簇,解答前面的问题。
使用温度传感器读入温度,根据温度值,判断是谁在使用办公室。

聚类算法概述

聚类算法的步骤可以抽象地分为如下几步:

  1. 获取原始数据
    2. 随机选取K个位置作为初始的聚类中心
    3. 计算每个样本与各个聚类中心之间的距离,把每个样本分配给距离它最近的聚类中心,聚类中心以及分配给它们的样本就代表一个聚类
    4. 每分配完成后,聚类的聚类中心会根据聚类中现有的样本被重新计算,获得新的聚类中心
    5. 这个过程将不断重复直到满足某个终止条件。
    6. 终止条件可以是没有(或最小数目)样本被重新分配给不同的聚类,或者是聚类中心不再发生变化。

在这里插入图片描述
接下来我们分步骤利用Python语言将这几步实例化。

聚类算法实例分析

(a)数据生成

这个任务首先需要我们人为构造有两条正态曲线的数据集构成的全数据集。随后我们再利用聚类算法将其分开。

作数据集之前首先导入必要的模块:

import numpy as np # numpy的ndarray数据类型极有利于数据处理;
import matplotlib.pyplot as plt # matplotlib用于作图;
import scipy.stats as stats # scipy辅助numpy进行统计分析。在这个实验中用于对直方图数据进行正态拟合。
  • 1
  • 2
  • 3

以下用于生成数据集:

half_total_times = 360 # 数据规模

# 将两条正态曲线的数据合并为总的数据集。
log = np.append(np.random.normal(33, 2, half_total_times),
                np.random.normal(27, 2, half_total_times))
# 也可以如下实现:
# log = np.random.normal(33, 2, half_total_times)
# log = np.append(log, np.random.normal(27, 2, half_total_times))

# 以下绘图
plt.hist(log, 80, histtype='bar', facecolor='yellowgreen', alpha=0.75)
plt.ylabel('Frequentness')
plt.xlabel('Temperature')
plt.xticks(np.arange(20, 42, 1))
plt.title("Raw Temperature Stats.")
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

作出数据集图样如下:

在这里插入图片描述

(b)确定聚类中心

首先将打乱的的数据分成两类,将它们的均值确定为两个聚类中心
要说明的是在这个一维问题当中,聚类中心就是一个float型的数字。聚类过程当中,这个数据通过均值反复更新,直至退出。

np.random.shuffle(log)
t1, t2 = log[:half_total_times], log[half_total_times:]
k1, k2 = np.mean(t1), np.mean(t2)
  • 1
  • 2
  • 3

(c)初步聚类

利用先前确定的聚类中心对两类各自进行是否偏离中心的判断,完成初步聚类。

i, j = 0, 0
while i < len(t1):
    tmp = t1[i] # 取出当前值,为判断作准备
    if abs(tmp-k1) > abs(tmp-k2): # 如果偏离中心,换到另一类
        t2 = np.append(t2, tmp) # 注意增删要用原来ndarray名去承接删除后的引用
        t1 = np.delete(t1, i)
        # print(tmp, "to 2")
        continue # 防止跳过
    i = i + 1
while j < len(t2):
    tmp = t2[j]
    if abs(tmp-k1) < abs(tmp-k2):
        t1 = np.append(t1, tmp)
        t2 = np.delete(t2, j)
        # print(tmp, "to 1")
        continue
    j = j + 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(d)重新确定聚类中心

在先前聚类的基础上再次有目标地确定聚类中心。

k1 = np.mean(t1)
k2 = np.mean(t2)
print(k1, k2)
  • 1
  • 2
  • 3

(e)(f)循环操作直至算法结束

重复上述操作直至两类之间不发生任何交换。通过循环将(c)(d)的代码重新实现如下。若发生交换则继续循环。

while True:
	flag = True # 默认不发生交换,已经完成标记为True
	i, j = 0, 0
	while i < len(t1):
	    tmp = t1[i]
	    if abs(tmp-k1) > abs(tmp-k2):
	        t2 = np.append(t2, tmp)
	        t1 = np.delete(t1, i)
	        flag = False  # 发生一次交换那么循环就会继续进行
	        print(tmp, "has been moved to 2")
	    i = i + 1
	while j < len(t2):
	    tmp = t2[j]
	    if abs(tmp-k1) < abs(tmp-k2):
	        t1 = np.append(t1, tmp)
	        t2 = np.delete(t2, j)
	        flag = False
	        print(tmp, "has been moved to 1")
	    j = j + 1
	if flag:
	    break
	k1 = np.mean(t1)
	k2 = np.mean(t2)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

聚类结果如下:

plt.hist(t1, 80, histtype='bar', density=1, facecolor='blue', alpha=0.75)
plt.hist(t2, 80, histtype='bar', density=1, facecolor='green', alpha=0.75)
plt.ylabel('Proportion')
plt.xlabel('Temperature')
plt.xticks(np.arange(20, 42, 1))
plt.title("2-Means Clustering")
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

为了更加清楚地表现这个聚类的结果,我们可以利用scipy的stats组件进行正态拟合,重新作图:

# 接受绘图的返回值
n1, bins1, patches1 = plt.hist(
    t1, 80, histtype='bar', density=1, facecolor='blue', alpha=0.75)  
n2, bins2, patches2 = plt.hist(
    t2, 80, histtype='bar', density=1, facecolor='green', alpha=0.75)
# 利用返回的横坐标与重新拟合
y1 = stats.norm.pdf(bins1, np.mean(t1), np.std(t1))
y2 = stats.norm.pdf(bins2, np.mean(t2), np.std(t2))
plt.plot(bins1, y1, 'r--')
plt.plot(bins2, y2, 'r--')
plt.ylabel('Proportion')
plt.xlabel('Temperature')
plt.xticks(np.arange(20, 42, 1))
plt.title("2-Means Clustering")
plt.show()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

如下:

注意这幅图和上面没有带拟合的是次实验的结果。

在这里插入图片描述

输出的值为:

final k-means: 27.00089944939223 33.127071306372585 # htt = 720
  • 1

思考:K均值聚类算法的一个缺陷

我们可以看到,这个聚类中心距离最初设定的27和33有一定的距离。

当数据量较小时,这个差距是由数据集本身的特性决定的。由于数据集过小,随机性使得平均值并不能较完美地趋近中心值。事实上,选定half_total_times为一个360时,每一次的结果都会有较大的差异。

于是我们猜测当数据量较大时,这个值将会排除随机因素,趋于一个稳定的值。依次将数据量扩大,作如下的试验:

数据顺序经过调整,将高温区和低温区对齐。实际聚类结果中,33.*的数据可能在后

# htt : half_total_times
final k-means: 33.12662153907564 26.9236373559599 # htt = 5000
final k-means: 33.12400746682427 26.867736591964416 # htt = 20000
final k-means: 33.13533873227883 26.89201367317691 # htt = 50000
final k-means: 33.13308044620618 26.87871264214395 # htt = 100000
final k-means: 33.113832074382046 26.873664094790183 # htt = 150000
final k-means: 33.104500152812 26.87391229842333 # htt = 200000
final k-means: 33.11234652005665 26.885121276955825 # htt = 250000
final k-means: 33.11720891682733 26.876981068264236 # htt = 300000
final k-means: 33.112480044788235 26.89036390407023 # htt = 350000
final k-means: 33.121317671847606 26.88380554470867 # htt = 400000
final k-means: 33.120665211354314 26.886840666914292# htt = 450000
final k-means: 33.118645760649365 26.87833700669375 # htt = 500000
final k-means: 33.11972276310472 26.883882190093534 # htt = 550000
final k-means: 33.11476481580636 26.882009336811468 # htt = 600000
final k-means: 33.11899334301187 26.885940661245822 # htt = 650000
final k-means: 33.122877982248134 26.884929683501547 # htt = 700000     
final k-means: 33.11483313992061 26.882989502619292 # htt = 750000      
final k-means: 33.112846759715936 26.880544838547046 # htt = 800000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我们不难看到,k均值也并没有收敛到正态的中心设定值。高温集合的均值偏大,低温集合的均值偏小。

从原始数据其实是不难理解的。两个正态曲线有一部分交错,由于聚类算法过程只按距离分配,低温的正态的波浪线部分分配给高温类,高温的正态的斜线部分分配给低温类,如图
在这里插入图片描述
所以两类当中各自并不是一条完整的正态曲线的数据集,当数据量大时这一点是容易看出的,如图直方图和红色的正态曲线有较大的差异。
在这里插入图片描述
聚类算法使得交错的部分不能有效地归入实际上的两类,而是数学上的两类。从而导致了最终的系统性的稳定偏离,收敛到33.112(3)和26.882(4),而不是33和27。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/706480
推荐阅读
相关标签
  

闽ICP备14008679号