lolamontes69

clusters.py for Collective Intelligence Ch3 Ex2

Jun 11th, 2013
61
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.09 KB | None | 0 0
  1. from math import sqrt
  2. from PIL import Image, ImageDraw
  3. import random as random
  4.  
  5. def pearson(v1, v2):
  6.     # Simple sums
  7.     sum1 = sum(v1)
  8.     sum2 = sum(v2)
  9.  
  10.     # Sums of the squares
  11.     sum1Sq = sum([pow(v, 2) for v in v1])
  12.     sum2Sq = sum([pow(v, 2) for v in v2])
  13.  
  14.     # Sum of the products
  15.     pSum = sum([v1[i]*v2[i] for i in range(len(v1))])
  16.  
  17.     # Calculatr r (Pearson score)
  18.     num = pSum-(sum1*sum2/len(v1))
  19.     den = sqrt((sum1Sq-pow(sum1, 2)/len(v1))*(sum2Sq-pow(sum2, 2)/len(v1)))
  20.     if den == 0: return 0
  21.  
  22.     return 1.0-num/den
  23.  
  24. def tanimoto(v1, v2):
  25.     c1, c2, shr = 0, 0, 0
  26.     for i in range(len(v1)):
  27.         if v1[i] != 0: c1 += 1   # in v1
  28.         if v2[i] != 0: c2 += 1   # in v2
  29.         if v1[i] != 0 and v2[i] != 0: shr += 1   # in both
  30.  
  31.     return 1.0 -(float(shr)/(c1+c2-shr))
  32.  
  33. def readfile(filename):
  34.     lines = [line for line in file(filename)]
  35.  
  36.     # First line is the column titles
  37.     colnames = lines[0].strip().split('\t')[1:]
  38.     rownames = []
  39.     data = []
  40.     for line in lines[1:]:
  41.         p = line.strip().split('\t')
  42.         # First column in each row is the rowname
  43.         rownames.append(p[0])
  44.         # The data for this row is the remainder of the row
  45.         data.append([float(x) for x in p[1:]])
  46.     return rownames, colnames, data
  47.  
  48. class bicluster:
  49.     def __init__(self,vec,left=None,right=None,distance=0.0,id=None):
  50.         self.left = left
  51.         self.right = right
  52.         self.vec = vec
  53.         self.id = id
  54.         self.distance = distance
  55.  
  56. def hcluster(rows, distance=pearson):
  57.     distances = {}
  58.     currentclustid = -1
  59.  
  60.     # Clusters are initially just the rows
  61.     clust = [bicluster(rows[i], id=i) for i in range(len(rows))]
  62.  
  63.     while len(clust) > 1:
  64.         lowestpair = (0, 1)
  65.         closest = distance(clust[0].vec, clust[1].vec)
  66.  
  67.         # loop through every pair looking for the smallest distance
  68.         for i in range(len(clust)):
  69.             for j in range(i+1, len(clust)):
  70.                 # distances is the cache of distance calculations
  71.                 if (clust[i].id, clust[j].id) not in distances:
  72.                     distances[(clust[i].id, clust[j].id)] = distance(clust[i].vec, clust[j].vec)
  73.  
  74.                 d = distances[(clust[i].id, clust[j].id)]
  75.  
  76.                 if d < closest:
  77.                     closest = d
  78.                     lowestpair = (i, j)
  79.         # calculate the average of the two clusters
  80.         mergevec = [(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0 for i in range(len(clust[0].vec))]
  81.  
  82.         # create the new cluster
  83.         newcluster = bicluster(mergevec, left=clust[lowestpair[0]], right=clust[lowestpair[1]], distance=closest, id=currentclustid)
  84.  
  85.         # cluster ids that weren't in the original set are negative
  86.         currentclustid -= 1
  87.         del clust[lowestpair[1]]
  88.         del clust[lowestpair[0]]
  89.         clust.append(newcluster)
  90.     return clust[0]
  91.  
  92. def printclust(clust, labels=None, n=0):
  93.     # indent to make a hierarchy layout
  94.     for i in range(n): print ' ',
  95.     if clust.id < 0:
  96.         # negative id means that this is branch
  97.         print '-'
  98.     else:
  99.         # positive id means that this is an endpoint
  100.         if labels == None: print clust.id
  101.         else: print labels[clust.id]
  102.  
  103.     # now print the right and left branches
  104.     if clust.left != None: printclust(clust.left, labels=labels, n=n+1)
  105.     if clust.right != None: printclust(clust.right, labels=labels, n=n+1)
  106.    
  107. def getheight(clust):
  108.     # Is this an endpoint? Then the height is just 1
  109.     if clust.left == None and clust.right == None: return 1
  110.  
  111.     # Otherwise the height is the sum of the heights of each branch
  112.     return getheight(clust.left)+getheight(clust.right)
  113.  
  114. def getdepth(clust):
  115.     # The distance of an endpoint is 0.0
  116.     if clust.left == None and clust.right == None: return 0
  117.  
  118.     # The distance of a branch is the greater of its two sides plus its own distance
  119.     return max(getdepth(clust.left),getdepth(clust.right))+clust.distance
  120.  
  121. def drawdendrogram(clust, labels, jpeg='clusters.jpg'):
  122.     # height and width
  123.     h = getheight(clust)*20
  124.     w = 1200
  125.     depth = getdepth(clust)
  126.  
  127.     # width is fixed, so scale distances accordingly
  128.     scaling = float(w-150)/depth
  129.  
  130.     # Create a new image with a white background
  131.     img = Image.new('RGB',(w, h),(255,255,255))
  132.     draw = ImageDraw.Draw(img)
  133.  
  134.     draw.line((0,h/2,10,h/2),fill=(255,0,0))
  135.  
  136.     # Draw the first node
  137.     drawnode(draw,clust,10,(h/2),scaling,labels)
  138.     img.save(jpeg,'JPEG')
  139.  
  140. def drawnode(draw, clust, x, y, scaling, labels):
  141.     if clust.id<0:
  142.         h1 = getheight(clust.left)*20
  143.         h2 = getheight(clust.right)*20
  144.         top = y-(h1+h2)/2
  145.         bottom = y+(h1+h2)/2
  146.         # Line length
  147.         ll = clust.distance*scaling
  148.         # Vertical line from this cluster to children
  149.         draw.line((x,top+h1/2, x, bottom-h2/2), fill=(255,0,0))
  150.  
  151.         # Horizontal line to left item
  152.         draw.line((x,top+h1/2, x+ll, top+h1/2), fill=(255,0,0))
  153.  
  154.         # Horizontal line to right item
  155.         draw.line((x,bottom-h2/2, x+ll, bottom-h2/2), fill=(255,0,0))
  156.  
  157.         # Call the function to draw the left and right nodes
  158.         drawnode(draw, clust.left, x+ll, top+h1/2, scaling, labels)
  159.         drawnode(draw, clust.right, x+ll, bottom-h2/2, scaling, labels)
  160.     else:
  161.         # If this is an endpoint, draw the item label
  162.         draw.text((x+5,y-7), labels[clust.id],(0,0,0))
  163.  
  164. def rotatematrix(data):
  165.     newdata = []
  166.     for i in range(len(data[0])):
  167.         newrow = [data[j][i] for j in range(len(data))]
  168.         newdata.append(newrow)
  169.     return newdata
  170.  
  171. def kcluster(rows, distance=pearson, k=4):
  172.     # Determine the min and max values for each point
  173.     ranges = [(min([row[i] for row in rows]),max([row[i] for row in rows])) for i in range(len(rows[0]))]
  174.  
  175.     # Create k randomly placed centroids
  176.     clusters = [[random.random()*(ranges[i][1]-ranges[i][0])+ranges[i][0] for i in range(len(rows[0]))] for j in range(k)]
  177.  
  178.     lastmatches=None
  179.     for t in range(100):
  180.         print 'Iteration %d' % t
  181.         bestmatches = [[] for i in range(k)]
  182.  
  183.         # find which centroid is the closest for each row
  184.         for j in range(len(rows)):
  185.             row = rows[j]
  186.             bestmatch = 0
  187.             for i in range(k):
  188.                 d = distance(clusters[i],row)
  189.                 if d < distance(clusters[bestmatch],row): bestmatch = i
  190.             bestmatches[bestmatch].append(j)
  191.  
  192.         # If the results are the same as last time, this is complete
  193.         if bestmatches == lastmatches: break
  194.         lastmatches = bestmatches
  195.  
  196.         # Move the centroids to the average of their members
  197.         for i in range(k):
  198.             avgs = [0.0]*len(rows[0])
  199.             if len(bestmatches[i]) > 0:
  200.                 for rowid in bestmatches[i]:
  201.                     for m in range(len(rows[rowid])):
  202.                         avgs[m] += rows[rowid][m]
  203.                 for j in range(len(avgs)):
  204.                     avgs[j]/=len(bestmatches[i])
  205.                 clusters[i] = avgs
  206.  
  207.     return bestmatches
  208.  
  209. def scaledown(data, distance=pearson, rate=0.01):
  210.     n = len(data)
  211.  
  212.     # The real distances between every pair of items
  213.     realdist = [[distance(data[i], data[j]) for j in range(n)] for i in range(0, n)]
  214.  
  215.     outersum = 0.0
  216.  
  217.     # Randomly initialize the starting points of the locations in 2D
  218.     loc = [[random.random(),random.random()] for i in range(n)]
  219.     fakedist = [[0.0 for j in range(n)] for i in range(n)]
  220.  
  221.     lasterror = None
  222.     for m in range(0, 1000):
  223.         # Find projected distances
  224.         for i in range(n):
  225.             for j in range(n):
  226.                 fakedist[i][j]=sqrt(sum([pow(loc[i][x]-loc[j][x],2) for x in range(len(loc[i]))]))
  227.  
  228.         # Move points
  229.         grad = [[0.0, 0.0] for i in range(n)]
  230.  
  231.         totalerror = 0
  232.         for k in range(n):
  233.             for j in range(n):
  234.                 if j == k: continue
  235.                 # The error is percent difference between the distances
  236.                 errorterm = (fakedist[j][k]-realdist[j][k])/realdist[j][k]
  237.  
  238.                 # Each point needs to be moved away from or towards the other
  239.                 # point in proportion to how much error it has
  240.                 grad[k][0] += ((loc[k][0]-loc[j][0])/fakedist[j][k])*errorterm
  241.                 grad[k][1] += ((loc[k][1]-loc[j][1])/fakedist[j][k])*errorterm
  242.  
  243.                 # Keep track of the total error
  244.                 totalerror += abs(errorterm)
  245.         print totalerror
  246.  
  247.         # If the answer got worse by moving the points, we are done
  248.         if lasterror and lasterror<totalerror: break
  249.         lasterror = totalerror
  250.  
  251.         # Move each of the points by the learning rate times the gradient
  252.         for k in range(n):
  253.             loc[k][0] -= rate*grad[k][0]
  254.             loc[k][1] -= rate*grad[k][1]
  255.  
  256.     return loc
  257.  
  258. def draw2d(data, labels, jpeg='mds2d.jpg'):
  259.     img=Image.new('RGB',(2000,2000),(255,255,255))
  260.     draw=ImageDraw.Draw(img)
  261.     for i in range(len(data)):
  262.         x=(data[i][0]+0.5)*1000
  263.         y=(data[i][1]+0.5)*1000
  264.         draw.text((x,y),labels[i],(0,0,0))
  265.     img.save(jpeg,'JPEG')
  266.  
  267. """
  268. What this program does:
  269.  organises the data in blogdata1.txt into a visual representation of the relationships
  270.  between elents within it. ie Words and the frequency of their usage.
  271.  A potential use could be to parse a text find frequencies of word usage
  272.  and determine from the style of the language used which jargon/slang is being used
  273.  and therefore how to translate it better.
  274.  Or evaluate a persons favorite style and recommend writings in the same/similar styles.
  275.  
  276. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  277. @----Hierarchical clustering----@
  278. @-------------------------------@
  279. @ step   result                 @
  280. @ 0      1,2,5,6,11,14          @
  281. @ 1      [1,2] 5,9,17,19        @
  282. @ 2      [1,2] 5,9 [17,19]      @
  283. @ 3      [[1,2] 5] 9 [17,19]    @
  284. @ 4      [[[1,2] 5] 9] [17,19]  @
  285. @ 5      [[[1,2] 5] 9 [17,19]]  @
  286. @                               @
  287. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  288.  
  289. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         scaledown() + draw2d()
  290. @------------K Means clustering--------------@         produces a visual representation
  291. @--------------------------------------------@         kclustered words in 2D
  292. @ step   result                              @
  293. @ 0      1,2,5,6,11,14                       @
  294. @ 1      [1 2 (k4)5]         [6 (k7) 11 14]  @
  295. @ 2      [1 2 (k8/3) 5  6]   [(k31/3) 11 14] @
  296. @ 3      [1 2 (k14/4) 5 6]   [11 (k23/2) 14] @
  297. @                                            @
  298. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  299. where k is a randomly placed centroid
  300.  
  301.    blogdata1.txt is a dataset
  302. it is in the format rows columns
  303. the first row is a list of commonly used words
  304. the rest of the rows are lists of the number of times each word has been used
  305. the indexes of the times used matches the indexes of the words
  306.  
  307. readfile()       reads the data in blogdata1.txt and returns it as lists (eg blognames,words)
  308.                                                          and list of lists (eg data)
  309. hcluster()       does sim_pearson on the vectors of the words(ie times they appear)
  310. printclust()     prints out the blogs related by word use onscreen
  311. drawdendrogram() creates a jpeg dendrogram, a view of the word use relationships
  312.  
  313. rotatematrix()   creates a matrix of word - its usage in all blogs
  314.                       rather than   blog - word(usage in this blog)
  315.                 looks at individual words relationships to other words
  316.                 and shows words used with the same frequency with blogs
  317.                 when the rotated matrix is passed to drawdendrogram()
  318.  
  319.  
  320. blogs with similar word usage get linked in the dendrogram
  321.  
  322.  
  323.  
  324. @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
  325. Visualizing the workings of hcluster:
  326.  
  327. ###############################################################################
  328. ##########clust = [bicluster(rows[i], id=i) for i in range(len(rows))]#########
  329. ###############################################################################
  330.  
  331. >>> frog = [[1,2,3],[4,5,6]]
  332. >>> fruit = [bicluster(frog[i],id=i) for i in range(len(frog))]
  333. >>> fruit[1].id
  334. >>> fruit[1].vec
  335. [4, 5, 6]
  336. >>> print fruit[0].__dict__
  337. {'id': 0, 'distance': 0.0, 'right': None, 'vec': [1, 2, 3], 'left': None}
  338. >>> print fruit[1].__dict__
  339. {'id': 1, 'distance': 0.0, 'right': None, 'vec': [4, 5, 6], 'left': None}
  340. >>> print len(fruit)
  341. 2
  342.  
  343. so cluster is created as a list of instances of the class bicluster
  344.  
  345. ###############################################################################
  346. ##############################while len(clust) > 1#############################
  347. ###############################################################################
  348. Within this section
  349. len(clust) is reduced to 1
  350. The dictionary distances is created
  351.     distances(id1,id2) = sim_pearson score
  352.     where ids are from clust[instances of bicluster]
  353.  
  354.  
  355. ###############################################################################
  356. ###################for i in range(len(clust)):#################################
  357. #######################for j in range(i+1, len(clust)):########################
  358. ###############################################################################
  359. Brute-style
  360. i is an item in clust
  361. j is the rest of the items
  362. if the sim_pearson score is less than closest i,j become the lowestpair
  363.  
  364. ###############################################################################
  365. mergevec = [(clust[lowestpair[0]].vec[i]+clust[lowestpair[1]].vec[i])/2.0 for i in range(len(clust[0].vec))]
  366. ###############################################################################
  367. (clust[lowestpair[0]].vec[i]
  368.    lowestpair = (A, B)
  369.    A is an instance of clust
  370.    .vec[i] is a vector from list .vec
  371.    therefore gets the averages of all word instances for A, B and gets average usage
  372.    
  373. ###############################################################################
  374. newcluster = bicluster(mergevec, left=clust[lowestpair[0]], right=clust[lowestpair[1]], distance=closest, id=currentclustid)
  375. ###############################################################################
  376.  
  377. newcluster is an instance of bicluster
  378.   .vec is the list of averages from A(for i...) and B(for j...)
  379.   left is instance of bicluster A
  380.   right is instance of bicluster A
  381.   id starts at -1 and goes down every round by -1
  382.  
  383.   the two lowestclusters having been joined together in the new cluster
  384.   have their originaL instances deleted
  385.  
  386.   and we return to the beginning of while len(clust) > 1
  387.   the two deleted instances are represented in cluster by
  388.    a single instance with the originals in .left .right
  389.    the new instance has average vecs
  390.  
  391.   if on the next round of while the lowest matching pair contains newcluster
  392.   then newcluster will be added(together with .right .left) as .left or .right
  393.   of this new newcluster
  394.  
  395.   this process will continue until all bicluster instances are matrixed together
  396.  
  397. ATALHEA
  398.  
  399. Example usage:
  400.        
  401. import clusters as clusters
  402. blognames, words, data=clusters.readfile('deliciousdata1.txt')
  403. clust=clusters.hcluster(data)
  404. rdata=clusters.rotatematrix(data)
  405. wordclust = clusters.hcluster(rdata)
  406.  
  407. clusters.drawdendrogram(wordclust, labels=words, jpeg='deliciousclust.jpg')
  408.  
  409. clusters.drawdendrogram(clust, labels=blognames, jpeg='deliciousclust.jpg')
  410.  
  411.  
  412.  
  413. kclust = clusters.kcluster(data,k=10)
  414. [blognames[r] for r in kclust[0]]
  415. -------------------------------------------------------------------------------
  416. ##########################
  417. # what does scaledown do #
  418. ##########################
  419. Return a list containig lists of coordinates
  420. First it calculates the distances between all items
  421. Then using a Kcluster-like method it produces coordinates x and y
  422.    for each item
  423. It does the kclustering process m times less times = less accuracy
  424.                                        more times = more time to calculate
  425. Each item is initially given a random coord and is moved towards similar items
  426. All items are moved m times sorting themselves into a more meaningful cluster
  427.  
  428.  
  429. """
Advertisement
Add Comment
Please, Sign In to add comment