1. 程式人生 > >資料探勘 K-Medoide聚類實現例項

資料探勘 K-Medoide聚類實現例項

接著上一篇內容來講,本次要實現K-Medoide聚類方法對waveform資料以及影象的分割。

簡而言之,K-Medoide就是將K-means求平均的方式替換成將各簇中所有點依次作為中心點對該簇所有點求距離之和進行遍歷,最終距離之和最小的作為新的中心點即可。

優點:當存在噪音和孤立點時, PAM 比 k-平均方法更健壯。 這是因為中心點不象平均值那麼容易被極端資料影響。

缺點:PAM對於小資料集工作得很好, 但不能很好地用於大資料集,每次迭代O(k(n-k)2 ),其中 n 是資料物件數目, k 是聚類數。

下面又進入程式碼環節:

import random 
import pandas as pd
import numpy as np
import tool

#設定精度小數點後兩位
np.set_printoptions(precision=2)

#讀取檔案
df=pd.read_csv("waveform.data",header = None)

#增加20%的高斯噪聲
for x in range(5000*22*0.2):
	i = random.randint(0,21)
	j = random.randint(0,4999)
	df[i][j] += random.gauss(0,0.5)  #均值維0,方差為0.5的高斯噪聲


#隨機選擇三個中心點作為初始質心
i1 = random.randint(0,21)     #列標0-21
j1 = random.randint(0,4998)   #行標0-4999
cent1 = df[i1][j1]

i2 = random.randint(0,21)     
j2 = random.randint(0,4998)   
cent2 = df[i2][j2]

i3 = random.randint(0,21)     #列標0-21
j3 = random.randint(0,4998)   #行標0-4999
cent3 = df[i3][j3]

#聚類儲存列表
list1 = []
list2 = []
list3 = []
list_sum = []

result = 1

while result:
	list1.clear()
	list2.clear()
	list3.clear()
	result = tool.circle_o (df,cent1,cent2,cent3,list_sum,list1,list2,list3,result)
	

print("聚類1:前100個數:",list1[:100],'\n')
print("聚類2:前100個數:",list2[:100],'\n')
print("聚類2:前100個數:",list3[:100],'\n')

由於老師佈置的作業是需要新增20%的高斯噪聲,因此本程式碼將高斯噪聲加入。注意,這個高斯噪聲一定要加進迴圈裡,否則新增的只是一個常數。在這裡,求距離和的時候有一個簡便演算法,因為datawave是可以看成一個一維陣列,所以求距離可以不需要迴圈,直接帶公式 x_{i}\times n-sum(list) 進行計算即可。

這裡本人封裝了一個circle藉口,用來迴圈判斷各點的聚類距離並分類,最後判斷是否會生成新的聚類點,返回result。當result為0則停止迴圈。具體介面程式碼和waveform資料檔案各位按需下載。

接著是對影象分割的程式碼實現。同理,老師要求對噪聲影象進行分割,本人在程式碼中依舊對影象進行了加噪處理。

主程式碼如下:

import random 
import pandas as pd
import numpy as np
import tool

from PIL import Image

#影象大小為98*69
#print(im.size,im.format,im.mode) 顯示圖片資訊

img = Image.open("dog.jpg","r")

#加高斯噪聲
for x in range(300):
	i0 = random.randint(0,img.size[0]-1)     
	j0 = random.randint(0,img.size[1]-1)
	img.putpixel((i0,j0),(0,0,0))

img_array=img.load()

#隨機選擇四個中心點作為初始質心
#(62,44)(57,51)(83,55)(53,36)
i1 = random.randint(0,img.size[0]-1)     
j1 = random.randint(0,img.size[1]-1)   
cent1 = img_array[i1,j1]

i2 = random.randint(0,img.size[0]-1)     
j2 = random.randint(0,img.size[1]-1)   
cent2 = img_array[i2,j2]

i3 = random.randint(0,img.size[0]-1)     
j3 = random.randint(0,img.size[1]-1)   
cent3 = img_array[i3,j3]

i4 = random.randint(0,img.size[0]-1)     
j4 = random.randint(0,img.size[1]-1)   
cent4 = img_array[i4,j4]

print(i1,j1,"\n",i2,j2,"\n",i3,j3,"\n",i4,j4)

#聚類儲存列表
list1 = []
list2 = []
list3 = []
list4 = []
list_sum = []

result = 1

while result:
	list1.clear
	list2.clear
	list3.clear
	list4.clear
	result = tool.pic_o (img,img_array,cent1,cent2,cent3,cent4,list_sum,list1,list2,list3,list4,result)
	
imgnew1 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew2 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew3 = Image.new("RGB",(img.size[0],img.size[1]))
imgnew4 = Image.new("RGB",(img.size[0],img.size[1]))

#三個引數依次為R,G,B,A   R:紅 G:綠 B:藍 A:透明度
#白色(225,255,255) 黑色(0,0,0)
pixTuple = (255,255,255)
for i in range(img.size[0]):
	for j in range(img.size[1]):
		if img_array[i,j] in list1:
			imgnew1.putpixel((i,j),img_array[i,j])
		else:
			imgnew1.putpixel((i,j),pixTuple)

		if img_array[i,j] in list2:
			imgnew2.putpixel((i,j),img_array[i,j])
		else:
			imgnew2.putpixel((i,j),pixTuple)

		if img_array[i,j] in list3:
			imgnew3.putpixel((i,j),img_array[i,j])
		else:
			imgnew3.putpixel((i,j),pixTuple)

		if img_array[i,j] in list4:
			imgnew4.putpixel((i,j),img_array[i,j])
		else:
			imgnew4.putpixel((i,j),pixTuple)

imgnew1.save('list1.jpg')
imgnew2.save('list2.jpg')
imgnew3.save('list3.jpg')
imgnew4.save('list4.jpg')

print("列印圖片\n")




這裡本人分割的影象為一個較為簡單的影象,原圖和分割影象如下:

原圖:

本人將其劃分成四個聚類,分別是天空,森林,地面,狗狗,分割圖如下:

      

可以看出分割效果較差。

原因分析:第一點,初始質心選擇為通過隨機數生成,我認為質心位置當正好出現在天空,森林,狗,地面時分割效果較好,此次分割天空和地面未分割,估計是其中兩個質心的選擇點都在天空或者地面,影象中天空和地面畫素點RGB數值又較為接近,因此造成了分割失效。

改進辦法:手動選擇初始質心,將其分別選至四個分割背景。

手動取點效果:

      

可以看到經過我手動選取四個背景的質心點後,分割結果有了明顯的改觀,由於天空和地面的RBG數值存在相似點,四個聚類必定無法實現完美的分割。

詳細程式碼下載地址: