多維縮放
參考:
http://book.51cto.com/art/200812/103661.htm
《集體智慧編程》
多維縮放是一種可視化的數據表達方式,現實生活中數據遠超2維,多維縮放可以為數據集找到一種二維表達形式。算法根據每對數據項之間的差距情況,嘗試繪制出一幅圖來,圖中的各數據項之間的距離遠近,對應於它們彼此間的差異程度。
步驟:
(1)計算所有數據項之間的目標距離,即所有項兩兩之間的實際距離作為目標距離。距離的度量可采用歐氏距離或皮爾遜距離等。
例:有一個如表所示的4維數據集(每一項數據有4個相關的值)
一個待縮放的簡單的4維表格
A |
0.5 |
0.0 |
0.3 |
0.1 |
B |
0.4 |
0.15 |
0.2 |
0.1 |
C |
0.2 |
0.4 |
0.7 |
0.8 |
D |
1.0 |
0.3 |
0.6 |
0.0 |
利用歐幾里德距離公式,我們可以得到每兩項間的距離值。所有數據項兩兩之間的距離矩陣如下表所示。
上述示例的距離矩陣
|
A |
B |
C |
D |
A |
0.0 |
0.2 |
0.9 |
0.8 |
B |
0.2 |
0.0 |
0.9 |
0.7 |
C |
0.9 |
0.9 |
0.0 |
1.1 |
D |
0.8 |
0.7 |
1.1 |
0.0 |
(2)將數據項隨機放置在二維圖上。所有數據項兩兩之間的當前距離是隨機放置在二維圖后的實際測量距離。(即當前隨機距離)
(3)針對每兩兩構成的一對數據項,將它們的實際距離與當前在二維圖上的距離進行比較,求出一個誤差值
(4)根據誤差的情況,按照比例將每個數據項的所在位置移近或移遠少許量。每一個節點的移動,都是所有其它節點施加在該節點上的推或拉的結合效應。節點每移動一次,其當前距離和目標距離之間的差距就會減少一些。
(5)重復第三步、第四步。這一過程會不斷地重復多次,直到無法再通過移動節點來減少總體誤差為止。
輸入與輸出:實現這一功能的函數接受一個數據向量作為參數,並返回一個只包含兩列的向量,即數據項在二維圖上的X坐標和Y坐標。
例:輸入
trainset=[[0.5,0.0,0.3,0.1],
[0.4,0.15,0.2,0.1],
[0.2,0.4,0.7,0.8],
[1.0,0.3,0.6,0.0]]
輸出為(距離度量采用皮爾遜度量):
[[0.069, 0.638],
[-0.028, 0.679],
[1.581, 0.191],
[-0.045, 0.639]]
在縮放過程中一些信息可能會丟失掉,但是縮放后的結果會更加有助於我們理解算法的原理。
實現此功能的python代碼:
def scaledown(data,distance=pearson,rate=0.01):
n=len(data)
# The real distances between every pair of items
realdist=[[distance(data[i],data[j]) for j in range(n)]
for i in range(0,n)]
# Randomly initialize the starting points of the locations in 2D
loc=[[random.random(),random.random()] for i in range(n)]
fakedist=[[0.0 for j in range(n)] for i in range(n)]
lasterror=None
for m in range(0,1000):
# Find projected distances
for i in range(n):
for j in range(n):
fakedist[i][j]=sqrt(sum([pow(loc[i][x]-loc[j][x],2)
for x in range(len(loc[i]))]))
# Move points
grad=[[0.0,0.0] for i in range(n)]
totalerror=0
for k in range(n):
for j in range(n):
if j==k: continue
# The error is percent difference between the distances
errorterm=(fakedist[j][k]-realdist[j][k])/realdist[j][k]
# Each point needs to be moved away from or towards the other
# point in proportion to how much error it has
grad[k][0]+=((loc[k][0]-loc[j][0])/fakedist[j][k])*errorterm
grad[k][1]+=((loc[k][1]-loc[j][1])/fakedist[j][k])*errorterm
# Keep track of the total error
totalerror+=abs(errorterm)
print totalerror
# If the answer got worse by moving the points, we are done
if lasterror and lasterror<totalerror: break
lasterror=totalerror
# Move each of the points by the learning rate times the gradient
for k in range(n):
loc[k][0]-=rate*grad[k][0]
loc[k][1]-=rate*grad[k][1]
return loc
def draw2d(data,labels,jpeg='mds2d.jpg'):
img=Image.new('RGB',(2000,2000),(255,255,255))
draw=ImageDraw.Draw(img)
for i in range(len(data)):
x=(data[i][0]+0.5)*1000
y=(data[i][1]+0.5)*1000
draw.text((x,y),labels[i],(0,0,0))
img.save(jpeg,'JPEG')
if __name__ == '__main__':
blognames,words,data = readfile('blogdata.txt')
coords = scaledown(data)
draw2d(coords,blognames,jpeg='blogs2d.jpg')
繪制結果如下圖所示: