赞
踩
假定某共用办公室有两个人使用,他们使用办公室的时候都会用空调遥控器设置房间的温度。但空调遥控器显示面板坏掉了,只能通过加减温度的方式盲调整。
但已知他们习惯的温度不同。办公室的温度计有记录功能,每天记录12 小时的温度,每15 分钟记录一次,根据一个月所记录的数据,通过聚类算法,算出这两个人的喜好温度,并估算他们各使用办公室多少时间。
可以认为这两个人使用办公室的时候室内温度是不同方差和不同均值的正态分布随机数,生成模拟数据,并用matplotlib 画出数据的直方图。
使用K 均值聚类算法将模拟数据分成两个簇,解答前面的问题。
使用温度传感器读入温度,根据温度值,判断是谁在使用办公室。
聚类算法的步骤可以抽象地分为如下几步:
接下来我们分步骤利用Python语言将这几步实例化。
这个任务首先需要我们人为构造有两条正态曲线的数据集构成的全数据集。随后我们再利用聚类算法将其分开。
作数据集之前首先导入必要的模块:
import numpy as np # numpy的ndarray数据类型极有利于数据处理;
import matplotlib.pyplot as plt # matplotlib用于作图;
import scipy.stats as stats # scipy辅助numpy进行统计分析。在这个实验中用于对直方图数据进行正态拟合。
以下用于生成数据集:
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()
作出数据集图样如下:
首先将打乱的的数据分成两类,将它们的均值确定为两个聚类中心。
要说明的是在这个一维问题当中,聚类中心就是一个float型的数字。聚类过程当中,这个数据通过均值反复更新,直至退出。
np.random.shuffle(log)
t1, t2 = log[:half_total_times], log[half_total_times:]
k1, k2 = np.mean(t1), np.mean(t2)
利用先前确定的聚类中心对两类各自进行是否偏离中心的判断,完成初步聚类。
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
在先前聚类的基础上再次有目标地确定聚类中心。
k1 = np.mean(t1)
k2 = np.mean(t2)
print(k1, k2)
重复上述操作直至两类之间不发生任何交换。通过循环将(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)
聚类结果如下:
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()
为了更加清楚地表现这个聚类的结果,我们可以利用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()
如下:
注意这幅图和上面没有带拟合的是次实验的结果。
输出的值为:
final k-means: 27.00089944939223 33.127071306372585 # htt = 720
我们可以看到,这个聚类中心距离最初设定的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
我们不难看到,k均值也并没有收敛到正态的中心设定值。高温集合的均值偏大,低温集合的均值偏小。
从原始数据其实是不难理解的。两个正态曲线有一部分交错,由于聚类算法过程只按距离分配,低温的正态的波浪线部分分配给高温类,高温的正态的斜线部分分配给低温类,如图
所以两类当中各自并不是一条完整的正态曲线的数据集,当数据量大时这一点是容易看出的,如图直方图和红色的正态曲线有较大的差异。
聚类算法使得交错的部分不能有效地归入实际上的两类,而是数学上的两类。从而导致了最终的系统性的稳定偏离,收敛到33.112(3)和26.882(4),而不是33和27。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。