my_data=[line.split('\t') for line in file('decision_tree_example.txt')]
for c, line in enumerate(my_data):
    my_data[c]=[s.strip() for s in line]
    
class decisionnode:
    def __init__(self,col=-1,value=None,results=None,tb=None,fb=None):
        self.col=col
        self.value=value
        self.results=results
        self.tb=tb
        self.fb=fb

def divideset(rows,column,value):
    split_function=None
    if isinstance(value,int) or isinstance(value,float):
        split_function=lambda row:row[column]>=value
    else:
        split_function=lambda row:row[column]==value

    set1=[row for row in rows if split_function(row)]
    set2=[row for row in rows if not split_function(row)]

    return (set1,set2)

def uniquecounts(rows, col):
    results={}
    for row in rows:
        r=row[col]
        if r not in results: results[r]=0
        results[r]+=1
    return results

def giniimpurity(rows, col):
    total=len(rows)
    counts=uniquecounts(rows,col)
    imp=0;
    for k1 in counts:
        p1=float(counts[k1])/total
        for k2 in counts:
            if k1==k2: continue
            p2=float(counts[k2])/total
            imp+=p1*p2
    return imp

def entropy(rows,col):
    from math import log
    log2=lambda x:log(x)/log(2)
    results=uniquecounts(rows,col)
    ent=0.0
    for r in results.keys():
        p=float(results[r])/len(rows)
        ent-=p*log2(p)
    return ent

set1,set2=divideset(my_data,2,'yes')
print "Entropy set1: %f " % entropy(set1,4)
print "Entropy set2: %f " % entropy(set2,4)
            
def buildtree(rows,rcol,scoref=entropy):
    if len(rows)==0: return decisionnode()
    current_score=scoref(rows,rcol)

    best_gain=0.0
    best_criteria=None
    best_sets=None

    column_count=len(rows[0])
    for col in range(0,column_count):
        if col!=rcol:
            #genera la lista di valori in questa colonna
            column_values={}
            for row in rows:
                column_values[row[col]]=1
            #partiziona rispetto a questi valori
            for value in column_values.keys():
                (set1,set2)=divideset(rows,col,value)
                #info gain
                p=float(len(set1))/len(rows)
                gain=current_score-p*scoref(set1,rcol)-(1-p)*scoref(set2,rcol)
                if gain>best_gain and len(set1)>0 and len(set2)>0:
                    best_gain=gain
                    best_criteria=(col,value)
                    best_sets=(set1,set2)
    #crea i due sottoalberi
    if best_gain>0:
        trueBranch=buildtree(best_sets[0],rcol)
        falseBranch=buildtree(best_sets[1],rcol)
        return decisionnode(col=best_criteria[0],value=best_criteria[1],tb=trueBranch,fb=falseBranch)
    else:
        return decisionnode(results=uniquecounts(rows,rcol))

def printtree(tree,indent=''):
    if tree.results!=None:
        print str(tree.results)
    else:
        print str(tree.col)+':'+str(tree.value)+'?'
        print indent+'T->',
        printtree(tree.tb,indent+'  ')
        print indent+'F->',
        printtree(tree.fb,indent+'  ')

def classify(observation,tree):
    if tree.results!=None:
        return tree.results
    else:
        v=observation[tree.col]
        branch=None
        if isinstance(v,int) or isinstance(v,float):
            if v>=tree.value: branch=tree.tb
            else: branch=tree.fb
        else:
            if v==tree.value: branch=tree.tb
            else: branch=tree.fb
        return classify(observation,branch)

tree=buildtree(my_data,4)
printtree(tree)
print classify(['(direct)','USA','yes',5],tree)

import random
        
def DTtrain(trainset, rcol):
    return buildtree(trainset,rcol)

def DTclassify(model, row):
    return classify(row,model)

def dividedata(data,test=0.3):
    trainset=[]
    testset=[]
    for row in data:
        if random.random()<test:
            testset.append(row)
        else:
            trainset.append(row)
    return trainset,testset

def testalgorithm(algf,trainset,testset,rcol):
    model=algf[0](trainset,rcol)
    error=0.0
    for row in testset:
        guess=algf[1](model,row)
        if not guess.has_key(row[rcol]):
            error=error+1.0
    return error/len(testset)

def crossvalidate(algf,data,rcol,trials=100,test=0.3):
    error=0.0
    for i in range(0,trials):
        trainset,testset=dividedata(data,test)
        error+=testalgorithm(algf,trainset,testset,rcol)
    return error/trials

mush_data=[line.split(',') for line in file('agaricus-lepiota.data.nomd')]
for c, line in enumerate(mush_data):
    mush_data[c]=[s.strip() for s in line]

#crossvalidate((DTtrain,DTclassify),mush_data[0:1000],0,1,0.3)

# per testarlo da python riga comando, eseguire:
## import treepredict
## reload(treepredict)
## print "error=" + str(treepredict.crossvalidate((treepredict.DTtrain,treepredict.DTclassify),treepredict.mush_data,0,10,0.9))
## 

# e poi provate
## print "error=" + str(treepredict.crossvalidate((treepredict.DTtrain,treepredict.DTclassify),treepredict.mush_data,0,10,0.99))


# e poi provate
## print "error=" + str(treepredict.crossvalidate((treepredict.DTtrain,treepredict.DTclassify),treepredict.mush_data,0,10,0.1))

# differenze? perche'?
