lolamontes69

Ch3 Ex5-Collective Intelligence (clusters.py rewrite)

Jun 11th, 2013
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.17 KB | None | 0 0
  1. from math import sqrt
  2. from PIL import Image, ImageDraw
  3. import random as random
  4.  
  5. def manhattan(v1, v2):
  6.     d = 0.0
  7.     for i in range(len(v1)):
  8.         d += abs(v1[i]-v2[i])
  9.     return d
  10.  
  11. def euclidean(v1, v2):
  12.     d = 0.0
  13.     for i in range(len(v1)):
  14.         d += (v1[i]-v2[i])**2
  15.     num = 1
  16.     den = 1+sqrt(d)
  17.     if den <= 0:
  18.         return 0
  19.     else:
  20.         return num/den
  21.  
  22. def pearson(v1, v2):
  23.     # Simple sums
  24.     sum1 = sum(v1)
  25.     sum2 = sum(v2)
  26.  
  27.     # Sums of the squares
  28.     sum1Sq = sum([pow(v, 2) for v in v1])
  29.     sum2Sq = sum([pow(v, 2) for v in v2])
  30.  
  31.     # Sum of the products
  32.     pSum = sum([v1[i]*v2[i] for i in range(len(v1))])
  33.  
  34.     # Calculatr r (Pearson score)
  35.     num = pSum-(sum1*sum2/len(v1))
  36.     den = sqrt((sum1Sq-pow(sum1, 2)/len(v1))*(sum2Sq-pow(sum2, 2)/len(v1)))
  37.     if den == 0: return 0
  38.  
  39.     return 1.0-num/den
  40.  
  41. def tanimoto(v1, v2):
  42.     c1, c2, shr = 0, 0, 0
  43.     for i in range(len(v1)):
  44.         if v1[i] != 0: c1 += 1   # in v1
  45.         if v2[i] != 0: c2 += 1   # in v2
  46.         if v1[i] != 0 and v2[i] != 0: shr += 1   # in both
  47.  
  48.     return 1.0 -(float(shr)/(c1+c2-shr))
  49.  
  50. def readfile(filename):
  51.     lines = [line for line in file(filename)]
  52.  
  53.     # First line is the column titles
  54.     colnames = lines[0].strip().split('\t')[1:]
  55.     rownames = []
  56.     data = []
  57.     for line in lines[1:]:
  58.         p = line.strip().split('\t')
  59.         # First column in each row is the rowname
  60.         rownames.append(p[0])
  61.         # The data for this row is the remainder of the row
  62.         data.append([float(x) for x in p[1:]])
  63.     return rownames, colnames, data
  64.  
  65. class bicluster:
  66.     def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
  67.         self.left = left
  68.         self.right = right
  69.         self.vec = vec
  70.         self.id = id
  71.         self.distance = distance
  72.  
  73. def hcluster(rows, distance=pearson):
  74.     distances = {}
  75.     currentclustid = -1
  76.  
  77.     # Clusters are initially just the rows
  78.     clust = [bicluster(rows[i], id=i) for i in range(len(rows))]
  79.  
  80.     while len(clust) > 1:
  81.         lowestpair = (0, 1)
  82.         closest = distance(clust[0].vec, clust[1].vec)
  83.  
  84.         # loop through every pair looking for the smallest distance
  85.         for i in range(len(clust)):
  86.             for j in range(i+1, len(clust)):
  87.                 # distances is the cache of distance calculations
  88.                 if (clust[i].id, clust[j].id) not in distances:
  89.                     distances[(clust[i].id, clust[j].id)] = distance(clust[i].vec, clust[j].vec)
  90.  
  91.                 d = distances[(clust[i].id, clust[j].id)]
  92.  
  93.                 if d < closest:
  94.                     closest = d
  95.                     lowestpair = (i, j)
  96.         # calculate the average of the two clusters
  97.         mergevec = [(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0 for i in range(len(clust[0].vec))]
  98.  
  99.         # create the new cluster
  100.         newcluster = bicluster(mergevec, left=clust[lowestpair[0]], right=clust[lowestpair[1]], distance=closest, id=currentclustid)
  101.  
  102.         # cluster ids that weren't in the original set are negative
  103.         currentclustid -= 1
  104.         del clust[lowestpair[1]]
  105.         del clust[lowestpair[0]]
  106.         clust.append(newcluster)
  107.     return clust[0]
  108.  
  109. def printclust(clust, labels=None, n=0):
  110.     # indent to make a hierarchy layout
  111.     for i in range(n): print ' ',
  112.     if clust.id < 0:
  113.         # negative id means that this is branch
  114.         print '-'
  115.     else:
  116.         # positive id means that this is an endpoint
  117.         if labels == None: print clust.id
  118.         else: print labels[clust.id]
  119.  
  120.     # now print the right and left branches
  121.     if clust.left != None: printclust(clust.left, labels=labels, n=n+1)
  122.     if clust.right != None: printclust(clust.right, labels=labels, n=n+1)
  123.    
  124. def getheight(clust):
  125.     # Is this an endpoint? Then the height is just 1
  126.     if clust.left == None and clust.right == None: return 1
  127.  
  128.     # Otherwise the height is the sum of the heights of each branch
  129.     return getheight(clust.left)+getheight(clust.right)
  130.  
  131. def getdepth(clust):
  132.     # The distance of an endpoint is 0.0
  133.     if clust.left == None and clust.right == None: return 0
  134.  
  135.     # The distance of a branch is the greater of its two sides plus its own distance
  136.     return max(getdepth(clust.left),getdepth(clust.right))+clust.distance
  137.  
  138. def drawdendrogram(clust, labels, jpeg='clusters.jpg'):
  139.     # height and width
  140.     h = getheight(clust)*20
  141.     w = 1200
  142.     depth = getdepth(clust)
  143.  
  144.     # width is fixed, so scale distances accordingly
  145.     scaling = float(w-150)/depth
  146.  
  147.     # Create a new image with a white background
  148.     img = Image.new('RGB',(w, h),(255,255,255))
  149.     draw = ImageDraw.Draw(img)
  150.  
  151.     draw.line((0,h/2,10,h/2),fill=(255,0,0))
  152.  
  153.     # Draw the first node
  154.     drawnode(draw,clust,10,(h/2),scaling,labels)
  155.     img.save(jpeg,'JPEG')
  156.  
  157. def drawnode(draw, clust, x, y, scaling, labels):
  158.     if clust.id<0:
  159.         h1 = getheight(clust.left)*20
  160.         h2 = getheight(clust.right)*20
  161.         top = y-(h1+h2)/2
  162.         bottom = y+(h1+h2)/2
  163.         # Line length
  164.         ll = clust.distance*scaling
  165.         # Vertical line from this cluster to children
  166.         draw.line((x,top+h1/2, x, bottom-h2/2), fill=(255,0,0))
  167.  
  168.         # Horizontal line to left item
  169.         draw.line((x,top+h1/2, x+ll, top+h1/2), fill=(255,0,0))
  170.  
  171.         # Horizontal line to right item
  172.         draw.line((x,bottom-h2/2, x+ll, bottom-h2/2), fill=(255,0,0))
  173.  
  174.         # Call the function to draw the left and right nodes
  175.         drawnode(draw, clust.left, x+ll, top+h1/2, scaling, labels)
  176.         drawnode(draw, clust.right, x+ll, bottom-h2/2, scaling, labels)
  177.     else:
  178.         # If this is an endpoint, draw the item label
  179.         draw.text((x+5,y-7), labels[clust.id],(0,0,0))
  180.  
  181. def rotatematrix(data):
  182.     newdata = []
  183.     for i in range(len(data[0])):
  184.         newrow = [data[j][i] for j in range(len(data))]
  185.         newdata.append(newrow)
  186.     return newdata
  187.  
  188. def kcluster(rows, distance=pearson, k=4):
  189.     # Determine the min and max values for each point
  190.     ranges = [(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
  191.  
  192.     # Create k randomly placed centroids
  193.     clusters = [[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]
  194.  
  195.     lastmatches=None
  196.     for t in range(100):
  197.         print 'Iteration %d' % t
  198.         bestmatches = [[] for i in range(k)]
  199.  
  200.         # find which centroid is the closest for each row
  201.         for j in range(len(rows)):
  202.             row = rows[j]
  203.             bestmatch = 0
  204.             for i in range(k):
  205.                 d = distance(clusters[i],row)
  206.                 if d < distance(clusters[bestmatch],row): bestmatch = i
  207.             bestmatches[bestmatch].append(j)
  208.  
  209.         # If the results are the same as last time, this is complete
  210.         if bestmatches == lastmatches: break
  211.         lastmatches = bestmatches
  212.  
  213.         # Move the centroids to the average of their members
  214.         for i in range(k):
  215.             avgs = [0.0]*len(rows[0])
  216.             if len(bestmatches[i]) > 0:
  217.                 for rowid in bestmatches[i]:
  218.                     for m in range(len(rows[rowid])):
  219.                         avgs[m] += rows[rowid][m]
  220.                 for j in range(len(avgs)):
  221.                     avgs[j]/=len(bestmatches[i])
  222.                 clusters[i] = avgs
  223.     distlist = {}
  224.     count = 0
  225.     for a in bestmatches:
  226.         clustersdata = clusters[count]
  227.         count += 1
  228.         for b in a:
  229.             distlist[b] = []
  230.             for d in range(len(rows[b])):
  231.                 if clustersdata[d] > rows[b][d]: result = clustersdata[d]-rows[b][d]
  232.                 else: result = rows[b][d]-clustersdata[d]
  233.                 distlist[b].append(result)
  234.     return bestmatches, distlist
  235.  
  236. def scaledown(data, distance=pearson, rate=0.01):
  237.     n = len(data)
  238.  
  239.     # The real distances between every pair of items
  240.     realdist = [[distance(data[i], data[j]) for j in range(n)] for i in range(0, n)]
  241.     outersum = 0.0
  242.  
  243.     # Randomly initialize the starting points of the locations in 2D
  244.     loc = [[random.random(),random.random()] for i in range(n)]
  245.     fakedist = [[0.0 for j in range(n)] for i in range(n)]
  246.  
  247.     lasterror = None
  248.     for m in range(0, 1000):
  249.         # Find projected distances
  250.         for i in range(n):
  251.             for j in range(n):
  252.                 fakedist[i][j]=sqrt(sum([pow(loc[i][x]-loc[j][x],2) for x in range(len(loc[i]))]))
  253.  
  254.         # Move points
  255.         grad = [[0.0, 0.0] for i in range(n)]
  256.  
  257.         totalerror = 0
  258.         for k in range(n):
  259.             for j in range(n):
  260.                 if j == k: continue
  261.                 # The error is percent difference between the distances
  262.                 errorterm = (fakedist[j][k]-realdist[j][k])/realdist[j][k]
  263.  
  264.                 # Each point needs to be moved away from or towards the other
  265.                 # point in proportion to how much error it has
  266.                 grad[k][0] += ((loc[k][0]-loc[j][0])/fakedist[j][k])*errorterm
  267.                 grad[k][1] += ((loc[k][1]-loc[j][1])/fakedist[j][k])*errorterm
  268.  
  269.                 # Keep track of the total error
  270.                 totalerror += abs(errorterm)
  271.         print totalerror
  272.  
  273.         # If the answer got worse by moving the points, we are done
  274.         if lasterror and lasterror<totalerror: break
  275.         lasterror = totalerror
  276.  
  277.         # Move each of the points by the learning rate times the gradient
  278.         for k in range(n):
  279.             loc[k][0] -= rate*grad[k][0]
  280.             loc[k][1] -= rate*grad[k][1]
  281.     print loc
  282.     return loc
  283.  
  284. def draw2d(data, labels, jpeg='mds2d.jpg'):
  285.     img=Image.new('RGB',(2000,2000),(255,255,255))
  286.     draw=ImageDraw.Draw(img)
  287.     for i in range(len(data)):
  288.         x=(data[i][0]+0.5)*1000
  289.         y=(data[i][1]+0.5)*1000
  290.         draw.text((x,y),labels[i],(0,0,0))
  291.     img.save(jpeg,'JPEG')
  292.  
  293. """
  294.   #####################
  295.   # usage of kcluster #
  296.   #####################
  297.  
  298. import clusters1 as clusters
  299. wants, people, data = clusters.readfile('minidata.txt')
  300. kclust, distlist = clusters.kcluster(data, k=3)
  301.  
  302. kclust = k-means clusters list
  303. distlist = items distance from centroids dictionary
  304. """
Advertisement
Add Comment
Please, Sign In to add comment