lolamontes69

Ch7 Ex4-Programming Collective Intelligence

Jul 29th, 2013
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.74 KB | None | 0 0
  1. """ Chapter 7 Exercise 4: Building with missing data.
  2.  
  3. >>> You built a function that can classify a row with missing data, but what if there is missing data in the training set?
  4.    Modify buildtree so that it will check for missing data and, in cases where it's not possible to send a result down a particular branch, will send it down both branches.  <<<
  5.  
  6.    To deal with missing data I created a function called find_missingvalues() that takes the training set and checks it for missing data. It does this by comparing values in the short row being checked against values in a dictionary from full rows.
  7.    This assumes that all values will be in more than one row. I made an exception for the integers as these are more likely to be unreplicated, so I just check that an integer is in the integer column. When missing data is discovered it adds the token 'MISSING' to the column.
  8.  
  9.    So now in buildtree() I don't split according to 'MISSING' being in the row, but when it is put into a FalseBranch I also place it into a TrueBranch, and vice-versa.
  10.  
  11.    I hope this at least makes a little sense :)
  12.    If not just read the code :p
  13.  
  14. """
  15.  
  16. from PIL import Image,ImageDraw
  17.  
  18. my_data=[line.split('\t') for line in file('decision_tree_example2.txt')]
  19. # Replace '/n' or it taints the data (example:{'Basic\n': 4, 'Premium\n': 1, 'Basic': 1})
  20. for a in range(len(my_data)):
  21.     for b in range(len(my_data[a])):
  22.         my_data[a][b] = my_data[a][b].replace('\n','')
  23.  
  24. # Checks my_data for missing values
  25. def find_missingvalues(rows):
  26.     col_contents={}
  27.     missinglist=[]
  28.     column_count=len(rows[0])-1
  29.     for col in range(0,column_count):
  30.         for row in rows:
  31.             if row in missinglist: pass
  32.             else:
  33.                 if (len(row)!=len(rows[0])) and (row not in missinglist): missinglist.append(row)
  34.                 else:
  35.                     if col not in col_contents:
  36.                         col_contents[col]=[]
  37.                         col_contents[col].append(row[col])
  38.                     elif row[col] not in col_contents[col]:
  39.                         col_contents[col].append(row[col])
  40.     for col in range(0,column_count):
  41.         col_contents[col].append('MISSING')
  42.     for a in missinglist:
  43.         hurr = 0
  44.         indy = rows.index(a)
  45.         while hurr==0:
  46.             for b in range(len(a)):
  47.                 try:
  48.                     x = int(a[b]) - int(col_contents[b][0])   # Integers may not have duplicates
  49.                 except:
  50.                     if a[b] in col_contents[b]: pass
  51.                     else:
  52.                         c = a
  53.                         c.insert(int(b),'MISSING')
  54.                         hurr = 1
  55.                         return rows
  56.             if len(rows[indy]) == len(rows[0])-1:
  57.                 c = a
  58.                 c.insert(int(b+1),'None')  # If the end result is missing classify it as None
  59.                 rows[indy]=c
  60.                 hurr=1
  61.     return rows
  62.  
  63. loop = 0
  64. while loop==0:
  65.     count=0
  66.     for row in my_data:
  67.         if (len(row)!=len(my_data[0])):
  68.             my_data = find_missingvalues(my_data)
  69.     for row in my_data:
  70.         if (len(row)!=len(my_data[0])): count+=1
  71.     if count==0: loop=1
  72.  
  73. class decisionnode:
  74.     def __init__(self,col=-1,value=None,results=None,tb=None,fb=None):
  75.         self.col=col
  76.         self.value=value
  77.         self.results=results
  78.         self.tb=tb
  79.         self.fb=fb
  80.  
  81. # Divides a set on a specific column. Can handle numeric or nominal values
  82. def divideset(rows,column,value):
  83.     # Make a function that tells us if a row is in
  84.     # the first group (true) or the second (false)
  85.     split_function=None
  86.     if isinstance(value,int) or isinstance(value,float):
  87.         split_function=lambda row:row[column]>=value
  88.     else:
  89.         split_function=lambda row:row[column]==value
  90.        
  91.     # Divide the rows into two sets and return them
  92.     set1=[row for row in rows if split_function(row)]
  93.     set2=[row for row in rows if not split_function(row)]
  94.     return (set1,set2)
  95.  
  96. # Create counts of possible results ( the last column of each row is the result)
  97. def uniquecounts(rows):
  98.     results={}
  99.     for row in rows:
  100.         # The result is the last column
  101.         r=row[len(row)-1]
  102.         if r not in results: results[r]=0
  103.         results[r]+=1
  104.     return results
  105.  
  106. # Probability that a randomly placed item will be in the wrong category
  107. def giniimpurity(rows):
  108.     total=len(rows)
  109.     counts=uniquecounts(rows)
  110.     imp=0
  111.     for k1 in counts:
  112.         p1=float(counts[k1])/total
  113.         for k2 in counts:
  114.             if k1==k2: continue
  115.             p2=float(counts[k2])/total
  116.             imp+=p1*p2
  117.     return imp
  118.  
  119. # Entropy is the sum of p(x)log(p(x)) across all the different possible results
  120. def entropy(rows):
  121.     from math import log
  122.     log2=lambda x:log(x)/log(2)
  123.     results=uniquecounts(rows)
  124.     # Now calculate the entropy
  125.     ent=0.0
  126.     for r in results.keys():
  127.         p=float(results[r])/len(rows)
  128.         ent=ent-p*log2(p)
  129.     return ent
  130.  
  131. def buildtree(rows,scoref=entropy):
  132.     if len(rows)==0: return decisionnode()
  133.     current_score=scoref(rows)
  134.  
  135.     # set up some variables to track the best criteria
  136.     best_gain=0.0
  137.     best_criteria=None
  138.     best_sets=None
  139.  
  140.     column_count=len(rows[0])-1
  141.     for col in range(0,column_count):
  142.         # Generate the list of different values in this column
  143.         column_values={}
  144.         for row in rows:
  145.             column_values[row[col]]=1
  146.         # Now try dividing the rows up for each value in this column
  147.         for value in column_values.keys():
  148.             # Don't split according to whether MISSING or not
  149.             if value == 'MISSING': pass
  150.             else:
  151.                 (set1,set2)=divideset(rows,col,value)
  152.                 # If MISSING is in col put it in both sets
  153.                 for a in set1:
  154.                     if (a[col]=='MISSING') and (a not in set2):
  155.                         set2.append(a)
  156.                 for a in set2:
  157.                     if (a[col]=='MISSING') and (a not in set1):
  158.                         set1.append(a)
  159.                 # Information gain
  160.                 p=float(len(set1))/len(rows)
  161.                 gain=current_score-p*scoref(set1)-(1-p)*scoref(set2)
  162.                 if gain>best_gain and len(set1)>0 and len(set2)>0:
  163.                     best_gain=gain
  164.                     best_criteria=(col,value)
  165.                     best_sets=(set1,set2)
  166.     # Create the subbranches
  167.     if best_gain>0:
  168.         trueBranch=buildtree(best_sets[0],scoref)                        # corrected adding scoref
  169.         falseBranch=buildtree(best_sets[1],scoref)                       # corrected adding scoref
  170.         return decisionnode(col=best_criteria[0],value=best_criteria[1],
  171.                             tb=trueBranch,fb=falseBranch)
  172.     else:
  173.         return decisionnode(results=uniquecounts(rows))
  174.  
  175. def printtree(tree,indent='  '):
  176.     # Is this a leaf node?
  177.     if tree.results!=None:
  178.         print str(tree.results)
  179.     else:
  180.         # Print the criteria
  181.         print str(tree.col)+':'+str(tree.value)+'? '
  182.  
  183.         # Print the branches
  184.         print indent+'T->',
  185.         printtree(tree.tb,indent+'  ')
  186.         print indent+'F->',
  187.         printtree(tree.fb,indent+'  ')
  188.  
  189. def getwidth(tree):
  190.     if tree.tb==None and tree.fb==None: return 1
  191.     return getwidth(tree.tb)+getwidth(tree.fb)
  192.  
  193. def getdepth(tree):
  194.     if tree.tb==None and tree.fb==None: return 0
  195.     return max(getdepth(tree.tb),getdepth(tree.fb))+1
  196.  
  197. def drawtree(tree,jpeg='tree.jpg'):
  198.     w=getwidth(tree)*100
  199.     h=getdepth(tree)*100+120
  200.  
  201.     img=Image.new('RGB',(w,h),(255,255,255))
  202.     draw=ImageDraw.Draw(img)
  203.  
  204.     drawnode(draw,tree,w/2,20)
  205.     img.save(jpeg,'JPEG')
  206.  
  207. def drawnode(draw,tree,x,y):
  208.     if tree.results==None:
  209.         # Get the width of each branch
  210.         w1=getwidth(tree.fb)*100
  211.         w2=getwidth(tree.tb)*100
  212.  
  213.         # Determine the total space required by this node
  214.         left=x-(w1+w2)/2
  215.         right=x+(w1+w2)/2
  216.  
  217.         # Draw the condition string
  218.         draw.text((x-20,y-10),str(tree.col)+':'+str(tree.value),(0,0,0))
  219.  
  220.         # Draw links to the branches
  221.         draw.line((x,y,left+w1/2,y+100),fill=(255,0,0))
  222.         draw.line((x,y,right-w2/2,y+100),fill=(255,0,0))
  223.  
  224.         # Draw the branch nodes
  225.         drawnode(draw,tree.fb,left+w1/2,y+100)
  226.         drawnode(draw,tree.tb,right-w2/2,y+100)
  227.     else:
  228.         txt=' \n'.join(['%s:%d'%v for v in tree.results.items()])
  229.         draw.text((x-20,y),txt,(0,0,0))
  230.  
  231. def classify(observation,tree):
  232.     if tree.results!=None:
  233.         return tree.results
  234.     else:
  235.         v=observation[tree.col]
  236.         branch=None
  237.         if isinstance(v,int) or isinstance(v,float):
  238.             if v>=tree.value: branch=tree.tb
  239.             else: branch=tree.fb
  240.         else:
  241.             if v==tree.value: branch=tree.tb
  242.             else: branch=tree.fb
  243.         return classify(observation,branch)
  244.  
  245. def prune(tree,mingain):
  246.     # If the branches aren't leaves prune them
  247.     if tree.tb.results==None:
  248.         prune(tree.tb,mingain)
  249.     if tree.fb.results==None:
  250.         prune(tree.fb,mingain)
  251.  
  252.     # If both the subbranches are now leaves, see if they should be merged
  253.     if tree.tb.results!=None and tree.fb.results!=None:
  254.         # Build a combined dataset
  255.         tb,fb=[],[]
  256.         for v,c in tree.tb.results.items():
  257.             tb+=[[v]]*c
  258.         for v,c in tree.fb.results.items():
  259.             fb+=[[v]]*c
  260.  
  261.         # Test the reduction in entropy
  262.         delta=entropy(tb+fb)-(entropy(tb)+entropy(fb))/2     # Xiaochen Wang CORRECTION
  263.         if delta<mingain:
  264.             # Merge the branches
  265.             tree.tb,tree.fb=None,None
  266.             tree.results=uniquecounts(tb+fb)
  267.  
  268. def mdclassify(observation,tree):
  269.     if tree.results!=None:
  270.         return tree.results
  271.     else:
  272.         v=observation[tree.col]
  273.         if v==None:
  274.             tr,fr=mdclassify(observation,tree.tb),mdclassify(observation,tree.fb)
  275.             tcount=sum(tr.values())
  276.             fcount=sum(fr.values())
  277.             tw=float(tcount)/(tcount+fcount)
  278.             fw=float(fcount)/(tcount+fcount)
  279.             result={}
  280.             for k,v in tr.items(): result[k]=v*tw
  281.             for k,v in fr.items(): result[k]=v*fw
  282.             return result
  283.         else:
  284.             if isinstance(v,int) or isinstance(v,float):
  285.                 if v>tree.value: branch=tree.tb
  286.                 else: branch=tree.fb
  287.             else:
  288.                 if v==tree.value: branch=tree.tb
  289.                 else: branch=tree.fb
  290.             return mdclassify(observation,branch)
  291.  
  292. def variance(rows):
  293.     if len(rows)==0: return 0
  294.     data=[float(row[len(row)-1]) for row in rows]
  295.     mean=sum(data)/len(data)
  296.     variance=sum([(d-mean)**2 for d in data])/len(data)
  297.     return variance
Advertisement
Add Comment
Please, Sign In to add comment