diff --git a/doc/src/DecisionTrees/cancer.dot b/doc/src/DecisionTrees/cancer.dot new file mode 100644 index 000000000..a0d5d819c --- /dev/null +++ b/doc/src/DecisionTrees/cancer.dot @@ -0,0 +1,57 @@ +digraph Tree { +node [shape=box, style="filled, rounded", color="black", fontname=helvetica] ; +edge [fontname=helvetica] ; +0 [label="worst perimeter <= 106.05\ngini = 0.465\nsamples = 426\nvalue = [[269, 157]\n[157, 269]]", fillcolor="#e5813908"] ; +1 [label="worst concave points <= 0.159\ngini = 0.067\nsamples = 259\nvalue = [[250, 9]\n[9, 250]]", fillcolor="#e58139db"] ; +0 -> 1 [labeldistance=2.5, labelangle=45, headlabel="True"] ; +2 [label="worst concave points <= 0.135\ngini = 0.031\nsamples = 253\nvalue = [[249, 4]\n[4, 249]]", fillcolor="#e58139ee"] ; +1 -> 2 ; +3 [label="area error <= 48.975\ngini = 0.008\nsamples = 242\nvalue = [[241, 1]\n[1, 241]]", fillcolor="#e58139fb"] ; +2 -> 3 ; +4 [label="gini = 0.0\nsamples = 239\nvalue = [[239, 0]\n[0, 239]]", fillcolor="#e58139ff"] ; +3 -> 4 ; +5 [label="symmetry error <= 0.025\ngini = 0.444\nsamples = 3\nvalue = [[2, 1]\n[1, 2]]", fillcolor="#e5813913"] ; +3 -> 5 ; +6 [label="gini = 0.0\nsamples = 1\nvalue = [[0, 1]\n[1, 0]]", fillcolor="#e58139ff"] ; +5 -> 6 ; +7 [label="gini = 0.0\nsamples = 2\nvalue = [[2, 0]\n[0, 2]]", fillcolor="#e58139ff"] ; +5 -> 7 ; +8 [label="mean texture <= 20.84\ngini = 0.397\nsamples = 11\nvalue = [[8, 3]\n[3, 8]]", fillcolor="#e581392c"] ; +2 -> 8 ; +9 [label="gini = 0.0\nsamples = 8\nvalue = [[8, 0]\n[0, 8]]", fillcolor="#e58139ff"] ; +8 -> 9 ; +10 [label="gini = 0.0\nsamples = 3\nvalue = [[0, 3]\n[3, 0]]", fillcolor="#e58139ff"] ; +8 -> 10 ; +11 [label="worst texture <= 24.785\ngini = 0.278\nsamples = 6\nvalue = [[1, 5]\n[5, 1]]", fillcolor="#e581396b"] ; +1 -> 11 ; +12 [label="gini = 0.0\nsamples = 1\nvalue = [[1, 0]\n[0, 1]]", fillcolor="#e58139ff"] ; +11 -> 12 ; +13 [label="gini = 0.0\nsamples = 5\nvalue = [[0, 5]\n[5, 0]]", fillcolor="#e58139ff"] ; +11 -> 13 ; +14 [label="worst texture <= 20.645\ngini = 0.202\nsamples = 167\nvalue = [[19, 148]\n[148, 19]]", fillcolor="#e5813994"] ; +0 -> 14 [labeldistance=2.5, labelangle=-45, headlabel="False"] ; +15 [label="worst perimeter <= 116.8\ngini = 0.375\nsamples = 16\nvalue = [[12, 4]\n[4, 12]]", fillcolor="#e5813938"] ; +14 -> 15 ; +16 [label="gini = 0.0\nsamples = 11\nvalue = [[11, 0]\n[0, 11]]", fillcolor="#e58139ff"] ; +15 -> 16 ; +17 [label="mean concavity <= 0.06\ngini = 0.32\nsamples = 5\nvalue = [[1, 4]\n[4, 1]]", fillcolor="#e5813955"] ; +15 -> 17 ; +18 [label="gini = 0.0\nsamples = 1\nvalue = [[1, 0]\n[0, 1]]", fillcolor="#e58139ff"] ; +17 -> 18 ; +19 [label="gini = 0.0\nsamples = 4\nvalue = [[0, 4]\n[4, 0]]", fillcolor="#e58139ff"] ; +17 -> 19 ; +20 [label="mean concave points <= 0.049\ngini = 0.088\nsamples = 151\nvalue = [[7, 144]\n[144, 7]]", fillcolor="#e58139d0"] ; +14 -> 20 ; +21 [label="concave points error <= 0.01\ngini = 0.48\nsamples = 15\nvalue = [[6, 9]\n[9, 6]]", fillcolor="#e5813900"] ; +20 -> 21 ; +22 [label="gini = 0.0\nsamples = 9\nvalue = [[0, 9]\n[9, 0]]", fillcolor="#e58139ff"] ; +21 -> 22 ; +23 [label="gini = 0.0\nsamples = 6\nvalue = [[6, 0]\n[0, 6]]", fillcolor="#e58139ff"] ; +21 -> 23 ; +24 [label="mean smoothness <= 0.079\ngini = 0.015\nsamples = 136\nvalue = [[1, 135]\n[135, 1]]", fillcolor="#e58139f7"] ; +20 -> 24 ; +25 [label="gini = 0.0\nsamples = 1\nvalue = [[1, 0]\n[0, 1]]", fillcolor="#e58139ff"] ; +24 -> 25 ; +26 [label="gini = 0.0\nsamples = 135\nvalue = [[0, 135]\n[135, 0]]", fillcolor="#e58139ff"] ; +24 -> 26 ; +} \ No newline at end of file diff --git a/doc/src/DecisionTrees/cart.py b/doc/src/DecisionTrees/cart.py new file mode 100644 index 000000000..afcfa34c3 --- /dev/null +++ b/doc/src/DecisionTrees/cart.py @@ -0,0 +1,172 @@ +# CART on the Bank Note dataset +from random import seed +from random import randrange +from csv import reader + +# Load a CSV file +def load_csv(filename): + file = open(filename, "rb") + lines = reader(file) + dataset = list(lines) + return dataset + +# Convert string column to float +def str_column_to_float(dataset, column): + for row in dataset: + row[column] = float(row[column].strip()) + +# Split a dataset into k folds +def cross_validation_split(dataset, n_folds): + dataset_split = list() + dataset_copy = list(dataset) + fold_size = int(len(dataset) / n_folds) + for i in range(n_folds): + fold = list() + while len(fold) < fold_size: + index = randrange(len(dataset_copy)) + fold.append(dataset_copy.pop(index)) + dataset_split.append(fold) + return dataset_split + +# Calculate accuracy percentage +def accuracy_metric(actual, predicted): + correct = 0 + for i in range(len(actual)): + if actual[i] == predicted[i]: + correct += 1 + return correct / float(len(actual)) * 100.0 + +# Evaluate an algorithm using a cross validation split +def evaluate_algorithm(dataset, algorithm, n_folds, *args): + folds = cross_validation_split(dataset, n_folds) + scores = list() + for fold in folds: + train_set = list(folds) + train_set.remove(fold) + train_set = sum(train_set, []) + test_set = list() + for row in fold: + row_copy = list(row) + test_set.append(row_copy) + row_copy[-1] = None + predicted = algorithm(train_set, test_set, *args) + actual = [row[-1] for row in fold] + accuracy = accuracy_metric(actual, predicted) + scores.append(accuracy) + return scores + +# Split a dataset based on an attribute and an attribute value +def test_split(index, value, dataset): + left, right = list(), list() + for row in dataset: + if row[index] < value: + left.append(row) + else: + right.append(row) + return left, right + +# Calculate the Gini index for a split dataset +def gini_index(groups, classes): + # count all samples at split point + n_instances = float(sum([len(group) for group in groups])) + # sum weighted Gini index for each group + gini = 0.0 + for group in groups: + size = float(len(group)) + # avoid divide by zero + if size == 0: + continue + score = 0.0 + # score the group based on the score for each class + for class_val in classes: + p = [row[-1] for row in group].count(class_val) / size + score += p * p + # weight the group score by its relative size + gini += (1.0 - score) * (size / n_instances) + return gini + +# Select the best split point for a dataset +def get_split(dataset): + class_values = list(set(row[-1] for row in dataset)) + b_index, b_value, b_score, b_groups = 999, 999, 999, None + for index in range(len(dataset[0])-1): + for row in dataset: + groups = test_split(index, row[index], dataset) + gini = gini_index(groups, class_values) + if gini < b_score: + b_index, b_value, b_score, b_groups = index, row[index], gini, groups + return {'index':b_index, 'value':b_value, 'groups':b_groups} + +# Create a terminal node value +def to_terminal(group): + outcomes = [row[-1] for row in group] + return max(set(outcomes), key=outcomes.count) + +# Create child splits for a node or make terminal +def split(node, max_depth, min_size, depth): + left, right = node['groups'] + del(node['groups']) + # check for a no split + if not left or not right: + node['left'] = node['right'] = to_terminal(left + right) + return + # check for max depth + if depth >= max_depth: + node['left'], node['right'] = to_terminal(left), to_terminal(right) + return + # process left child + if len(left) <= min_size: + node['left'] = to_terminal(left) + else: + node['left'] = get_split(left) + split(node['left'], max_depth, min_size, depth+1) + # process right child + if len(right) <= min_size: + node['right'] = to_terminal(right) + else: + node['right'] = get_split(right) + split(node['right'], max_depth, min_size, depth+1) + +# Build a decision tree +def build_tree(train, max_depth, min_size): + root = get_split(train) + split(root, max_depth, min_size, 1) + return root + +# Make a prediction with a decision tree +def predict(node, row): + if row[node['index']] < node['value']: + if isinstance(node['left'], dict): + return predict(node['left'], row) + else: + return node['left'] + else: + if isinstance(node['right'], dict): + return predict(node['right'], row) + else: + return node['right'] + +# Classification and Regression Tree Algorithm +def decision_tree(train, test, max_depth, min_size): + tree = build_tree(train, max_depth, min_size) + predictions = list() + for row in test: + prediction = predict(tree, row) + predictions.append(prediction) + return(predictions) + +# Test CART on Bank Note dataset +seed(1) +# load and prepare data +filename = 'DataFiles/rideclass.csv' +dataset = load_csv(filename) +# convert string attributes to integers +for i in range(len(dataset[0])): + str_column_to_float(dataset, i) +# evaluate algorithm +n_folds = 5 +max_depth = 5 +min_size = 10 +scores = evaluate_algorithm(dataset, decision_tree, n_folds, max_depth, min_size) +print('Scores: %s' % scores) +print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores)))) diff --git a/doc/src/DecisionTrees/cart.py~ b/doc/src/DecisionTrees/cart.py~ new file mode 100644 index 000000000..ee6c049b7 --- /dev/null +++ b/doc/src/DecisionTrees/cart.py~ @@ -0,0 +1,172 @@ +# CART on the Bank Note dataset +from random import seed +from random import randrange +from csv import reader + +# Load a CSV file +def load_csv(filename): + file = open(filename, "rb") + lines = reader(file) + dataset = list(lines) + return dataset + +# Convert string column to float +def str_column_to_float(dataset, column): + for row in dataset: + row[column] = float(row[column].strip()) + +# Split a dataset into k folds +def cross_validation_split(dataset, n_folds): + dataset_split = list() + dataset_copy = list(dataset) + fold_size = int(len(dataset) / n_folds) + for i in range(n_folds): + fold = list() + while len(fold) < fold_size: + index = randrange(len(dataset_copy)) + fold.append(dataset_copy.pop(index)) + dataset_split.append(fold) + return dataset_split + +# Calculate accuracy percentage +def accuracy_metric(actual, predicted): + correct = 0 + for i in range(len(actual)): + if actual[i] == predicted[i]: + correct += 1 + return correct / float(len(actual)) * 100.0 + +# Evaluate an algorithm using a cross validation split +def evaluate_algorithm(dataset, algorithm, n_folds, *args): + folds = cross_validation_split(dataset, n_folds) + scores = list() + for fold in folds: + train_set = list(folds) + train_set.remove(fold) + train_set = sum(train_set, []) + test_set = list() + for row in fold: + row_copy = list(row) + test_set.append(row_copy) + row_copy[-1] = None + predicted = algorithm(train_set, test_set, *args) + actual = [row[-1] for row in fold] + accuracy = accuracy_metric(actual, predicted) + scores.append(accuracy) + return scores + +# Split a dataset based on an attribute and an attribute value +def test_split(index, value, dataset): + left, right = list(), list() + for row in dataset: + if row[index] < value: + left.append(row) + else: + right.append(row) + return left, right + +# Calculate the Gini index for a split dataset +def gini_index(groups, classes): + # count all samples at split point + n_instances = float(sum([len(group) for group in groups])) + # sum weighted Gini index for each group + gini = 0.0 + for group in groups: + size = float(len(group)) + # avoid divide by zero + if size == 0: + continue + score = 0.0 + # score the group based on the score for each class + for class_val in classes: + p = [row[-1] for row in group].count(class_val) / size + score += p * p + # weight the group score by its relative size + gini += (1.0 - score) * (size / n_instances) + return gini + +# Select the best split point for a dataset +def get_split(dataset): + class_values = list(set(row[-1] for row in dataset)) + b_index, b_value, b_score, b_groups = 999, 999, 999, None + for index in range(len(dataset[0])-1): + for row in dataset: + groups = test_split(index, row[index], dataset) + gini = gini_index(groups, class_values) + if gini < b_score: + b_index, b_value, b_score, b_groups = index, row[index], gini, groups + return {'index':b_index, 'value':b_value, 'groups':b_groups} + +# Create a terminal node value +def to_terminal(group): + outcomes = [row[-1] for row in group] + return max(set(outcomes), key=outcomes.count) + +# Create child splits for a node or make terminal +def split(node, max_depth, min_size, depth): + left, right = node['groups'] + del(node['groups']) + # check for a no split + if not left or not right: + node['left'] = node['right'] = to_terminal(left + right) + return + # check for max depth + if depth >= max_depth: + node['left'], node['right'] = to_terminal(left), to_terminal(right) + return + # process left child + if len(left) <= min_size: + node['left'] = to_terminal(left) + else: + node['left'] = get_split(left) + split(node['left'], max_depth, min_size, depth+1) + # process right child + if len(right) <= min_size: + node['right'] = to_terminal(right) + else: + node['right'] = get_split(right) + split(node['right'], max_depth, min_size, depth+1) + +# Build a decision tree +def build_tree(train, max_depth, min_size): + root = get_split(train) + split(root, max_depth, min_size, 1) + return root + +# Make a prediction with a decision tree +def predict(node, row): + if row[node['index']] < node['value']: + if isinstance(node['left'], dict): + return predict(node['left'], row) + else: + return node['left'] + else: + if isinstance(node['right'], dict): + return predict(node['right'], row) + else: + return node['right'] + +# Classification and Regression Tree Algorithm +def decision_tree(train, test, max_depth, min_size): + tree = build_tree(train, max_depth, min_size) + predictions = list() + for row in test: + prediction = predict(tree, row) + predictions.append(prediction) + return(predictions) + +# Test CART on Bank Note dataset +seed(1) +# load and prepare data +filename = 'DataFiles/ride.csv' +dataset = load_csv(filename) +# convert string attributes to integers +for i in range(len(dataset[0])): + str_column_to_float(dataset, i) +# evaluate algorithm +n_folds = 5 +max_depth = 5 +min_size = 10 +scores = evaluate_algorithm(dataset, decision_tree, n_folds, max_depth, min_size) +print('Scores: %s' % scores) +print('Mean Accuracy: %.3f%%' % (sum(scores)/float(len(scores)))) diff --git a/doc/src/DecisionTrees/decisiontree.py b/doc/src/DecisionTrees/decisiontree.py new file mode 100644 index 000000000..1142d2d84 --- /dev/null +++ b/doc/src/DecisionTrees/decisiontree.py @@ -0,0 +1,187 @@ +import re +import math +from collections import deque + +# x is examples in training set +# y is set of attributes +# label is target attributes +# Node is a class which has properties values, childs, and next +# root is top node in the decision tree + +class Node(object): + def __init__(self): + self.value = None + self.next = None + self.childs = None + +# Simple class of Decision Tree +# Aimed for who want to learn Decision Tree, so it is not optimized +class DecisionTree(object): + def __init__(self, sample, attributes, labels): + self.sample = sample + self.attributes = attributes + self.labels = labels + self.labelCodes = None + self.labelCodesCount = None + self.initLabelCodes() + # print(self.labelCodes) + self.root = None + self.entropy = self.getEntropy([x for x in range(len(self.labels))]) + + def initLabelCodes(self): + self.labelCodes = [] + self.labelCodesCount = [] + for l in self.labels: + if l not in self.labelCodes: + self.labelCodes.append(l) + self.labelCodesCount.append(0) + self.labelCodesCount[self.labelCodes.index(l)] += 1 + + def getLabelCodeId(self, sampleId): + return self.labelCodes.index(self.labels[sampleId]) + + def getAttributeValues(self, sampleIds, attributeId): + vals = [] + for sid in sampleIds: + val = self.sample[sid][attributeId] + if val not in vals: + vals.append(val) + # print(vals) + return vals + + def getEntropy(self, sampleIds): + entropy = 0 + labelCount = [0] * len(self.labelCodes) + for sid in sampleIds: + labelCount[self.getLabelCodeId(sid)] += 1 + # print("-ge", labelCount) + for lv in labelCount: + # print(lv) + if lv != 0: + entropy += -lv/len(sampleIds) * math.log(lv/len(sampleIds), 2) + else: + entropy += 0 + return entropy + + def getDominantLabel(self, sampleIds): + labelCodesCount = [0] * len(self.labelCodes) + for sid in sampleIds: + labelCodesCount[self.labelCodes.index(self.labels[sid])] += 1 + return self.labelCodes[labelCodesCount.index(max(labelCodesCount))] + + def getInformationGain(self, sampleIds, attributeId): + gain = self.getEntropy(sampleIds) + attributeVals = [] + attributeValsCount = [] + attributeValsIds = [] + for sid in sampleIds: + val = self.sample[sid][attributeId] + if val not in attributeVals: + attributeVals.append(val) + attributeValsCount.append(0) + attributeValsIds.append([]) + vid = attributeVals.index(val) + attributeValsCount[vid] += 1 + attributeValsIds[vid].append(sid) + # print("-gig", self.attributes[attributeId]) + for vc, vids in zip(attributeValsCount, attributeValsIds): + # print("-gig", vids) + gain -= vc/len(sampleIds) * self.getEntropy(vids) + return gain + + def getAttributeMaxInformationGain(self, sampleIds, attributeIds): + attributesEntropy = [0] * len(attributeIds) + for i, attId in zip(range(len(attributeIds)), attributeIds): + attributesEntropy[i] = self.getInformationGain(sampleIds, attId) + maxId = attributeIds[attributesEntropy.index(max(attributesEntropy))] + return self.attributes[maxId], maxId + + def isSingleLabeled(self, sampleIds): + label = self.labels[sampleIds[0]] + for sid in sampleIds: + if self.labels[sid] != label: + return False + return True + + def getLabel(self, sampleId): + return self.labels[sampleId] + + def id3(self): + sampleIds = [x for x in range(len(self.sample))] + attributeIds = [x for x in range(len(self.attributes))] + self.root = self.id3Recv(sampleIds, attributeIds, self.root) + + def id3Recv(self, sampleIds, attributeIds, root): + root = Node() # Initialize current root + if self.isSingleLabeled(sampleIds): + root.value = self.labels[sampleIds[0]] + return root + # print(attributeIds) + if len(attributeIds) == 0: + root.value = self.getDominantLabel(sampleIds) + return root + bestAttrName, bestAttrId = self.getAttributeMaxInformationGain( + sampleIds, attributeIds) + # print(bestAttrName) + root.value = bestAttrName + root.childs = [] # Create list of children + for value in self.getAttributeValues(sampleIds, bestAttrId): + # print(value) + child = Node() + child.value = value + root.childs.append(child) # Append new child node to current + # root + childSampleIds = [] + for sid in sampleIds: + if self.sample[sid][bestAttrId] == value: + childSampleIds.append(sid) + if len(childSampleIds) == 0: + child.next = self.getDominantLabel(sampleIds) + else: + # print(bestAttrName, bestAttrId) + # print(attributeIds) + if len(attributeIds) > 0 and bestAttrId in attributeIds: + toRemove = attributeIds.index(bestAttrId) + attributeIds.pop(toRemove) + child.next = self.id3Recv( + childSampleIds, attributeIds, child.next) + return root + + def printTree(self): + if self.root: + roots = deque() + roots.append(self.root) + while len(roots) > 0: + root = roots.popleft() + print(root.value) + if root.childs: + for child in root.childs: + print('({})'.format(child.value)) + roots.append(child.next) + elif root.next: + print(root.next) + + +def test(): + f = open('DataFiles/rideclass.csv') + attributes = f.readline().split(',') + attributes = attributes[1:len(attributes)-1] + print(attributes) + sample = f.readlines() + f.close() + for i in range(len(sample)): + sample[i] = re.sub('\d+,', '', sample[i]) + sample[i] = sample[i].strip().split(',') + labels = [] + for s in sample: + labels.append(s.pop()) + # print(sample) + # print(labels) + decisionTree = DecisionTree(sample, attributes, labels) + print("System entropy {}".format(decisionTree.entropy)) + decisionTree.id3() + decisionTree.printTree() + + +if __name__ == '__main__': + test() diff --git a/doc/src/DecisionTrees/decisiontree.py~ b/doc/src/DecisionTrees/decisiontree.py~ new file mode 100644 index 000000000..8b3420f3e --- /dev/null +++ b/doc/src/DecisionTrees/decisiontree.py~ @@ -0,0 +1,187 @@ +import re +import math +from collections import deque + +# x is examples in training set +# y is set of attributes +# label is target attributes +# Node is a class which has properties values, childs, and next +# root is top node in the decision tree + +class Node(object): + def __init__(self): + self.value = None + self.next = None + self.childs = None + +# Simple class of Decision Tree +# Aimed for who want to learn Decision Tree, so it is not optimized +class DecisionTree(object): + def __init__(self, sample, attributes, labels): + self.sample = sample + self.attributes = attributes + self.labels = labels + self.labelCodes = None + self.labelCodesCount = None + self.initLabelCodes() + # print(self.labelCodes) + self.root = None + self.entropy = self.getEntropy([x for x in range(len(self.labels))]) + + def initLabelCodes(self): + self.labelCodes = [] + self.labelCodesCount = [] + for l in self.labels: + if l not in self.labelCodes: + self.labelCodes.append(l) + self.labelCodesCount.append(0) + self.labelCodesCount[self.labelCodes.index(l)] += 1 + + def getLabelCodeId(self, sampleId): + return self.labelCodes.index(self.labels[sampleId]) + + def getAttributeValues(self, sampleIds, attributeId): + vals = [] + for sid in sampleIds: + val = self.sample[sid][attributeId] + if val not in vals: + vals.append(val) + # print(vals) + return vals + + def getEntropy(self, sampleIds): + entropy = 0 + labelCount = [0] * len(self.labelCodes) + for sid in sampleIds: + labelCount[self.getLabelCodeId(sid)] += 1 + # print("-ge", labelCount) + for lv in labelCount: + # print(lv) + if lv != 0: + entropy += -lv/len(sampleIds) * math.log(lv/len(sampleIds), 2) + else: + entropy += 0 + return entropy + + def getDominantLabel(self, sampleIds): + labelCodesCount = [0] * len(self.labelCodes) + for sid in sampleIds: + labelCodesCount[self.labelCodes.index(self.labels[sid])] += 1 + return self.labelCodes[labelCodesCount.index(max(labelCodesCount))] + + def getInformationGain(self, sampleIds, attributeId): + gain = self.getEntropy(sampleIds) + attributeVals = [] + attributeValsCount = [] + attributeValsIds = [] + for sid in sampleIds: + val = self.sample[sid][attributeId] + if val not in attributeVals: + attributeVals.append(val) + attributeValsCount.append(0) + attributeValsIds.append([]) + vid = attributeVals.index(val) + attributeValsCount[vid] += 1 + attributeValsIds[vid].append(sid) + # print("-gig", self.attributes[attributeId]) + for vc, vids in zip(attributeValsCount, attributeValsIds): + # print("-gig", vids) + gain -= vc/len(sampleIds) * self.getEntropy(vids) + return gain + + def getAttributeMaxInformationGain(self, sampleIds, attributeIds): + attributesEntropy = [0] * len(attributeIds) + for i, attId in zip(range(len(attributeIds)), attributeIds): + attributesEntropy[i] = self.getInformationGain(sampleIds, attId) + maxId = attributeIds[attributesEntropy.index(max(attributesEntropy))] + return self.attributes[maxId], maxId + + def isSingleLabeled(self, sampleIds): + label = self.labels[sampleIds[0]] + for sid in sampleIds: + if self.labels[sid] != label: + return False + return True + + def getLabel(self, sampleId): + return self.labels[sampleId] + + def id3(self): + sampleIds = [x for x in range(len(self.sample))] + attributeIds = [x for x in range(len(self.attributes))] + self.root = self.id3Recv(sampleIds, attributeIds, self.root) + + def id3Recv(self, sampleIds, attributeIds, root): + root = Node() # Initialize current root + if self.isSingleLabeled(sampleIds): + root.value = self.labels[sampleIds[0]] + return root + # print(attributeIds) + if len(attributeIds) == 0: + root.value = self.getDominantLabel(sampleIds) + return root + bestAttrName, bestAttrId = self.getAttributeMaxInformationGain( + sampleIds, attributeIds) + # print(bestAttrName) + root.value = bestAttrName + root.childs = [] # Create list of children + for value in self.getAttributeValues(sampleIds, bestAttrId): + # print(value) + child = Node() + child.value = value + root.childs.append(child) # Append new child node to current + # root + childSampleIds = [] + for sid in sampleIds: + if self.sample[sid][bestAttrId] == value: + childSampleIds.append(sid) + if len(childSampleIds) == 0: + child.next = self.getDominantLabel(sampleIds) + else: + # print(bestAttrName, bestAttrId) + # print(attributeIds) + if len(attributeIds) > 0 and bestAttrId in attributeIds: + toRemove = attributeIds.index(bestAttrId) + attributeIds.pop(toRemove) + child.next = self.id3Recv( + childSampleIds, attributeIds, child.next) + return root + + def printTree(self): + if self.root: + roots = deque() + roots.append(self.root) + while len(roots) > 0: + root = roots.popleft() + print(root.value) + if root.childs: + for child in root.childs: + print('({})'.format(child.value)) + roots.append(child.next) + elif root.next: + print(root.next) + + +def test(): + f = open('rideclass.csv') + attributes = f.readline().split(',') + attributes = attributes[1:len(attributes)-1] + print(attributes) + sample = f.readlines() + f.close() + for i in range(len(sample)): + sample[i] = re.sub('\d+,', '', sample[i]) + sample[i] = sample[i].strip().split(',') + labels = [] + for s in sample: + labels.append(s.pop()) + # print(sample) + # print(labels) + decisionTree = DecisionTree(sample, attributes, labels) + print("System entropy {}".format(decisionTree.entropy)) + decisionTree.id3() + decisionTree.printTree() + + +if __name__ == '__main__': + test() diff --git a/doc/src/DecisionTrees/dtcancer.py b/doc/src/DecisionTrees/dtcancer.py new file mode 100644 index 000000000..45d9e5fe1 --- /dev/null +++ b/doc/src/DecisionTrees/dtcancer.py @@ -0,0 +1,30 @@ +from sklearn.datasets import load_breast_cancer +from sklearn.tree import DecisionTreeClassifier +from sklearn.model_selection import train_test_split +from sklearn.metrics import confusion_matrix +from sklearn.tree import export_graphviz + +from IPython.display import Image +from pydot import graph_from_dot_data +import pandas as pd +import numpy as np + +cancer = load_breast_cancer() +X = pd.DataFrame(cancer.data, columns=cancer.feature_names) +print(X) +y = pd.Categorical.from_codes(cancer.target, cancer.target_names) +y = pd.get_dummies(y) +print(y) +X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1) +tree_clf = DecisionTreeClassifier(max_depth=5) +tree_clf.fit(X_train, y_train) + +export_graphviz( + tree_clf, + out_file="cancer.dot", + feature_names=cancer.feature_names, + class_names=cancer.target_names, + rounded=True, + filled=True +) + diff --git a/doc/src/DecisionTrees/dtcancer.py~ b/doc/src/DecisionTrees/dtcancer.py~ new file mode 100644 index 000000000..0008c1d32 --- /dev/null +++ b/doc/src/DecisionTrees/dtcancer.py~ @@ -0,0 +1,29 @@ +from sklearn.datasets import load_breast_cancer +from sklearn.tree import DecisionTreeClassifier +from sklearn.model_selection import train_test_split +from sklearn.metrics import confusion_matrix +from sklearn.tree import export_graphviz + +from IPython.display import Image +from pydot import graph_from_dot_data +import pandas as pd +import numpy as np + +cancer = load_breast_cancer() +X = pd.DataFrame(cancer.data, columns=cancer.feature_names) +y = pd.Categorical.from_codes(cancer.target, cancer.target_names) +y = pd.get_dummies(y) + +X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1) +tree_clf = DecisionTreeClassifier(max_depth=5) +tree_clf.fit(X_train, y_train) + +export_graphviz( + tree_clf, + out_file="cancer.dot", + feature_names=cancer.feature_names, + class_names=cancer.target_names, + rounded=True, + filled=True +) + diff --git a/doc/src/DecisionTrees/gini.py b/doc/src/DecisionTrees/gini.py new file mode 100644 index 000000000..a393242d7 --- /dev/null +++ b/doc/src/DecisionTrees/gini.py @@ -0,0 +1,55 @@ +# Split a dataset based on an attribute and an attribute value +def test_split(index, value, dataset): + left, right = list(), list() + for row in dataset: + if row[index] < value: + left.append(row) + else: + right.append(row) + return left, right + +# Calculate the Gini index for a split dataset +def gini_index(groups, classes): + # count all samples at split point + n_instances = float(sum([len(group) for group in groups])) + # sum weighted Gini index for each group + gini = 0.0 + for group in groups: + size = float(len(group)) + # avoid divide by zero + if size == 0: + continue + score = 0.0 + # score the group based on the score for each class + for class_val in classes: + p = [row[-1] for row in group].count(class_val) / size + score += p * p + # weight the group score by its relative size + gini += (1.0 - score) * (size / n_instances) + return gini + +# Select the best split point for a dataset +def get_split(dataset): + class_values = list(set(row[-1] for row in dataset)) + b_index, b_value, b_score, b_groups = 999, 999, 999, None + for index in range(len(dataset[0])-1): + for row in dataset: + groups = test_split(index, row[index], dataset) + gini = gini_index(groups, class_values) + print('X%d < %.3f Gini=%.3f' % ((index+1), row[index], gini)) + if gini < b_score: + b_index, b_value, b_score, b_groups = index, row[index], gini, groups + return {'index':b_index, 'value':b_value, 'groups':b_groups} + +dataset = [[2.771244718,1.784783929,0], + [1.728571309,1.169761413,0], + [3.678319846,2.81281357,0], + [3.961043357,2.61995032,0], + [2.999208922,2.209014212,0], + [7.497545867,3.162953546,1], + [9.00220326,3.339047188,1], + [7.444542326,0.476683375,1], + [10.12493903,3.234550982,1], + [6.642287351,3.319983761,1]] +split = get_split(dataset) +print('Split: [X%d < %.3f]' % ((split['index']+1), split['value'])) diff --git a/doc/src/DecisionTrees/moons.py b/doc/src/DecisionTrees/moons.py new file mode 100644 index 000000000..bde45069e --- /dev/null +++ b/doc/src/DecisionTrees/moons.py @@ -0,0 +1,28 @@ +# Common imports +import numpy as np +from sklearn.model_selection import train_test_split +from sklearn.tree import DecisionTreeClassifier +from sklearn.datasets import make_moons +from sklearn.tree import export_graphviz +from pydot import graph_from_dot_data +import pandas as pd + + +np.random.seed(42) +X, y = make_moons(n_samples=100, noise=0.25, random_state=53) +X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=0) +tree_clf = DecisionTreeClassifier(max_depth=5) +tree_clf.fit(X_train, y_train) + +export_graphviz( + tree_clf, + out_file="moons.dot", +# feature_names=tree_clf.feature_names, +# class_names=tree_clf.target_names, + rounded=True, + filled=True +) + + + + diff --git a/doc/src/DecisionTrees/read.py b/doc/src/DecisionTrees/read.py new file mode 100644 index 000000000..5d2fc64f5 --- /dev/null +++ b/doc/src/DecisionTrees/read.py @@ -0,0 +1,78 @@ +# Common imports +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from sklearn.tree import DecisionTreeClassifier +from sklearn.model_selection import train_test_split +from sklearn.tree import export_graphviz +from sklearn.preprocessing import StandardScaler, OneHotEncoder +from sklearn.compose import ColumnTransformer +from IPython.display import Image +from pydot import graph_from_dot_data +import os + +# Where to save the figures and data files +PROJECT_ROOT_DIR = "Results" +FIGURE_ID = "Results/FigureFiles" +DATA_ID = "DataFiles/" + +if not os.path.exists(PROJECT_ROOT_DIR): + os.mkdir(PROJECT_ROOT_DIR) + +if not os.path.exists(FIGURE_ID): + os.makedirs(FIGURE_ID) + +if not os.path.exists(DATA_ID): + os.makedirs(DATA_ID) + +def image_path(fig_id): + return os.path.join(FIGURE_ID, fig_id) + +def data_path(dat_id): + return os.path.join(DATA_ID, dat_id) + +def save_fig(fig_id): + plt.savefig(image_path(fig_id) + ".png", format='png') + +infile = open(data_path("ride.csv"),'r') + +# Read the experimental data with Pandas +from IPython.display import display +ridedata = pd.read_csv(infile,names = ('Outlook','Temperature','Humidity','Wind','Ride')) +ridedata = pd.DataFrame(ridedata) +display(ridedata) +# Features and targets +X = ridedata.loc[:, ridedata.columns != 'Ride'].values +display(X) +y = ridedata.loc[:, ridedata.columns == 'Ride'].values +display(y) +# Categorical variables to one-hot's +onehotencoder = OneHotEncoder(categories="auto") + +X = ColumnTransformer([("", onehotencoder)]).fit_transform(X) +y.shape + +display(X) +display(y) + + +""" +X = pd.DataFrame(ridedata.data, columns=ridedata.feature_names) +y = pd.Categorical.from_codes(ridedata.target, ridedata.target_names) +y = pd.get_dummies(y) + + +tree_clf = DecisionTreeClassifier(max_depth=2) +tree_clf.fit(X, y) + + +export_graphviz( + tree_clf, + out_file="ride.dot", + feature_names=tree_clf.feature_names, + class_names=tree_clf.target_names, + rounded=True, + filled=True +) +""" + diff --git a/doc/src/DecisionTrees/read.py~ b/doc/src/DecisionTrees/read.py~ new file mode 100644 index 000000000..cc7dadd8b --- /dev/null +++ b/doc/src/DecisionTrees/read.py~ @@ -0,0 +1,80 @@ +# Common imports +import numpy as np +import pandas as pd +import matplotlib.pyplot as plt +from sklearn.tree import DecisionTreeClassifier +from sklearn.model_selection import train_test_split +from sklearn.tree import export_graphviz +from sklearn.preprocessing import StandardScaler, OneHotEncoder +from sklearn.compose import ColumnTransformer +from IPython.display import Image +from pydot import graph_from_dot_data +import os + +# Where to save the figures and data files +PROJECT_ROOT_DIR = "Results" +FIGURE_ID = "Results/FigureFiles" +DATA_ID = "DataFiles/" + +if not os.path.exists(PROJECT_ROOT_DIR): + os.mkdir(PROJECT_ROOT_DIR) + +if not os.path.exists(FIGURE_ID): + os.makedirs(FIGURE_ID) + +if not os.path.exists(DATA_ID): + os.makedirs(DATA_ID) + +def image_path(fig_id): + return os.path.join(FIGURE_ID, fig_id) + +def data_path(dat_id): + return os.path.join(DATA_ID, dat_id) + +def save_fig(fig_id): + plt.savefig(image_path(fig_id) + ".png", format='png') + +infile = open(data_path("ride.csv"),'r') + +# Read the experimental data with Pandas +from IPython.display import display +ridedata = pd.read_csv(infile,names = ('Outlook','Temperature','Humidity','Wind','Ride')) +ridedata = pd.DataFrame(ridedata) +display(ridedata) +# Features and targets +X = ridedata.loc[:, ridedata.columns != 'Ride'].values +display(X) +y = ridedata.loc[:, ridedata.columns == 'Ride'].values +display(y) +# Categorical variables to one-hot's +onehotencoder = OneHotEncoder(categories="auto") + +X = ColumnTransformer( + [("", onehotencoder)], + remainder="passthrough").fit_transform(X) +y.shape + +display(X) +display(y) + + +""" +X = pd.DataFrame(ridedata.data, columns=ridedata.feature_names) +y = pd.Categorical.from_codes(ridedata.target, ridedata.target_names) +y = pd.get_dummies(y) + + +tree_clf = DecisionTreeClassifier(max_depth=2) +tree_clf.fit(X, y) + + +export_graphviz( + tree_clf, + out_file="ride.dot", + feature_names=tree_clf.feature_names, + class_names=tree_clf.target_names, + rounded=True, + filled=True +) +""" +