-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtreelearner.py
More file actions
163 lines (119 loc) · 5.29 KB
/
Copy pathtreelearner.py
File metadata and controls
163 lines (119 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
"""
TreeLearner
"""
from classifier import Classifier
from math import log
from rule import Rule
import random
class TreeLearner(Classifier):
def __init__(self, treshold, target, prate): # pylint: disable=E1002
self._treshold = treshold
self._targetVal = target
self._samples = []
self._pruning_rate = prate
self._prunset = []
def addSample(self, sample):
self._samples.append(sample)
def setTarget(self, target):
assert(target in self._samples[0][0].getValues())
self._targetVal = target
# 'parameters' would be used to tell which features we can use
def infer(self, parameters):
self._parameters = parameters
#TODO:this one should be in constructor
self._targetAtt = parameters[0].getName()
#mark the target as used
used = [True] + [False]*(len(parameters) - 1)
#separate the pruning dataset, is this really unbiased?
size = int(self._pruning_rate * len(self._samples))
for dummy in xrange(size):
rndidx = random.randint(0, len(self._samples) - 1)
self._prunset.append(self._samples[rndidx])
self._samples.remove(self._samples[rndidx])
#check the proportion of training and pruning
assert(abs((float(len(self._samples))/len(self._prunset)) - 1.0) < 0.1)
return self._growTree(self._samples, used)
def _growTree(self, dataset, used):
if(len(dataset) == 0):
return Leaf(0, '','')
#all positive
if(len(filter(lambda x: x[0] != self._targetVal, dataset))==0):
return Leaf(1, self._targetAtt, self._targetVal)
#all negative
if(len(filter(lambda x: x[0] == self._targetVal, dataset))==0):
return Leaf(1, self._targetAtt, 'other')
#we run out of parameters and still classification is not good.
#TODO: apply threshold
if(reduce(lambda x, y: x and y, used)):
return Leaf(0, '', '')
# idx: location of the attribute in _samples,
idx = self._chooseBestAttribute(dataset, used)
node = Node()
used[idx] = True
#split the data in as many subsets as values has the attribute.
for value in self._parameters[idx].getValues():
chunk = [it for it in dataset if it[idx] == value]
child = self._growTree(chunk, list(used))
child.addAttVal(self._parameters[idx].getName(), value)
node.addChild(child)
return node
def _chooseBestAttribute(self, dataset, used):
assert(used[0] == True)
minfo = [0] * len(self._parameters)
#compute mutual information between the class and each attribute
for idx in range(0, len(self._parameters)):
if not used[idx]:
for value in self._parameters[idx].getValues():
subset = filter(lambda x: x[idx]==value, dataset)
val_prob = float(len(subset))/len(dataset)
if val_prob == 0:
continue
#compute entropy
entropy = 0
for target in self._parameters[0].getValues():
prob = float(len(filter(lambda x: x[0] == target, subset)))/len(subset)
entropy += prob * log(prob + 0.00001)
#-P(B=b).H(A|B=b)
minfo[idx] += -val_prob * entropy
else:
#attributes already used must lose.
minfo[idx] = float("inf")
#do the actual choice according to minfo
print minfo
return min(enumerate(minfo), key=lambda x: x[1])[0]
def prune(self, rules):
prunedRules = map(lambda x: x.prune(self._prunset, self._parameters), rules)
return list(set(prunedRules))
class Node(object):
def __init__(self):
self._children = []
self._att = self._val = ''
def addChild(self, child):
self._children.append(child)
#Depth first search to collect rules
def _addClause(self):
children_list = []
for child in self._children:
children_list = children_list + child._addClause()
#children_list = filter(lambda x: x != None, children_list)
#Add my clause to every rule
local_result = []
for rule in children_list:
local_result.append(rule.addClause(self._att, self._val))
return local_result
#Better interface to not expose recursion to clients
def getRules(self):
return self._addClause()
def addAttVal(self, att, val):
self._att = att
self._val = val
class Leaf(Node):
def __init__(self, pos, targetAtt, targetVal):
self._outcome = pos
self._targetAtt = targetAtt
self._targetVal = targetVal
def _addClause(self):
if self._outcome is 1:
rule = Rule(self._targetAtt, self._targetVal)
return [rule.addClause(self._att, self._val)]
return []