聲明
:本人小白,文章作為自己的學習參考資料,供大家學習交流
本文refer to
作者:csuldw
鏈接:https://github.com/csuldw/MachineLearning/tree/master/Kmeans
來源:Github
感謝此文章原創(chuàng)者
如有侵犯您的知識產權和版權問題,請通知本人,本人會即時做出處理并刪除文章
Email:louhergetup@gmail.com
理論部分
K-means原理
(這部分感覺 csuldw 大神寫的很好,所以直接拿來用了,想了解詳情請訪問上面 Github 的鏈接)
創(chuàng)建 k 個點作為 k 個簇的起始質心(經常隨機選擇)
- 分別計算剩下的元素到k個簇中心的相異度(距離),將這些元素分別劃歸到相異度最低的簇。
- 根據聚類結果,重新計算k個簇各自的中心,計算方法是取簇中所有元素各自維度的算術平均值。
- 將 D 中全部元素按照新的中心重新聚類。
- 重復第4步,直到聚類結果不再變化。
- 最后,輸出聚類結果。
實現(xiàn)部分
實驗環(huán)境
- 操作系統(tǒng):win10 64
- 編程語言:Python 3.7.3
K-means聚類實現(xiàn)
這部分也是 csuldw 大神的實現(xiàn)
# -*- coding: utf-8 -*-
import numpy as np
class KMeansClassifier():
"this is a k-means classifier"
def __init__(self, k=3, initCent='random', max_iter=500):
self._k = k
self._initCent = initCent
self._max_iter = max_iter
self._clusterAssment = None
self._labels = None
self._sse = None
def _calEDist(self, arrA, arrB):
"""
功能:歐拉距離距離計算
輸入:兩個一維數組
"""
return np.math.sqrt(sum(np.power(arrA - arrB, 2)))
def _calMDist(self, arrA, arrB):
"""
功能:曼哈頓距離距離計算
輸入:兩個一維數組
"""
return sum(np.abs(arrA - arrB))
def _randCent(self, data_X, k):
"""
功能:隨機選取k個質心
輸出:centroids # 返回一個m*n的質心矩陣
"""
n = data_X.shape[1] # 獲取特征的維數
centroids = np.empty((k, n)) # 使用numpy生成一個k*n的矩陣,用于存儲質心
for j in range(n):
minJ = min(data_X[:, j])
rangeJ = float(max(data_X[:, j] - minJ))
# 使用flatten拉平嵌套列表(nested list)
centroids[:, j] = (minJ + rangeJ * np.random.rand(k, 1)).flatten()
return centroids
def fit(self, data_X):
"""
輸入:一個m*n維的矩陣
"""
if not isinstance(data_X, np.ndarray) or \
isinstance(data_X, np.matrixlib.defmatrix.matrix):
try:
data_X = np.asarray(data_X)
except:
raise TypeError("numpy.ndarray resuired for data_X")
m = data_X.shape[0] # 獲取樣本的個數
# 一個m*2的二維矩陣,矩陣第一列存儲樣本點所屬的族的索引值,
# 第二列存儲該點與所屬族的質心的平方誤差
self._clusterAssment = np.zeros((m, 2))
if self._initCent == 'random':
self._centroids = self._randCent(data_X, self._k)
clusterChanged = True
for _ in range(self._max_iter): # 使用"_"主要是因為后面沒有用到這個值
clusterChanged = False
for i in range(m): # 將每個樣本點分配到離它最近的質心所屬的族
minDist = np.inf # 首先將minDist置為一個無窮大的數
minIndex = -1 # 將最近質心的下標置為-1
for j in range(self._k): # 次迭代用于尋找最近的質心
arrA = self._centroids[j, :]
arrB = data_X[i, :]
distJI = self._calEDist(arrA, arrB) # 計算誤差值
if distJI < minDist:
minDist = distJI
minIndex = j
if self._clusterAssment[i,
0] != minIndex or self._clusterAssment[
i, 1] > minDist**2:
clusterChanged = True
self._clusterAssment[i, :] = minIndex, minDist**2
if not clusterChanged: # 若所有樣本點所屬的族都不改變,則已收斂,結束迭代
break
for i in range(self._k): # 更新質心,將每個族中的點的均值作為質心
index_all = self._clusterAssment[:, 0] # 取出樣本所屬簇的索引值
value = np.nonzero(index_all == i) # 取出所有屬于第i個簇的索引值
ptsInClust = data_X[value[0]] # 取出屬于第i個簇的所有樣本點
self._centroids[i, :] = np.mean(ptsInClust, axis=0) # 計算均值
self._labels = self._clusterAssment[:, 0]
self._sse = sum(self._clusterAssment[:, 1])
def predict(self, X): # 根據聚類結果,預測新輸入數據所屬的族
# 類型檢查
if not isinstance(X, np.ndarray):
try:
X = np.asarray(X)
except:
raise TypeError("numpy.ndarray required for X")
m = X.shape[0] # m代表樣本數量
preds = np.empty((m, ))
for i in range(m): # 將每個樣本點分配到離它最近的質心所屬的族
minDist = np.inf
for j in range(self._k):
distJI = self._calEDist(self._centroids[j, :], X[i, :])
if distJI < minDist:
minDist = distJI
preds[i] = j
return preds
測試部分
導入 Python 庫
import pandas as pd
import numpy as np
from kmeans import KMeansClassifier
import matplotlib.pyplot as plt
import csv
提取數據
讀取.csv 文件(學生成績文件)
filename = './data/2017EngGrade.csv'
with open(filename) as f:
reader = csv.reader(f)
創(chuàng)建兩個列表,分別用于存放.csv 文件中的數學成績和英語成績
gradeMaths = []
gradeEnglishs = []
遍歷文件中數據,將兩列數據分別存放至兩個列表
for row in reader:
gradeMath = int(row[2])
gradeMaths.append(gradeMath)
gradeEnglish = int(row[1])
gradeEnglishs.append(gradeEnglish)
合并兩列表
z = list(zip(gradeMaths, gradeEnglishs))
將合并后的兩列表轉為矩陣
matz = np.array(z)
提取數據部分完整代碼如下:
#讀取.csv 文件
filename = './data/2017EngGrade.csv'
with open(filename) as f:
reader = csv.reader(f)
#創(chuàng)建兩個列表,分別用于存放.csv 文件中的數學成績和英語成績
gradeMaths = []
gradeEnglishs = []
#遍歷文件中數據,將兩列數據分別存放至兩個列表
for row in reader:
gradeMath = int(row[2])
gradeMaths.append(gradeMath)
gradeEnglish = int(row[1])
gradeEnglishs.append(gradeEnglish)
#合并兩列表
z = list(zip(gradeMaths, gradeEnglishs))
#將合并后的兩列表轉為矩陣
matz = np.array(z)
使用 K-means 進行測試
if __name__=="__main__":
data_X = matz
k = 3
clf = KMeansClassifier(k)
clf.fit(data_X)
cents = clf._centroids
labels = clf._labels
sse = clf._sse
colors = ['b','g','r','k','c','m','y','#e24fff','#524C90','#845868']
for i in range(k):
index = np.nonzero(labels==i)[0]
x0 = data_X[index, 0]
x1 = data_X[index, 1]
y_i = i
for j in range(len(x0)):
plt.text(x0[j], x1[j], str(y_i), color=colors[i], \
fontdict={'weight': 'bold', 'size': 6})
plt.scatter(cents[i,0],cents[i,1],marker='x',color=colors[i],\
linewidths=7)
plt.title("SSE={:.2f}".format(sse))
plt.axis([40,100,40,100])
outname = "./result/k_clusters" + str(k) + ".png"
plt.savefig(outname)
plt.show()
結果展示
k = 3 時,K-means 算法結果如圖:
k = 4 時,K-means 算法結果如圖:
k = 5 時,K-means 算法結果如圖:
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
