군집을 덴드로그램으로 보면 더 명확하게 해석할 수 있다. 계층적 군집화 결과는 대개 이런 식으로 보여지는데, 덴드로그램은 비교적 작은 공간에 많은 정보를 담고 있기 때문이다. 덴드로그램은 그래픽으로 표시되고 JPG로 저장되므로 http://pythonware.com에서 사용할 수 있는 Python Imaging Library(PIL)를 다운로드하십시오.
이 라이브러리에는 Windows용 설치 프로그램 및 다른 플랫폼용 소스 배포가 함께 제공된다. PIL 다운로드 및 설치에 대한 자세한 내용은 부록 A를 참조하십시오. PIL을 사용하면 텍스트와 선으로 이미지를 쉽게 생성할 수 있으며, 덴드로그램만 만들면 된다.
from PIL import Image,ImageDraw
첫 번째 단계는 주어진 클러스터의 총 높이를 반환하는 기능을 사용하는 것이다. 이미지의 전체 높이와 다양한 노드의 위치를 결정할 때, 그 총 높이를 알아야 한다. 이 클러스터가 끝점(즉, 분기 없음)인 경우, 그 높이는 1이고, 그렇지 않으면 그 가지 높이의 합이다.
def getheight(clust):
# Is this an endpoint? Then the height is just 1
if clust.left==None and clust.right==None: return 1
# Otherwise the height is the same of the heights of
# each branch
return getheight(clust.left)+getheight(clust.right)
또 하나 알아야 할 것은 루트 노드의 총 오류다. 선 길이는 각 노드에서 오차의 양으로 조정되므로 총 오차의 양에 따라 스케일링 계수를 생성하게 된다. 노드의 오류 깊이는 각 분기에서 발생할 수 있는 최대 오류일 뿐이다.
def getdepth(clust):
# The distance of an endpoint is 0.0
if clust.left==None and clust.right==None: return 0
drawdendrogram 함수는 각 최종 클러스터에 대해 20픽셀의 높이와 고정 폭을 허용하는 새로운 이미지를 생성한다. 스케일링 계수는 고정 폭을 총 깊이로 나누어 결정한다. 이 함수는 이 이미지에 대한 그리기 객체를 생성한 다음 루트 노드에서 드로잉된 객체를 호출하여 해당 위치가 이미지의 왼쪽 절반 아래에 있어야 함을 알려준다.
def drawdendrogram(clust,labels,jpeg='clusters.jpg'):
# height and width
h=getheight(clust)*20
w=1200
depth=getdepth(clust)
# width is fixed, so scale distances accordingly
scaling=float(w-150)/depth
# Create a new image with a white background
img=Image.new('RGB',(w,h),(255,255,255))
draw=ImageDraw.Draw(img)
draw.line((0,h/2,10,h/2),fill=(255,0,0))
# Draw the first node
drawnode(draw,clust,10,(h/2),scaling,labels)
img.save(jpeg,'JPEG')
여기서 중요한 기능은 클러스터와 그 위치를 차지하는 드로이드다. 어린이 노드의 높이를 재고, 어디에 있어야 하는지를 계산하고, 그 노드에 하나의 긴 수직선과 두 개의 수평선인 선을 그린다. 수평선의 길이는 클러스터에 오류가 있는 정도에 따라 결정된다. 줄이 길면 클러스터를 생성하기 위해 병합된 두 클러스터가 그렇게 비슷하지 않은 반면, 줄이 짧으면 거의 동일하다는 것을 보여준다. 그리기 기능을 클러스터에 추가하십시오.피:
def drawnode(draw,clust,x,y,scaling,labels):
if clust.id<0:
h1=getheight(clust.left)*20
h2=getheight(clust.right)*20
top=y-(h1+h2)/2
bottom=y+(h1+h2)/2
# Line length
ll=clust.distance*scaling
# Vertical line from this cluster to children
draw.line((x,top+h1/2,x,bottom-h2/2),fill=(255,0,0))
# Horizontal line to left item
draw.line((x,top+h1/2,x+ll,top+h1/2),fill=(255,0,0))
# Horizontal line to right item
draw.line((x,bottom-h2/2,x+ll,bottom-h2/2),fill=(255,0,0))
# Call the function to draw the left and right nodes
drawnode(draw,clust.left,x+ll,top+h1/2,scaling,labels)
drawnode(draw,clust.right,x+ll,bottom-h2/2,scaling,labels)
else:
# If this is an endpoint, draw the item label
draw.text((x+5,y-7),labels[clust.id],(0,0,0))
이것은 dendrogram과 함께 blogclust.jpg라는 파일을 생성하게 될 것이다. 덴드로그램은 그림 3-3에 표시된 것과 유사하게 보여야 한다. 원하는 경우 높이 및 너비 설정을 변경하여 인쇄하기 쉽거나 덜 어수선하도록 할 수 있다.