-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathdecision_tree.py
More file actions
155 lines (133 loc) · 5.16 KB
/
Copy pathdecision_tree.py
File metadata and controls
155 lines (133 loc) · 5.16 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
import logging
import numpy as np
from .tree import TreeNode
logging.basicConfig()
logger = logging.getLogger(__file__)
logger.setLevel(logging.INFO)
class DecisionTree():
def __init__(self,
max_depth=2,
min_samples_split=2,
min_samples_leaf=1,
n_classes=2,
max_features=None,
impurity='gini',
is_classifier=True):
"""Decision tree model
Parameters:
----------
max_depth: int
The maximum depth allowed when "growing" a tree
min_samples_split: int
The minimum number of samples required to allow a split at a
node
min_samples_leaf: int
The minimum number of samples allowed in a leaf. A split
candidate leading to less samples in a node than the
min_samples_leaf will be rejected
n_classes: int, optional, default 2
Number of classes in a classification setting. Ignored when
self.is_classifier = False
max_features: int, optional, default None
If set to 'sqrt' then only a random subset of features are
used to split at each node, the number of features used in
this case is sqrt(n_features).
Else all the features are considered when splitting at each
node
impurity: str, optional, default 'gini'
The impurity measure to use when splitting at each node.
I have currently only implemented two
'gini' - Uses the gini impurity (for classification)
'mse' - Uses the mean square error - equal to variance (for
regression)
is_classifier: bool, optional, default True
Is the model used as part of a classification problem
or a regression problem. Should be set to True if
classification, False if regression
"""
self.max_depth = max_depth
self.min_samples_split = min_samples_split
self.min_samples_leaf = min_samples_leaf
self.n_classes = n_classes
self.max_features = max_features
self.impurity = impurity
self.is_classifier = is_classifier
self.is_fitted = False
self.tree = None
def fit(self, X, y):
"""Fits the decision tree model
The tree is fitted by instantiaing a root TreeNode instance and
then calling the recursive_split method. This iteratively grows
the tree by finding the best split to reduce the impurity the
most.
Parameters:
----------
X: numpy.ndarray
Training data, shape (m samples, n features)
y: numpy.ndarray
Target values, shape (m samples, 1)
If classifier with n_classes the values are assumed to be in
0, ..., n-1
"""
y_shape = (X.shape[0], 1)
data = np.concatenate((X, y.reshape(y_shape)), axis=1)
self.tree = TreeNode(
data=data,
max_depth=self.max_depth,
min_samples_split=self.min_samples_split,
min_samples_leaf=self.min_samples_leaf,
n_classes=self.n_classes,
max_features=self.max_features,
impurity=self.impurity,
is_classifier=self.is_classifier)
self.tree.recursive_split()
self.is_fitted = True
def predict(self, data):
"""Predicts target values or class labels for classification
Predicts target values/class for each row in data by walking the
tree and returning the leaf node value for regression or the
class with the largest predicted probability for classification
Parameters:
----------
data: numpy.ndarray
The input data with shape (m samples, n features)
Returns:
-------
numpy.ndarray:
Predicted target values or class labels for classification
"""
if not self.is_fitted:
raise Exception('Decision tree not fitted')
return self.tree.predict(data)
def predict_proba(self, data):
"""Predicts class probabilities for input data
Predicts class probabilities for each row in data by walking the
tree and returning the leaf node class probabilities
Parameters:
----------
data: numpy.ndarray
The input data with shape (m samples, n features)
Returns:
-------
numpy.ndarray:
Predicted sample class probabilities,
shape (m samples, n classes)
"""
if not self.is_fitted:
raise Exception('Decision tree not fitted')
return self.tree.predict_proba(data)
def render(self, feature_names):
"""Returns Digraph visualizing the decision tree (if fitted)
Parameters:
----------
feature_names: list[str]
List of feature names
Returns:
-------
graphviz.Digraph:
dot for tree diagram visual
"""
if not self.is_fitted:
print('Decision tree not fitted')
else:
return self.tree.dot(feature_names=feature_names)