資料探勘演算法之K_means演算法
轉載地址:https://blog.csdn.net/baimafujinji/article/details/50570824
聚類是將相似物件歸到同一個簇中的方法,這有點像全自動分類。簇內的物件越相似,聚類的效果越好。支援向量機、神經網路所討論的分類問題都是有監督的學習方式,現在我們所介紹的聚類則是無監督的。其中,K均值(K-means)是最基本、最簡單的聚類演算法。
學習更多機器學習演算法原理並瞭解在R中如何實現機器學習的技術,你還可以參考我的《R語言實戰:機器學習與資料分析》(電子工業出版社出版)一書。
在K均值演算法中,質心是定義聚類原型(也就是機器學習獲得的結果)的核心。在介紹演算法實施的具體過程中,我們將演示質心的計算方法。而且你將看到除了第一次的質心是被指定的以外,此後的質心都是經由計算均值而獲得的。
首先,選擇K個初始質心(這K個質心並不要求來自於樣本資料集),其中K是使用者指定的引數,也就是所期望的簇的個數。每個資料點都被收歸到距其最近之質心的分類中,而同一個質心所收歸的點集為一個簇。然後,根據本次分類的結果,更新每個簇的質心。重複上述資料點分類與質心變更步驟,直到簇內資料點不再改變,或者等價地說,直到質心不再改變。
基本的K均值演算法描述如下:
根據資料點到新質心的距離,再次對資料集中的資料進行分類,如圖13-2(c)所示。然後,演算法根據新的分類來計算新的質心,並再次根據資料點到新質心的距離,對資料集中的資料進行分類。結果發現簇內資料點不再改變,所以演算法執行結束,最終的聚類結果如圖13-2(d)所示。
對於距離函式和質心型別的某些組合,演算法總是收斂到一個解,即K均值到達一種狀態,聚類結果和質心都不再改變。但為了避免過度迭代所導致的時間消耗,實踐中,也常用一個較弱的條件替換掉“質心不再發生變化”這個條件。例如,使用“直到僅有1%的點改變簇”。
儘管K均值聚類比較簡單,但它也的確相當有效。它的某些變種甚至更有效, 並且不太受初始化問題的影響。但K均值並不適合所有的資料型別。它不能處理非球形簇、不同尺寸和不同密度的簇,儘管指定足夠大的簇個數時它通常可以發現純子簇。對包含離群點的資料進行聚類時,K均值也有問題。在這種情況下,離群點檢測和刪除大有幫助。K均值的另一個問題是,它對初值的選擇是敏感的,這說明不同初值的選擇所導致的迭代次數可能相差很大。此外,K值的選擇也是一個問題。顯然,演算法本身並不能自適應地判定資料集應該被劃分成幾個簇。最後,K均值僅限於具有質心(均值)概念的資料。一種相關的K中心點聚類技術沒有這種限制。在K中心點聚類中,我們每次選擇的不再是均值,而是中位數。這種演算法實現的其他細節與K均值相差不大,我們不再贅述。
最後我們給出一個實際應用的例子。(程式碼採用我最喜歡用做資料探勘的R語言來實現)
一組來自世界銀行的資料統計了30個國家的兩項指標,我們用如下程式碼讀入檔案並顯示其中最開始的幾行資料。可見,資料共分三列,其中第一列是國家的名字,該項與後面的聚類分析無關,我們更關心後面兩列資訊。第二列給出的該國第三產業增加值佔GDP的比重,最後一列給出的是人口結構中年齡大於等於65歲的人口(也就是老齡人口)佔總人口的比重。
為了方便後續處理,下面對讀入的資料庫進行一些必要的預處理,主要是調整列標籤,以及用國名替換掉行標籤(同時刪除包含國名的列)。
如果你繪製這些資料的散點圖,不難發現這些資料大致可以分為兩組。事實上,資料中有一半的國家是OECD成員國,而另外一半則屬於發展中國家(包括一些東盟國家、南亞國家和拉美國家)。所以我們可以採用下面的程式碼來進行K均值聚類分析。
對於聚類結果,限於篇幅我們仍然只列出了最開始的幾條。但是如果用圖形來顯示的話,可能更易於接受。下面是示例程式碼。
上述程式碼的執行結果如圖13-3所示。
另外一種與k-means非常類似的演算法是k-median演算法。此處已經無需再詳細介紹k-中值演算法的細節了,基本上和k-means一樣,只是把所有均值出現的地方換成中值而已。這個思想看起好像很不起眼,但是你還別說,k-median演算法還真的存在,而且是k-means演算法的一個重要補充和改進。
用python實現的程式碼入下:(詳細程式碼見https://github.com/yantijin/Lean_DataMining)
#coding=utf-8
from collections import defaultdict
from math import sqrt
from random import uniform
"""
需要定義的函式:
計算聚類中心;
計算兩點之間距離;
將所有點按照距離中心點的距離分類;
隨機生成k箇中心點
"""
"""
計算兩點之間的距離
INPUT:
data1,data2: data of the point
OUTPUT:
distance between data1 and data2
"""
def get_distance(data1,data2):
dimensions = len(data1)
dis = 0
for dimension in range(dimensions):
dis =dis +(data1[dimension]-data2[dimension])**2
return sqrt(dis)
"""
計算聚類中心
INPUT:
data_set: an list consists of list
OUTPUT:
center: calculate center of the input datas
"""
def cal_center(data_set):
center = []
dimensions = len(data_set[0])
amount = len(data_set)
for dimension in range(dimensions):
point_sum = 0
for data in data_set :
point_sum = point_sum +data[dimension]
center.append(point_sum/amount)
return center
"""
根據現有分類,重新計算聚類中心
INPUT:
assignments: a list that assign each data to a center
data_set: an list consists of list
OUTPUT:
center: update center by the assignments and data_set
"""
def update_center(assignments, data_set):
center = []
dt = defaultdict(list)
for assignment, data in zip(assignments, data_set):
dt[assignment].append(data)
for points in dt.values():
center.append(cal_center(points))
return center
"""
將所有點按照距離中心點的距離分類
INPUT:
data_set: an list consists of list
center: clustering center until now
OUTPUT:
assignments: assign each data for a center
"""
def assign_point(data_set, center):
len_centers = len(center)
len_data_sets = len(data_set)
assignments = []
for len_data_set in range(len_data_sets):
shortest_dis = float("inf")
assignment = -1
for len_center in range(len_centers):
dis = get_distance(data_set[len_data_set], center[len_center])
if dis < shortest_dis :
shortest_dis = dis
assignment = len_center
assignments.append(assignment)
return assignments
"""
隨機生成k箇中心點
INPUT:
data_set: an list consists of list
k: num of centers that wants for clustering
OUTPUT:
init_center: init random center
"""
def generate_init_center(data_set, k):
dimensions = len(data_set[0])
center = []
min_max = defaultdict(float)
for data in data_set:
for dimension in range(dimensions):
min_key = 'min_%d' % dimension
max_key = 'max_%d' % dimension
val = data[dimension]
if min_key not in min_max or val < min_max[min_key]:
min_max[min_key] = val
if max_key not in min_max or val > min_max[max_key]:
min_max[max_key] = val
# 此步要產生k個在min和max之間的data
for _k in range(k):
data = []
for dimension in range(dimensions):
min_val = min_max['min_%d' % dimension]
max_val = min_max['max_%d' % dimension]
data.append(uniform(min_val, max_val))
center.append(data)
return center
"""
k_means演算法實現
INPUT:
data_set: an list consists of list
k: num of centers that wants for clustering
OUTPUT:
"""
def kmeans(data_set, k):
center = generate_init_center(data_set, k)
assignments = assign_point(data_set, center)
new_center = update_center(assignments, data_set)
while new_center != center:
center = new_center
assignments = assign_point(data_set, center)
new_center = update_center(assignments, data_set)
return zip(assignments, data_set), new_center
"""
檔案讀寫
"""
def fileread(filepath):
file_object = open(filepath)
data = []
data1 = []
try:
for line in file_object.readlines():
line.replace('\n', '', 1)
tmp = line.split(' ')
tmplen = len(tmp)
for nm in range(tmplen):
data1.append(float(tmp[nm]))
data.append(data1)
data1=[]
finally:
file_object.close()
return data
if __name__== "__main__":
filepath = input("請輸入檔案地址及命名\n")
data_set = fileread(filepath)
k = 3
zip, center = kmeans(data_set, k)
for i in zip:
print(i)
print(center)
注意檔案輸入資料的格式應該按照如下來寫,
3 3
4 10
9 6
14 8
18 11
21 7