1
0
mirror of https://github.com/sjwhitworth/golearn.git synced 2025-04-26 13:49:14 +08:00

Merge pull request #5 from sjwhitworth/master

Bringing master up to date
This commit is contained in:
Ayush Goel 2020-08-14 10:51:55 +05:30 committed by GitHub
commit 6aa37aca00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 3495 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,889 @@
3,1,2,0
1,0,0,1
3,0,2,1
1,0,2,1
3,1,2,0
3,1,1,0
1,1,2,0
3,1,2,0
3,0,2,1
2,0,0,1
3,0,2,1
1,0,2,1
3,1,2,0
3,1,2,0
3,0,2,0
2,0,2,1
3,1,1,0
2,1,2,1
3,0,2,0
3,0,0,1
2,1,2,0
2,1,2,1
3,0,1,1
1,1,2,1
3,0,2,0
3,0,2,1
3,1,0,0
1,1,2,0
3,0,1,1
3,1,2,0
1,1,0,0
1,0,0,1
3,0,1,1
2,1,2,0
1,1,0,0
1,1,2,0
3,1,0,1
3,1,2,0
3,0,2,0
3,0,0,1
3,0,2,0
2,0,2,0
3,1,0,0
2,0,0,1
3,0,1,1
3,1,2,0
3,1,1,0
3,0,1,1
3,1,0,0
3,0,2,0
3,1,2,0
3,1,2,0
1,0,0,1
2,0,2,1
1,1,0,0
1,1,2,1
2,0,2,1
3,1,0,0
2,0,2,1
3,1,2,0
3,1,0,0
1,1,2,0
3,1,2,0
1,1,0,0
3,1,0,1
2,0,2,1
3,1,2,0
3,0,2,1
3,1,2,0
2,1,2,0
3,0,2,0
2,1,2,0
3,1,0,0
3,1,2,1
3,1,2,0
3,1,2,0
3,1,2,0
2,1,2,1
3,0,2,1
3,1,2,0
3,1,2,1
3,0,1,1
1,1,2,0
2,0,2,1
3,0,2,1
3,1,2,0
3,1,2,0
1,0,2,1
3,1,2,0
3,1,2,0
3,1,2,0
1,1,2,0
3,1,2,0
3,1,2,0
3,1,2,0
1,1,0,0
1,1,0,1
2,0,2,1
2,1,2,0
3,0,2,0
3,1,2,0
1,1,2,0
3,1,2,0
3,1,2,0
3,1,2,0
3,0,2,1
3,1,2,1
3,1,2,0
3,0,1,1
1,1,2,0
3,0,0,0
3,1,2,0
3,0,2,0
3,0,0,0
3,1,2,0
3,1,1,0
2,1,2,0
1,1,0,0
3,0,2,0
2,1,2,0
3,1,2,0
2,1,0,0
2,0,2,1
1,1,2,0
3,1,0,1
3,1,1,0
3,1,2,1
3,0,0,1
3,1,2,0
3,1,0,0
3,1,2,0
3,0,2,0
2,0,2,1
2,1,2,0
2,1,0,0
1,0,2,1
1,1,2,0
3,1,2,0
1,1,0,0
3,0,0,0
3,0,2,1
3,0,2,1
3,1,1,0
2,1,2,0
2,1,2,0
3,1,2,1
3,0,2,0
2,1,2,0
2,1,2,0
2,1,2,0
1,0,2,1
3,1,2,0
3,1,2,0
3,1,2,0
1,1,0,0
3,0,1,1
3,1,2,0
3,1,2,0
3,1,2,0
3,1,2,0
2,0,2,1
3,1,2,0
3,1,2,0
3,1,2,0
3,1,2,1
1,0,2,1
3,0,2,0
1,1,2,0
3,1,2,0
1,1,2,0
3,1,1,0
3,0,2,1
3,1,2,0
1,1,0,0
3,1,2,0
3,1,2,0
1,0,0,0
2,1,2,0
3,1,2,0
3,0,2,0
2,1,0,0
3,1,2,0
2,1,2,1
3,0,2,1
1,1,2,0
3,0,1,1
1,1,2,1
3,1,1,0
3,1,2,0
2,0,2,1
2,1,2,0
3,0,2,1
2,1,2,1
1,0,0,1
1,0,0,1
3,1,1,0
3,1,2,0
3,0,1,1
2,0,2,0
3,1,2,0
3,1,2,0
3,1,2,0
3,1,0,0
3,1,2,1
3,0,2,0
3,1,2,0
3,1,0,1
3,0,1,1
1,1,0,1
3,1,2,0
2,0,2,1
3,1,2,0
2,1,2,0
3,1,1,0
1,0,0,1
3,0,2,1
2,1,2,0
1,0,0,1
2,1,2,0
3,1,2,1
2,1,2,0
3,1,2,0
3,1,2,0
1,1,2,1
3,1,2,0
2,1,2,1
3,1,2,0
2,1,2,0
3,0,2,0
1,0,2,1
3,1,2,0
2,1,2,0
3,0,2,1
2,1,2,0
3,0,2,0
2,1,2,0
2,0,2,1
2,1,2,0
2,1,2,0
3,0,0,0
3,0,1,1
2,1,2,0
3,1,2,0
3,1,0,0
1,1,1,0
3,0,2,0
2,0,2,1
1,1,2,1
2,1,2,0
3,1,2,0
3,0,2,0
1,1,2,0
3,1,2,0
3,0,2,0
3,0,0,1
1,0,0,1
1,0,2,1
1,0,0,1
2,0,2,1
3,1,1,0
3,1,2,1
1,1,2,0
1,1,2,0
3,0,1,0
2,1,2,0
3,1,2,0
3,1,2,1
1,0,2,1
1,0,2,1
1,1,2,0
3,1,2,1
2,0,2,1
1,1,0,0
3,0,1,1
1,0,2,1
3,0,2,0
2,1,2,0
3,1,1,0
3,0,2,1
3,1,1,0
3,1,2,0
3,1,2,0
3,1,2,1
1,1,2,0
3,1,0,0
3,1,2,1
3,1,2,0
2,1,2,1
3,0,1,1
1,0,2,1
1,0,0,1
2,1,0,0
3,0,2,0
3,1,2,0
1,1,0,0
3,1,0,0
1,0,2,0
1,1,2,1
1,0,0,1
3,0,1,1
3,1,1,1
3,1,2,0
2,0,1,1
3,1,2,0
1,1,2,1
1,0,0,1
1,0,0,1
2,1,0,0
1,0,0,1
1,0,0,1
1,0,0,1
2,0,2,0
3,1,2,0
2,1,2,0
3,0,2,1
2,0,2,1
2,1,2,0
1,0,2,1
1,0,0,1
3,1,2,0
3,1,2,0
2,0,1,1
2,0,2,1
3,1,2,0
1,0,0,1
3,1,2,0
2,0,2,1
3,0,2,1
1,0,0,1
3,0,1,1
1,1,2,0
1,1,2,0
3,1,2,0
1,0,2,1
3,1,2,0
1,1,2,0
1,0,0,1
3,1,2,1
1,1,2,0
2,1,2,1
1,0,2,1
2,1,2,0
2,1,2,0
2,1,2,0
2,0,2,1
2,0,2,1
3,0,2,1
3,1,2,1
3,1,2,0
3,1,2,0
1,1,2,0
3,1,0,0
3,1,2,0
3,1,0,0
3,1,2,0
1,0,2,1
2,0,2,0
3,0,1,1
3,0,1,1
3,1,2,0
2,1,0,0
3,0,0,0
3,1,2,0
3,1,1,0
3,1,2,0
1,0,0,1
3,0,0,1
3,0,1,1
1,0,0,1
1,1,0,1
3,1,2,0
3,1,2,0
1,1,0,0
3,0,2,0
1,0,0,1
3,0,2,1
1,1,0,0
3,1,0,0
3,1,2,0
1,0,0,1
3,0,0,1
3,1,2,0
1,0,2,1
3,1,2,0
2,1,2,0
3,1,2,0
2,0,2,1
3,1,1,0
2,0,0,1
1,1,2,1
3,1,2,1
3,1,2,0
1,0,0,1
3,0,2,1
3,1,2,0
3,0,2,0
2,1,2,0
2,1,2,0
2,0,2,1
3,1,2,1
3,1,2,0
3,0,2,0
3,1,2,0
3,0,2,0
2,1,2,0
3,1,2,0
2,1,2,1
3,1,2,0
3,0,2,0
3,1,2,0
3,1,1,0
1,0,1,1
2,1,2,0
3,1,2,1
3,0,2,0
2,0,2,1
2,0,2,1
2,1,2,0
3,0,2,0
3,1,0,0
3,1,1,0
3,1,2,0
3,0,2,0
3,1,2,0
3,1,2,0
2,0,2,1
2,0,2,1
3,1,1,0
3,1,2,1
1,1,2,1
3,0,2,1
2,0,2,1
3,1,2,0
1,1,2,0
1,0,2,1
3,0,2,0
2,0,2,1
1,1,2,0
2,1,2,0
2,0,2,1
3,1,2,0
3,1,2,0
2,0,2,1
3,1,2,1
1,1,2,1
2,0,2,1
1,1,2,1
3,0,0,1
1,1,2,1
2,1,2,0
3,1,2,0
1,1,0,0
1,1,0,1
3,1,2,0
3,1,0,1
1,1,2,0
1,0,2,1
2,0,2,1
3,1,1,0
1,1,2,1
3,1,2,0
1,1,2,0
2,1,2,0
3,1,2,0
3,1,2,0
2,1,2,0
1,1,2,0
3,1,1,0
3,0,0,1
3,1,2,0
3,1,2,0
2,0,2,1
2,0,0,1
3,0,2,0
1,1,2,0
2,1,2,0
3,1,2,0
3,1,2,0
3,0,2,1
3,1,2,0
2,1,2,0
3,1,2,0
3,0,2,1
1,1,0,1
3,0,2,0
1,0,2,1
1,1,0,0
3,1,2,0
3,1,2,1
3,1,2,0
3,1,2,0
1,1,2,0
1,1,0,0
3,1,2,0
3,1,0,0
1,0,0,1
3,1,2,0
1,0,2,0
3,1,2,0
3,1,2,0
3,0,1,0
3,0,1,0
3,0,2,0
1,0,2,1
1,1,0,0
2,0,2,1
1,1,2,1
3,1,2,0
3,1,2,1
3,1,1,1
3,1,2,0
1,1,2,1
1,0,0,1
3,1,2,0
1,1,2,0
2,0,2,1
3,1,1,0
2,0,2,1
3,1,2,0
1,0,2,1
3,1,2,0
3,1,0,0
1,0,0,1
3,1,0,0
3,1,1,0
2,0,2,1
1,1,2,0
3,1,2,0
2,1,2,0
2,0,2,1
3,1,0,0
3,1,0,0
3,0,0,1
3,0,2,0
2,0,2,1
1,1,2,0
1,0,0,1
3,1,2,0
1,0,0,1
1,0,2,1
3,0,2,0
3,0,2,0
2,1,2,1
1,1,0,0
1,1,2,0
2,0,2,1
2,1,0,1
3,1,2,0
2,1,2,1
1,1,0,1
2,1,2,0
3,1,1,0
3,1,0,1
3,0,2,1
1,1,2,0
1,0,0,1
1,1,0,0
1,0,2,1
3,0,2,1
3,1,1,0
3,1,2,0
2,1,2,0
3,1,2,0
3,0,2,0
3,1,2,0
3,1,2,0
3,0,2,0
3,1,0,0
3,1,2,1
2,1,2,1
1,0,2,1
1,1,2,1
3,0,1,1
3,1,2,0
3,1,2,0
2,0,2,1
1,0,2,1
3,0,0,0
3,1,2,1
2,0,2,1
1,0,0,1
2,1,2,0
1,1,0,0
3,1,0,0
1,0,2,1
2,1,2,0
1,1,0,1
3,1,2,0
3,1,2,0
3,1,2,0
1,0,0,1
3,1,2,0
3,0,1,0
2,1,2,0
3,1,2,0
2,0,2,1
3,1,2,0
3,1,0,0
1,1,0,1
2,0,2,1
3,1,2,0
1,1,2,0
3,1,2,0
1,1,0,1
3,1,2,0
3,1,2,0
1,1,2,1
2,0,0,1
1,0,2,1
3,0,2,0
3,1,2,0
3,0,1,1
3,1,1,0
3,1,2,0
2,0,2,1
3,1,2,0
3,0,2,0
2,0,2,1
2,1,2,0
3,1,0,0
1,1,2,1
3,1,0,1
3,1,2,0
3,1,2,0
1,1,2,0
2,1,1,0
1,0,2,1
3,1,2,0
3,1,1,0
1,1,2,1
3,1,2,0
1,1,0,1
1,1,2,0
3,0,2,0
2,0,2,1
3,1,2,0
2,1,2,0
3,0,2,0
3,1,2,0
3,1,2,0
1,0,0,1
3,0,2,0
3,1,2,1
3,0,0,1
1,1,0,1
3,1,2,0
1,1,0,1
3,1,2,0
3,0,2,1
3,1,2,0
2,0,2,1
3,1,2,0
3,0,1,1
3,0,1,0
2,1,2,0
3,1,2,0
3,0,1,0
2,1,2,0
1,1,0,0
1,1,2,1
3,1,0,0
1,1,2,0
3,1,2,0
3,1,2,1
2,1,2,0
2,1,2,0
3,1,2,0
3,1,2,0
1,0,2,1
2,0,2,1
1,1,2,0
2,1,2,0
2,1,2,1
2,1,2,0
3,1,2,0
3,1,2,0
3,0,2,1
3,0,2,0
1,1,0,1
3,0,1,0
1,1,0,1
3,1,2,0
3,1,2,0
2,1,2,0
2,1,0,0
3,1,2,0
3,1,2,0
3,1,2,0
1,0,2,1
1,1,2,1
3,0,0,1
3,1,2,1
3,1,0,0
1,1,2,0
2,1,2,0
3,1,2,0
3,0,1,1
1,1,0,0
3,1,2,0
1,0,0,1
1,1,2,1
3,0,0,0
3,1,1,0
3,1,2,0
2,1,2,0
2,0,2,1
1,1,2,1
1,0,2,1
3,1,0,1
1,0,0,1
1,1,2,0
1,1,2,1
3,1,2,0
2,1,2,0
3,1,2,0
1,0,0,1
2,0,2,1
3,1,1,0
3,1,2,0
2,0,2,1
3,1,2,0
2,1,2,0
2,1,2,0
1,1,2,1
3,1,2,0
2,0,2,1
3,0,1,1
2,1,2,0
3,0,2,0
1,0,2,1
3,1,0,0
2,1,2,0
2,1,2,0
2,1,2,0
3,1,2,0
3,0,2,0
1,1,0,1
3,1,2,0
3,1,2,0
1,1,2,1
1,1,2,0
1,0,0,1
3,1,2,0
3,1,2,1
1,1,2,0
3,1,2,0
2,0,2,1
1,1,2,0
3,1,1,0
2,0,2,1
3,1,2,1
3,1,2,0
3,1,2,0
2,0,2,1
2,1,2,1
3,1,2,0
2,1,2,0
3,1,2,0
1,0,2,1
3,1,2,0
3,1,2,0
3,1,0,1
1,0,2,1
3,1,2,0
1,0,2,1
1,1,0,0
3,0,1,0
3,1,1,0
3,1,2,0
3,1,2,0
3,1,2,0
2,0,2,0
3,1,0,0
2,0,2,1
3,1,2,0
3,1,1,0
3,0,2,1
3,1,1,0
1,0,2,1
3,0,0,1
1,0,2,1
1,1,2,0
3,1,2,0
3,1,2,0
3,1,2,0
3,0,2,1
3,1,1,0
3,1,2,1
1,1,0,0
3,1,1,0
2,1,2,0
3,0,2,0
1,1,0,0
3,1,2,0
2,1,2,0
1,0,2,1
3,0,2,1
3,1,0,0
3,0,2,0
2,1,2,0
2,0,2,1
1,1,2,1
3,1,0,1
3,1,2,1
3,1,2,0
1,1,2,0
3,0,2,0
2,1,2,0
1,0,2,1
3,1,2,0
3,1,2,0
2,1,2,0
3,0,2,0
3,1,2,0
1,1,2,0
3,0,2,0
2,1,0,0
3,1,2,0
3,1,2,0
1,0,2,1
3,1,2,1
1,1,2,0
3,0,2,1
3,1,2,0
3,1,1,0
3,1,2,0
2,1,0,1
3,1,1,1
3,0,0,1
2,1,2,1
3,1,0,0
3,1,2,0
3,1,2,0
1,0,0,1
3,1,2,0
3,1,2,0
3,1,2,1
1,1,0,1
3,1,2,0
2,1,2,0
1,0,0,1
3,1,0,0
3,1,2,0
3,1,2,0
3,1,2,0
3,1,0,0
2,1,2,0
1,0,0,1
3,1,2,0
3,1,2,0
3,0,0,0
1,0,2,1
2,0,2,0
3,0,2,1
1,0,2,1
1,1,2,1
3,0,0,1
3,1,0,0
3,1,2,0
2,1,2,0
1,0,2,1
3,0,2,0
2,1,2,0
2,0,2,1
2,0,0,1
1,1,2,0
3,1,2,0
3,1,2,1
3,1,2,0
1,0,2,1
1,1,2,0
3,1,2,0
2,0,0,1
3,0,0,1
3,1,2,0
3,1,2,0
3,1,2,0
1,0,0,1
2,0,2,1
3,1,2,0
3,0,2,0
2,1,2,0
3,1,2,0
3,0,1,0
2,1,2,0
1,0,2,1
3,0,2,0
1,1,0,1
3,1,1,0
1 3 1 2 0
2 1 0 0 1
3 3 0 2 1
4 1 0 2 1
5 3 1 2 0
6 3 1 1 0
7 1 1 2 0
8 3 1 2 0
9 3 0 2 1
10 2 0 0 1
11 3 0 2 1
12 1 0 2 1
13 3 1 2 0
14 3 1 2 0
15 3 0 2 0
16 2 0 2 1
17 3 1 1 0
18 2 1 2 1
19 3 0 2 0
20 3 0 0 1
21 2 1 2 0
22 2 1 2 1
23 3 0 1 1
24 1 1 2 1
25 3 0 2 0
26 3 0 2 1
27 3 1 0 0
28 1 1 2 0
29 3 0 1 1
30 3 1 2 0
31 1 1 0 0
32 1 0 0 1
33 3 0 1 1
34 2 1 2 0
35 1 1 0 0
36 1 1 2 0
37 3 1 0 1
38 3 1 2 0
39 3 0 2 0
40 3 0 0 1
41 3 0 2 0
42 2 0 2 0
43 3 1 0 0
44 2 0 0 1
45 3 0 1 1
46 3 1 2 0
47 3 1 1 0
48 3 0 1 1
49 3 1 0 0
50 3 0 2 0
51 3 1 2 0
52 3 1 2 0
53 1 0 0 1
54 2 0 2 1
55 1 1 0 0
56 1 1 2 1
57 2 0 2 1
58 3 1 0 0
59 2 0 2 1
60 3 1 2 0
61 3 1 0 0
62 1 1 2 0
63 3 1 2 0
64 1 1 0 0
65 3 1 0 1
66 2 0 2 1
67 3 1 2 0
68 3 0 2 1
69 3 1 2 0
70 2 1 2 0
71 3 0 2 0
72 2 1 2 0
73 3 1 0 0
74 3 1 2 1
75 3 1 2 0
76 3 1 2 0
77 3 1 2 0
78 2 1 2 1
79 3 0 2 1
80 3 1 2 0
81 3 1 2 1
82 3 0 1 1
83 1 1 2 0
84 2 0 2 1
85 3 0 2 1
86 3 1 2 0
87 3 1 2 0
88 1 0 2 1
89 3 1 2 0
90 3 1 2 0
91 3 1 2 0
92 1 1 2 0
93 3 1 2 0
94 3 1 2 0
95 3 1 2 0
96 1 1 0 0
97 1 1 0 1
98 2 0 2 1
99 2 1 2 0
100 3 0 2 0
101 3 1 2 0
102 1 1 2 0
103 3 1 2 0
104 3 1 2 0
105 3 1 2 0
106 3 0 2 1
107 3 1 2 1
108 3 1 2 0
109 3 0 1 1
110 1 1 2 0
111 3 0 0 0
112 3 1 2 0
113 3 0 2 0
114 3 0 0 0
115 3 1 2 0
116 3 1 1 0
117 2 1 2 0
118 1 1 0 0
119 3 0 2 0
120 2 1 2 0
121 3 1 2 0
122 2 1 0 0
123 2 0 2 1
124 1 1 2 0
125 3 1 0 1
126 3 1 1 0
127 3 1 2 1
128 3 0 0 1
129 3 1 2 0
130 3 1 0 0
131 3 1 2 0
132 3 0 2 0
133 2 0 2 1
134 2 1 2 0
135 2 1 0 0
136 1 0 2 1
137 1 1 2 0
138 3 1 2 0
139 1 1 0 0
140 3 0 0 0
141 3 0 2 1
142 3 0 2 1
143 3 1 1 0
144 2 1 2 0
145 2 1 2 0
146 3 1 2 1
147 3 0 2 0
148 2 1 2 0
149 2 1 2 0
150 2 1 2 0
151 1 0 2 1
152 3 1 2 0
153 3 1 2 0
154 3 1 2 0
155 1 1 0 0
156 3 0 1 1
157 3 1 2 0
158 3 1 2 0
159 3 1 2 0
160 3 1 2 0
161 2 0 2 1
162 3 1 2 0
163 3 1 2 0
164 3 1 2 0
165 3 1 2 1
166 1 0 2 1
167 3 0 2 0
168 1 1 2 0
169 3 1 2 0
170 1 1 2 0
171 3 1 1 0
172 3 0 2 1
173 3 1 2 0
174 1 1 0 0
175 3 1 2 0
176 3 1 2 0
177 1 0 0 0
178 2 1 2 0
179 3 1 2 0
180 3 0 2 0
181 2 1 0 0
182 3 1 2 0
183 2 1 2 1
184 3 0 2 1
185 1 1 2 0
186 3 0 1 1
187 1 1 2 1
188 3 1 1 0
189 3 1 2 0
190 2 0 2 1
191 2 1 2 0
192 3 0 2 1
193 2 1 2 1
194 1 0 0 1
195 1 0 0 1
196 3 1 1 0
197 3 1 2 0
198 3 0 1 1
199 2 0 2 0
200 3 1 2 0
201 3 1 2 0
202 3 1 2 0
203 3 1 0 0
204 3 1 2 1
205 3 0 2 0
206 3 1 2 0
207 3 1 0 1
208 3 0 1 1
209 1 1 0 1
210 3 1 2 0
211 2 0 2 1
212 3 1 2 0
213 2 1 2 0
214 3 1 1 0
215 1 0 0 1
216 3 0 2 1
217 2 1 2 0
218 1 0 0 1
219 2 1 2 0
220 3 1 2 1
221 2 1 2 0
222 3 1 2 0
223 3 1 2 0
224 1 1 2 1
225 3 1 2 0
226 2 1 2 1
227 3 1 2 0
228 2 1 2 0
229 3 0 2 0
230 1 0 2 1
231 3 1 2 0
232 2 1 2 0
233 3 0 2 1
234 2 1 2 0
235 3 0 2 0
236 2 1 2 0
237 2 0 2 1
238 2 1 2 0
239 2 1 2 0
240 3 0 0 0
241 3 0 1 1
242 2 1 2 0
243 3 1 2 0
244 3 1 0 0
245 1 1 1 0
246 3 0 2 0
247 2 0 2 1
248 1 1 2 1
249 2 1 2 0
250 3 1 2 0
251 3 0 2 0
252 1 1 2 0
253 3 1 2 0
254 3 0 2 0
255 3 0 0 1
256 1 0 0 1
257 1 0 2 1
258 1 0 0 1
259 2 0 2 1
260 3 1 1 0
261 3 1 2 1
262 1 1 2 0
263 1 1 2 0
264 3 0 1 0
265 2 1 2 0
266 3 1 2 0
267 3 1 2 1
268 1 0 2 1
269 1 0 2 1
270 1 1 2 0
271 3 1 2 1
272 2 0 2 1
273 1 1 0 0
274 3 0 1 1
275 1 0 2 1
276 3 0 2 0
277 2 1 2 0
278 3 1 1 0
279 3 0 2 1
280 3 1 1 0
281 3 1 2 0
282 3 1 2 0
283 3 1 2 1
284 1 1 2 0
285 3 1 0 0
286 3 1 2 1
287 3 1 2 0
288 2 1 2 1
289 3 0 1 1
290 1 0 2 1
291 1 0 0 1
292 2 1 0 0
293 3 0 2 0
294 3 1 2 0
295 1 1 0 0
296 3 1 0 0
297 1 0 2 0
298 1 1 2 1
299 1 0 0 1
300 3 0 1 1
301 3 1 1 1
302 3 1 2 0
303 2 0 1 1
304 3 1 2 0
305 1 1 2 1
306 1 0 0 1
307 1 0 0 1
308 2 1 0 0
309 1 0 0 1
310 1 0 0 1
311 1 0 0 1
312 2 0 2 0
313 3 1 2 0
314 2 1 2 0
315 3 0 2 1
316 2 0 2 1
317 2 1 2 0
318 1 0 2 1
319 1 0 0 1
320 3 1 2 0
321 3 1 2 0
322 2 0 1 1
323 2 0 2 1
324 3 1 2 0
325 1 0 0 1
326 3 1 2 0
327 2 0 2 1
328 3 0 2 1
329 1 0 0 1
330 3 0 1 1
331 1 1 2 0
332 1 1 2 0
333 3 1 2 0
334 1 0 2 1
335 3 1 2 0
336 1 1 2 0
337 1 0 0 1
338 3 1 2 1
339 1 1 2 0
340 2 1 2 1
341 1 0 2 1
342 2 1 2 0
343 2 1 2 0
344 2 1 2 0
345 2 0 2 1
346 2 0 2 1
347 3 0 2 1
348 3 1 2 1
349 3 1 2 0
350 3 1 2 0
351 1 1 2 0
352 3 1 0 0
353 3 1 2 0
354 3 1 0 0
355 3 1 2 0
356 1 0 2 1
357 2 0 2 0
358 3 0 1 1
359 3 0 1 1
360 3 1 2 0
361 2 1 0 0
362 3 0 0 0
363 3 1 2 0
364 3 1 1 0
365 3 1 2 0
366 1 0 0 1
367 3 0 0 1
368 3 0 1 1
369 1 0 0 1
370 1 1 0 1
371 3 1 2 0
372 3 1 2 0
373 1 1 0 0
374 3 0 2 0
375 1 0 0 1
376 3 0 2 1
377 1 1 0 0
378 3 1 0 0
379 3 1 2 0
380 1 0 0 1
381 3 0 0 1
382 3 1 2 0
383 1 0 2 1
384 3 1 2 0
385 2 1 2 0
386 3 1 2 0
387 2 0 2 1
388 3 1 1 0
389 2 0 0 1
390 1 1 2 1
391 3 1 2 1
392 3 1 2 0
393 1 0 0 1
394 3 0 2 1
395 3 1 2 0
396 3 0 2 0
397 2 1 2 0
398 2 1 2 0
399 2 0 2 1
400 3 1 2 1
401 3 1 2 0
402 3 0 2 0
403 3 1 2 0
404 3 0 2 0
405 2 1 2 0
406 3 1 2 0
407 2 1 2 1
408 3 1 2 0
409 3 0 2 0
410 3 1 2 0
411 3 1 1 0
412 1 0 1 1
413 2 1 2 0
414 3 1 2 1
415 3 0 2 0
416 2 0 2 1
417 2 0 2 1
418 2 1 2 0
419 3 0 2 0
420 3 1 0 0
421 3 1 1 0
422 3 1 2 0
423 3 0 2 0
424 3 1 2 0
425 3 1 2 0
426 2 0 2 1
427 2 0 2 1
428 3 1 1 0
429 3 1 2 1
430 1 1 2 1
431 3 0 2 1
432 2 0 2 1
433 3 1 2 0
434 1 1 2 0
435 1 0 2 1
436 3 0 2 0
437 2 0 2 1
438 1 1 2 0
439 2 1 2 0
440 2 0 2 1
441 3 1 2 0
442 3 1 2 0
443 2 0 2 1
444 3 1 2 1
445 1 1 2 1
446 2 0 2 1
447 1 1 2 1
448 3 0 0 1
449 1 1 2 1
450 2 1 2 0
451 3 1 2 0
452 1 1 0 0
453 1 1 0 1
454 3 1 2 0
455 3 1 0 1
456 1 1 2 0
457 1 0 2 1
458 2 0 2 1
459 3 1 1 0
460 1 1 2 1
461 3 1 2 0
462 1 1 2 0
463 2 1 2 0
464 3 1 2 0
465 3 1 2 0
466 2 1 2 0
467 1 1 2 0
468 3 1 1 0
469 3 0 0 1
470 3 1 2 0
471 3 1 2 0
472 2 0 2 1
473 2 0 0 1
474 3 0 2 0
475 1 1 2 0
476 2 1 2 0
477 3 1 2 0
478 3 1 2 0
479 3 0 2 1
480 3 1 2 0
481 2 1 2 0
482 3 1 2 0
483 3 0 2 1
484 1 1 0 1
485 3 0 2 0
486 1 0 2 1
487 1 1 0 0
488 3 1 2 0
489 3 1 2 1
490 3 1 2 0
491 3 1 2 0
492 1 1 2 0
493 1 1 0 0
494 3 1 2 0
495 3 1 0 0
496 1 0 0 1
497 3 1 2 0
498 1 0 2 0
499 3 1 2 0
500 3 1 2 0
501 3 0 1 0
502 3 0 1 0
503 3 0 2 0
504 1 0 2 1
505 1 1 0 0
506 2 0 2 1
507 1 1 2 1
508 3 1 2 0
509 3 1 2 1
510 3 1 1 1
511 3 1 2 0
512 1 1 2 1
513 1 0 0 1
514 3 1 2 0
515 1 1 2 0
516 2 0 2 1
517 3 1 1 0
518 2 0 2 1
519 3 1 2 0
520 1 0 2 1
521 3 1 2 0
522 3 1 0 0
523 1 0 0 1
524 3 1 0 0
525 3 1 1 0
526 2 0 2 1
527 1 1 2 0
528 3 1 2 0
529 2 1 2 0
530 2 0 2 1
531 3 1 0 0
532 3 1 0 0
533 3 0 0 1
534 3 0 2 0
535 2 0 2 1
536 1 1 2 0
537 1 0 0 1
538 3 1 2 0
539 1 0 0 1
540 1 0 2 1
541 3 0 2 0
542 3 0 2 0
543 2 1 2 1
544 1 1 0 0
545 1 1 2 0
546 2 0 2 1
547 2 1 0 1
548 3 1 2 0
549 2 1 2 1
550 1 1 0 1
551 2 1 2 0
552 3 1 1 0
553 3 1 0 1
554 3 0 2 1
555 1 1 2 0
556 1 0 0 1
557 1 1 0 0
558 1 0 2 1
559 3 0 2 1
560 3 1 1 0
561 3 1 2 0
562 2 1 2 0
563 3 1 2 0
564 3 0 2 0
565 3 1 2 0
566 3 1 2 0
567 3 0 2 0
568 3 1 0 0
569 3 1 2 1
570 2 1 2 1
571 1 0 2 1
572 1 1 2 1
573 3 0 1 1
574 3 1 2 0
575 3 1 2 0
576 2 0 2 1
577 1 0 2 1
578 3 0 0 0
579 3 1 2 1
580 2 0 2 1
581 1 0 0 1
582 2 1 2 0
583 1 1 0 0
584 3 1 0 0
585 1 0 2 1
586 2 1 2 0
587 1 1 0 1
588 3 1 2 0
589 3 1 2 0
590 3 1 2 0
591 1 0 0 1
592 3 1 2 0
593 3 0 1 0
594 2 1 2 0
595 3 1 2 0
596 2 0 2 1
597 3 1 2 0
598 3 1 0 0
599 1 1 0 1
600 2 0 2 1
601 3 1 2 0
602 1 1 2 0
603 3 1 2 0
604 1 1 0 1
605 3 1 2 0
606 3 1 2 0
607 1 1 2 1
608 2 0 0 1
609 1 0 2 1
610 3 0 2 0
611 3 1 2 0
612 3 0 1 1
613 3 1 1 0
614 3 1 2 0
615 2 0 2 1
616 3 1 2 0
617 3 0 2 0
618 2 0 2 1
619 2 1 2 0
620 3 1 0 0
621 1 1 2 1
622 3 1 0 1
623 3 1 2 0
624 3 1 2 0
625 1 1 2 0
626 2 1 1 0
627 1 0 2 1
628 3 1 2 0
629 3 1 1 0
630 1 1 2 1
631 3 1 2 0
632 1 1 0 1
633 1 1 2 0
634 3 0 2 0
635 2 0 2 1
636 3 1 2 0
637 2 1 2 0
638 3 0 2 0
639 3 1 2 0
640 3 1 2 0
641 1 0 0 1
642 3 0 2 0
643 3 1 2 1
644 3 0 0 1
645 1 1 0 1
646 3 1 2 0
647 1 1 0 1
648 3 1 2 0
649 3 0 2 1
650 3 1 2 0
651 2 0 2 1
652 3 1 2 0
653 3 0 1 1
654 3 0 1 0
655 2 1 2 0
656 3 1 2 0
657 3 0 1 0
658 2 1 2 0
659 1 1 0 0
660 1 1 2 1
661 3 1 0 0
662 1 1 2 0
663 3 1 2 0
664 3 1 2 1
665 2 1 2 0
666 2 1 2 0
667 3 1 2 0
668 3 1 2 0
669 1 0 2 1
670 2 0 2 1
671 1 1 2 0
672 2 1 2 0
673 2 1 2 1
674 2 1 2 0
675 3 1 2 0
676 3 1 2 0
677 3 0 2 1
678 3 0 2 0
679 1 1 0 1
680 3 0 1 0
681 1 1 0 1
682 3 1 2 0
683 3 1 2 0
684 2 1 2 0
685 2 1 0 0
686 3 1 2 0
687 3 1 2 0
688 3 1 2 0
689 1 0 2 1
690 1 1 2 1
691 3 0 0 1
692 3 1 2 1
693 3 1 0 0
694 1 1 2 0
695 2 1 2 0
696 3 1 2 0
697 3 0 1 1
698 1 1 0 0
699 3 1 2 0
700 1 0 0 1
701 1 1 2 1
702 3 0 0 0
703 3 1 1 0
704 3 1 2 0
705 2 1 2 0
706 2 0 2 1
707 1 1 2 1
708 1 0 2 1
709 3 1 0 1
710 1 0 0 1
711 1 1 2 0
712 1 1 2 1
713 3 1 2 0
714 2 1 2 0
715 3 1 2 0
716 1 0 0 1
717 2 0 2 1
718 3 1 1 0
719 3 1 2 0
720 2 0 2 1
721 3 1 2 0
722 2 1 2 0
723 2 1 2 0
724 1 1 2 1
725 3 1 2 0
726 2 0 2 1
727 3 0 1 1
728 2 1 2 0
729 3 0 2 0
730 1 0 2 1
731 3 1 0 0
732 2 1 2 0
733 2 1 2 0
734 2 1 2 0
735 3 1 2 0
736 3 0 2 0
737 1 1 0 1
738 3 1 2 0
739 3 1 2 0
740 1 1 2 1
741 1 1 2 0
742 1 0 0 1
743 3 1 2 0
744 3 1 2 1
745 1 1 2 0
746 3 1 2 0
747 2 0 2 1
748 1 1 2 0
749 3 1 1 0
750 2 0 2 1
751 3 1 2 1
752 3 1 2 0
753 3 1 2 0
754 2 0 2 1
755 2 1 2 1
756 3 1 2 0
757 2 1 2 0
758 3 1 2 0
759 1 0 2 1
760 3 1 2 0
761 3 1 2 0
762 3 1 0 1
763 1 0 2 1
764 3 1 2 0
765 1 0 2 1
766 1 1 0 0
767 3 0 1 0
768 3 1 1 0
769 3 1 2 0
770 3 1 2 0
771 3 1 2 0
772 2 0 2 0
773 3 1 0 0
774 2 0 2 1
775 3 1 2 0
776 3 1 1 0
777 3 0 2 1
778 3 1 1 0
779 1 0 2 1
780 3 0 0 1
781 1 0 2 1
782 1 1 2 0
783 3 1 2 0
784 3 1 2 0
785 3 1 2 0
786 3 0 2 1
787 3 1 1 0
788 3 1 2 1
789 1 1 0 0
790 3 1 1 0
791 2 1 2 0
792 3 0 2 0
793 1 1 0 0
794 3 1 2 0
795 2 1 2 0
796 1 0 2 1
797 3 0 2 1
798 3 1 0 0
799 3 0 2 0
800 2 1 2 0
801 2 0 2 1
802 1 1 2 1
803 3 1 0 1
804 3 1 2 1
805 3 1 2 0
806 1 1 2 0
807 3 0 2 0
808 2 1 2 0
809 1 0 2 1
810 3 1 2 0
811 3 1 2 0
812 2 1 2 0
813 3 0 2 0
814 3 1 2 0
815 1 1 2 0
816 3 0 2 0
817 2 1 0 0
818 3 1 2 0
819 3 1 2 0
820 1 0 2 1
821 3 1 2 1
822 1 1 2 0
823 3 0 2 1
824 3 1 2 0
825 3 1 1 0
826 3 1 2 0
827 2 1 0 1
828 3 1 1 1
829 3 0 0 1
830 2 1 2 1
831 3 1 0 0
832 3 1 2 0
833 3 1 2 0
834 1 0 0 1
835 3 1 2 0
836 3 1 2 0
837 3 1 2 1
838 1 1 0 1
839 3 1 2 0
840 2 1 2 0
841 1 0 0 1
842 3 1 0 0
843 3 1 2 0
844 3 1 2 0
845 3 1 2 0
846 3 1 0 0
847 2 1 2 0
848 1 0 0 1
849 3 1 2 0
850 3 1 2 0
851 3 0 0 0
852 1 0 2 1
853 2 0 2 0
854 3 0 2 1
855 1 0 2 1
856 1 1 2 1
857 3 0 0 1
858 3 1 0 0
859 3 1 2 0
860 2 1 2 0
861 1 0 2 1
862 3 0 2 0
863 2 1 2 0
864 2 0 2 1
865 2 0 0 1
866 1 1 2 0
867 3 1 2 0
868 3 1 2 1
869 3 1 2 0
870 1 0 2 1
871 1 1 2 0
872 3 1 2 0
873 2 0 0 1
874 3 0 0 1
875 3 1 2 0
876 3 1 2 0
877 3 1 2 0
878 1 0 0 1
879 2 0 2 1
880 3 1 2 0
881 3 0 2 0
882 2 1 2 0
883 3 1 2 0
884 3 0 1 0
885 2 1 2 0
886 1 0 2 1
887 3 0 2 0
888 1 1 0 1
889 3 1 1 0

92
examples/trees/cart.go Normal file
View File

@ -0,0 +1,92 @@
// Example of how to use CART trees for both Classification and Regression
package main
import (
"fmt"
"github.com/sjwhitworth/golearn/base"
"github.com/sjwhitworth/golearn/trees"
)
func main() {
/* Performance of CART Algorithm:
Training Time for Titanic Dataset 611 µs
Prediction Time for Titanic Datset 101 µs
Complexity Analysis:
1x Dataset -- x ms
2x Dataset -- 1.7x ms
128x Dataset -- 74x ms
Complexity is sub linear
Sklearn:
Training Time for Titanic Dataset 8.8 µs
Prediction Time for Titanic Datset  7.87 µs
This implementation and sci-kit learn produce the exact same tree for the exact same dataset.
Predictions on the same test set also yield the exact same accuracy.
This implementation is optimized to prevent redundant iterations over the dataset, but it is not completely optimized. Also, sklearn makes use of numpy to access column easily, whereas here a complete iteration is required.
In terms of Hyperparameters, this implmentation gives you the ability to choose the impurity function and the maxDepth.
Many of the other hyperparameters used in sklearn are not here, but pruning and impurity is included.
*/
// Load Titanic Data For classification
classificationData, err := base.ParseCSVToInstances("../datasets/titanic.csv", false)
if err != nil {
panic(err)
}
trainData, testData := base.InstancesTrainTestSplit(classificationData, 0.5)
// Create New Classification Tree
// Hyperparameters - loss function, max Depth (-1 will split until pure), list of unique labels
decTree := NewDecisionTreeClassifier("entropy", -1, []int64{0, 1})
// Train Tree
err = decTree.Fit(trainData)
if err != nil {
panic(err)
}
// Print out tree for visualization - shows splits and feature and predictions
fmt.Println(decTree.String())
// Access Predictions
classificationPreds := decTree.Predict(testData)
fmt.Println("Titanic Predictions")
fmt.Println(classificationPreds)
// Evaluate Accuracy on Test Data
fmt.Println(decTree.Evaluate(testData))
// Load House Price Data For Regression
regressionData, err := base.ParseCSVToInstances("../datasets/boston_house_prices.csv", false)
if err != nil {
panic(err)
}
trainRegData, testRegData := base.InstancesTrainTestSplit(regressionData, 0.5)
// Hyperparameters - Loss function, max Depth (-1 will split until pure)
regTree := NewDecisionTreeRegressor("mse", -1)
// Train Tree
err = regTree.Fit(trainRegData)
if err != nil {
panic(err)
}
// Print out tree for visualization
fmt.Println(regTree.String())
// Access Predictions
regressionPreds := regTree.Predict(testRegData)
fmt.Println("Boston House Price Predictions")
fmt.Println(regressionPreds)
}

441
trees/cart_classifier.go Normal file
View File

@ -0,0 +1,441 @@
package trees
import (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
"github.com/sjwhitworth/golearn/base"
)
const (
GINI string = "gini"
ENTROPY string = "entropy"
)
// CNode is Node struct for Decision Tree Classifier.
// It holds the information for each split (which feature to use, what threshold, and which label to assign for each side of the split)
type classifierNode struct {
Left *classifierNode
Right *classifierNode
Threshold float64
Feature int64
LeftLabel int64
RightLabel int64
isNodeNeeded bool
}
// CARTDecisionTreeClassifier: Tree struct for Decision Tree Classifier
// It contains the rootNode, as well as all of the hyperparameters chosen by the user.
// It also keeps track of all splits done at the tree level.
type CARTDecisionTreeClassifier struct {
RootNode *classifierNode
criterion string
maxDepth int64
labels []int64
triedSplits [][]float64
}
// Convert a series of labels to frequency map for efficient impurity calculation
func convertToMap(y []int64, labels []int64) map[int64]int {
labelCount := make(map[int64]int)
for _, label := range labels {
labelCount[label] = 0
}
for _, value := range y {
labelCount[value]++
}
return labelCount
}
// Calculate Gini Impurity of Target Labels
func computeGiniImpurityAndModeLabel(y []int64, labels []int64) (float64, int64) {
nInstances := len(y)
gini := 0.0
var maxLabel int64 = 0
labelCount := convertToMap(y, labels)
for _, label := range labels {
if labelCount[label] > labelCount[maxLabel] {
maxLabel = label
}
p := float64(labelCount[label]) / float64(nInstances)
gini += p * (1 - p)
}
return gini, maxLabel
}
// Calculate Entropy loss of Target Labels
func computeEntropyAndModeLabel(y []int64, labels []int64) (float64, int64) {
nInstances := len(y)
entropy := 0.0
var maxLabel int64 = 0
labelCount := convertToMap(y, labels)
for _, label := range labels {
if labelCount[label] > labelCount[maxLabel] {
maxLabel = label
}
p := float64(labelCount[label]) / float64(nInstances)
logP := math.Log2(p)
if p == 0 {
logP = 0
}
entropy += (-p * logP)
}
return entropy, maxLabel
}
func calculateClassificationLoss(y []int64, labels []int64, criterion string) (float64, int64, error) {
if len(y) == 0 {
return 0, 0, errors.New("Need atleast 1 value to compute impurity")
}
if criterion == GINI {
loss, modeLabel := computeGiniImpurityAndModeLabel(y, labels)
return loss, modeLabel, nil
} else if criterion == ENTROPY {
loss, modeLabel := computeEntropyAndModeLabel(y, labels)
return loss, modeLabel, nil
} else {
return 0, 0, errors.New("Invalid impurity function, choose from GINI or ENTROPY")
}
}
// Split the data into left node and right node based on feature and threshold
func classifierCreateSplit(data [][]float64, feature int64, y []int64, threshold float64) ([][]float64, [][]float64, []int64, []int64) {
var left [][]float64
var right [][]float64
var lefty []int64
var righty []int64
for i := range data {
example := data[i]
if example[feature] < threshold {
left = append(left, example)
lefty = append(lefty, y[i])
} else {
right = append(right, example)
righty = append(righty, y[i])
}
}
return left, right, lefty, righty
}
// Function to Create New Decision Tree Classifier.
// It assigns all of the hyperparameters by user into the tree attributes.
func NewDecisionTreeClassifier(criterion string, maxDepth int64, labels []int64) *CARTDecisionTreeClassifier {
var tree CARTDecisionTreeClassifier
tree.criterion = strings.ToLower(criterion)
tree.maxDepth = maxDepth
tree.labels = labels
return &tree
}
// Reorder the data by feature being considered. Optimizes code by reducing the number of times we have to loop over data for splitting
func classifierReOrderData(featureVal []float64, data [][]float64, y []int64) ([][]float64, []int64) {
s := NewSlice(featureVal)
sort.Sort(s)
indexes := s.Idx
var dataSorted [][]float64
var ySorted []int64
for _, index := range indexes {
dataSorted = append(dataSorted, data[index])
ySorted = append(ySorted, y[index])
}
return dataSorted, ySorted
}
// Update the left and right side of the split based on the threshold.
func classifierUpdateSplit(left [][]float64, leftY []int64, right [][]float64, rightY []int64, feature int64, threshold float64) ([][]float64, []int64, [][]float64, []int64) {
for right[0][feature] < threshold {
left = append(left, right[0])
right = right[1:]
leftY = append(leftY, rightY[0])
rightY = rightY[1:]
}
return left, leftY, right, rightY
}
// Fit - Creates an Empty Root Node
// Trains the tree by calling recursive function classifierBestSplit
func (tree *CARTDecisionTreeClassifier) Fit(X base.FixedDataGrid) error {
var emptyNode classifierNode
var err error
data := convertInstancesToProblemVec(X)
y, err := classifierConvertInstancesToLabelVec(X)
if err != nil {
return err
}
emptyNode, err = classifierBestSplit(*tree, data, y, tree.labels, emptyNode, tree.criterion, tree.maxDepth, 0)
if err != nil {
return err
}
tree.RootNode = &emptyNode
return nil
}
// Iterativly find and record the best split
// Stop If depth reaches maxDepth or nodes are pure
func classifierBestSplit(tree CARTDecisionTreeClassifier, data [][]float64, y []int64, labels []int64, upperNode classifierNode, criterion string, maxDepth int64, depth int64) (classifierNode, error) {
// Ensure that we have not reached maxDepth. maxDepth =-1 means split until nodes are pure
depth++
if maxDepth != -1 && depth > maxDepth {
return upperNode, nil
}
numFeatures := len(data[0])
var bestGini, origGini float64
var err error
// Calculate loss based on Criterion Specified by user
origGini, upperNode.LeftLabel, err = calculateClassificationLoss(y, labels, criterion)
if err != nil {
return upperNode, err
}
bestGini = origGini
bestLeft, bestRight, bestLefty, bestRighty := data, data, y, y
numData := len(data)
bestLeftGini, bestRightGini := bestGini, bestGini
upperNode.isNodeNeeded = true
var leftN, rightN classifierNode
// Iterate over all features
for i := 0; i < numFeatures; i++ {
featureVal := getFeature(data, int64(i))
unique := findUnique(featureVal)
sort.Float64s(unique)
sortData, sortY := classifierReOrderData(featureVal, data, y)
firstTime := true
var left, right [][]float64
var leftY, rightY []int64
// Iterate over all possible thresholds for that feature
for j := 0; j < len(unique)-1; j++ {
threshold := (unique[j] + unique[j+1]) / 2
// Ensure that same split has not been made before
if validate(tree.triedSplits, int64(i), threshold) {
// We need to split data from fresh when considering new feature for the first time.
// Otherwise, we need to update the split by moving data points from left to right.
if firstTime {
left, right, leftY, rightY = classifierCreateSplit(sortData, int64(i), sortY, threshold)
firstTime = false
} else {
left, leftY, right, rightY = classifierUpdateSplit(left, leftY, right, rightY, int64(i), threshold)
}
var leftGini, rightGini float64
var leftLabels, rightLabels int64
leftGini, leftLabels, _ = calculateClassificationLoss(leftY, labels, criterion)
rightGini, rightLabels, _ = calculateClassificationLoss(rightY, labels, criterion)
// Calculate weighted gini impurity of child nodes
subGini := (leftGini * float64(len(left)) / float64(numData)) + (rightGini * float64(len(right)) / float64(numData))
// If we find a split that reduces impurity
if subGini < bestGini {
bestGini = subGini
bestLeft, bestRight = left, right
bestLefty, bestRighty = leftY, rightY
upperNode.Threshold, upperNode.Feature = threshold, int64(i)
upperNode.LeftLabel, upperNode.RightLabel = leftLabels, rightLabels
bestLeftGini, bestRightGini = leftGini, rightGini
}
}
}
}
// If no split was found, we don't want to use this node, so we will flag it
if bestGini == origGini {
upperNode.isNodeNeeded = false
return upperNode, nil
}
// Until nodes are not pure
if bestGini > 0 {
// If left node is pure, no need to split on left side again
if bestLeftGini > 0 {
tree.triedSplits = append(tree.triedSplits, []float64{float64(upperNode.Feature), upperNode.Threshold})
// Recursive splitting logic
leftN, err = classifierBestSplit(tree, bestLeft, bestLefty, labels, leftN, criterion, maxDepth, depth)
if err != nil {
return upperNode, err
}
if leftN.isNodeNeeded == true {
upperNode.Left = &leftN
}
}
// If right node is pure, no need to split on right side again
if bestRightGini > 0 {
tree.triedSplits = append(tree.triedSplits, []float64{float64(upperNode.Feature), upperNode.Threshold})
// Recursive splitting logic
rightN, err = classifierBestSplit(tree, bestRight, bestRighty, labels, rightN, criterion, maxDepth, depth)
if err != nil {
return upperNode, err
}
if rightN.isNodeNeeded == true {
upperNode.Right = &rightN
}
}
}
// Return the node - contains all information regarding feature and threshold.
return upperNode, nil
}
// String : this function prints out entire tree for visualization.
// Calls a recursive function to print the tree - classifierPrintTreeFromNode
func (tree *CARTDecisionTreeClassifier) String() string {
rootNode := *tree.RootNode
return classifierPrintTreeFromNode(rootNode, "")
}
func classifierPrintTreeFromNode(tree classifierNode, spacing string) string {
returnString := ""
returnString += spacing + "Feature "
returnString += strconv.FormatInt(tree.Feature, 10)
returnString += " < "
returnString += fmt.Sprintf("%.3f", tree.Threshold)
returnString += "\n"
if tree.Left == nil {
returnString += spacing + "---> True" + "\n"
returnString += " " + spacing + "PREDICT "
returnString += strconv.FormatInt(tree.LeftLabel, 10) + "\n"
}
if tree.Right == nil {
returnString += spacing + "---> False" + "\n"
returnString += " " + spacing + "PREDICT "
returnString += strconv.FormatInt(tree.RightLabel, 10) + "\n"
}
if tree.Left != nil {
returnString += spacing + "---> True" + "\n"
returnString += classifierPrintTreeFromNode(*tree.Left, spacing+" ")
}
if tree.Right != nil {
returnString += spacing + "---> False" + "\n"
returnString += classifierPrintTreeFromNode(*tree.Right, spacing+" ")
}
return returnString
}
// Predict a single data point by traversing the entire tree
// Uses recursive logic to navigate the tree.
func classifierPredictSingle(tree classifierNode, instance []float64) int64 {
if instance[tree.Feature] < tree.Threshold {
if tree.Left == nil {
return tree.LeftLabel
} else {
return classifierPredictSingle(*tree.Left, instance)
}
} else {
if tree.Right == nil {
return tree.RightLabel
} else {
return classifierPredictSingle(*tree.Right, instance)
}
}
}
// Given test data, return predictions for every datapoint. calls classifierPredictFromNode
func (tree *CARTDecisionTreeClassifier) Predict(X_test base.FixedDataGrid) []int64 {
root := *tree.RootNode
test := convertInstancesToProblemVec(X_test)
return classifierPredictFromNode(root, test)
}
// This function uses the rootnode from Predict.
// It iterates through every data point and calls the recursive function to give predictions and then summarizes them.
func classifierPredictFromNode(tree classifierNode, test [][]float64) []int64 {
var preds []int64
for i := range test {
iPred := classifierPredictSingle(tree, test[i])
preds = append(preds, iPred)
}
return preds
}
// Given Test data and label, return the accuracy of the classifier.
// First it retreives predictions from the data, then compares for accuracy.
// Calls classifierEvaluateFromNode
func (tree *CARTDecisionTreeClassifier) Evaluate(test base.FixedDataGrid) (float64, error) {
rootNode := *tree.RootNode
xTest := convertInstancesToProblemVec(test)
yTest, err := classifierConvertInstancesToLabelVec(test)
if err != nil {
return 0, err
}
return classifierEvaluateFromNode(rootNode, xTest, yTest), nil
}
// Retrieve predictions and then calculate accuracy.
func classifierEvaluateFromNode(tree classifierNode, xTest [][]float64, yTest []int64) float64 {
preds := classifierPredictFromNode(tree, xTest)
accuracy := 0.0
for i := range preds {
if preds[i] == yTest[i] {
accuracy++
}
}
accuracy /= float64(len(yTest))
return accuracy
}
// Helper function to convert base.FixedDataGrid into required format. Called in Fit, Predict
func classifierConvertInstancesToLabelVec(X base.FixedDataGrid) ([]int64, error) {
// Get the class Attributes
classAttrs := X.AllClassAttributes()
// Only support 1 class Attribute
if len(classAttrs) != 1 {
return []int64{0}, errors.New(fmt.Sprintf("%d ClassAttributes (1 expected)", len(classAttrs)))
}
// ClassAttribute must be numeric
if _, ok := classAttrs[0].(*base.FloatAttribute); !ok {
return []int64{0}, errors.New(fmt.Sprintf("%s: ClassAttribute must be a FloatAttribute", classAttrs[0]))
}
// Allocate return structure
_, rows := X.Size()
labelVec := make([]int64, rows)
// Resolve class Attribute specification
classAttrSpecs := base.ResolveAttributes(X, classAttrs)
X.MapOverRows(classAttrSpecs, func(row [][]byte, rowNo int) (bool, error) {
labelVec[rowNo] = int64(base.UnpackBytesToFloat(row[0]))
return true, nil
})
return labelVec, nil
}

412
trees/cart_regressor.go Normal file
View File

@ -0,0 +1,412 @@
package trees
import (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
"github.com/sjwhitworth/golearn/base"
)
const (
MAE string = "mae"
MSE string = "mse"
)
// RNode - Node struct for Decision Tree Regressor
// It holds the information for each split
// Which feature to use, threshold, left prediction and right prediction
type regressorNode struct {
Left *regressorNode
Right *regressorNode
Threshold float64
Feature int64
LeftPred float64
RightPred float64
isNodeNeeded bool
}
// CARTDecisionTreeRegressor - Tree struct for Decision Tree Regressor
// It contains the rootNode, as well as the hyperparameters chosen by user.
// Also keeps track of splits used at tree level.
type CARTDecisionTreeRegressor struct {
RootNode *regressorNode
criterion string
maxDepth int64
triedSplits [][]float64
}
// Find average
func average(y []float64) float64 {
mean := 0.0
for _, value := range y {
mean += value
}
mean /= float64(len(y))
return mean
}
// Calculate Mean Absolute Error for a constant prediction
func meanAbsoluteError(y []float64, yBar float64) float64 {
error := 0.0
for _, target := range y {
error += math.Abs(target - yBar)
}
error /= float64(len(y))
return error
}
// Turn Mean Absolute Error into impurity function for decision trees.
func computeMaeImpurityAndAverage(y []float64) (float64, float64) {
yHat := average(y)
return meanAbsoluteError(y, yHat), yHat
}
// Calculate Mean Squared Error for constant prediction
func meanSquaredError(y []float64, yBar float64) float64 {
error := 0.0
for _, target := range y {
itemError := target - yBar
error += math.Pow(itemError, 2)
}
error /= float64(len(y))
return error
}
// Convert mean squared error into impurity function for decision trees
func computeMseImpurityAndAverage(y []float64) (float64, float64) {
yHat := average(y)
return meanSquaredError(y, yHat), yHat
}
func calculateRegressionLoss(y []float64, criterion string) (float64, float64, error) {
if criterion == MAE {
loss, avg := computeMaeImpurityAndAverage(y)
return loss, avg, nil
} else if criterion == MSE {
loss, avg := computeMseImpurityAndAverage(y)
return loss, avg, nil
} else {
panic("Invalid impurity function, choose from MAE or MSE")
}
}
// Split the data into left and right based on trehsold and feature.
func regressorCreateSplit(data [][]float64, feature int64, y []float64, threshold float64) ([][]float64, [][]float64, []float64, []float64) {
var left [][]float64
var lefty []float64
var right [][]float64
var righty []float64
for i := range data {
example := data[i]
if example[feature] < threshold {
left = append(left, example)
lefty = append(lefty, y[i])
} else {
right = append(right, example)
righty = append(righty, y[i])
}
}
return left, right, lefty, righty
}
// Interface for creating new Decision Tree Regressor
func NewDecisionTreeRegressor(criterion string, maxDepth int64) *CARTDecisionTreeRegressor {
var tree CARTDecisionTreeRegressor
tree.maxDepth = maxDepth
tree.criterion = strings.ToLower(criterion)
return &tree
}
// Re order data based on a feature for optimizing code
// Helps in updating splits without reiterating entire dataset
func regressorReOrderData(featureVal []float64, data [][]float64, y []float64) ([][]float64, []float64) {
s := NewSlice(featureVal)
sort.Sort(s)
indexes := s.Idx
var dataSorted [][]float64
var ySorted []float64
for _, index := range indexes {
dataSorted = append(dataSorted, data[index])
ySorted = append(ySorted, y[index])
}
return dataSorted, ySorted
}
// Update the left and right data based on change in threshold
func regressorUpdateSplit(left [][]float64, leftY []float64, right [][]float64, rightY []float64, feature int64, threshold float64) ([][]float64, []float64, [][]float64, []float64) {
for right[0][feature] < threshold {
left = append(left, right[0])
right = right[1:]
leftY = append(leftY, rightY[0])
rightY = rightY[1:]
}
return left, leftY, right, rightY
}
// Fit - Build the tree using the data
// Creates empty root node and builds tree by calling regressorBestSplit
func (tree *CARTDecisionTreeRegressor) Fit(X base.FixedDataGrid) error {
var emptyNode regressorNode
var err error
data := regressorConvertInstancesToProblemVec(X)
y, err := regressorConvertInstancesToLabelVec(X)
if err != nil {
return err
}
emptyNode, err = regressorBestSplit(*tree, data, y, emptyNode, tree.criterion, tree.maxDepth, 0)
if err != nil {
return err
}
tree.RootNode = &emptyNode
return nil
}
// Builds the tree by iteratively finding the best split.
// Recursive function - stops if maxDepth is reached or nodes are pure
func regressorBestSplit(tree CARTDecisionTreeRegressor, data [][]float64, y []float64, upperNode regressorNode, criterion string, maxDepth int64, depth int64) (regressorNode, error) {
// Ensure that we have not reached maxDepth. maxDepth =-1 means split until nodes are pure
depth++
if depth > maxDepth && maxDepth != -1 {
return upperNode, nil
}
numFeatures := len(data[0])
var bestLoss, origLoss float64
var err error
origLoss, upperNode.LeftPred, err = calculateRegressionLoss(y, criterion)
if err != nil {
return upperNode, err
}
bestLoss = origLoss
bestLeft, bestRight, bestLefty, bestRighty := data, data, y, y
numData := len(data)
bestLeftLoss, bestRightLoss := bestLoss, bestLoss
upperNode.isNodeNeeded = true
var leftN, rightN regressorNode
// Iterate over all features
for i := 0; i < numFeatures; i++ {
featureVal := getFeature(data, int64(i))
unique := findUnique(featureVal)
sort.Float64s(unique)
sortData, sortY := regressorReOrderData(featureVal, data, y)
firstTime := true
var left, right [][]float64
var leftY, rightY []float64
for j := 0; j < len(unique)-1; j++ {
threshold := (unique[j] + unique[j+1]) / 2
if validate(tree.triedSplits, int64(i), threshold) {
if firstTime {
left, right, leftY, rightY = regressorCreateSplit(sortData, int64(i), sortY, threshold)
firstTime = false
} else {
left, leftY, right, rightY = regressorUpdateSplit(left, leftY, right, rightY, int64(i), threshold)
}
var leftLoss, rightLoss float64
var leftPred, rightPred float64
leftLoss, leftPred, _ = calculateRegressionLoss(leftY, criterion)
rightLoss, rightPred, _ = calculateRegressionLoss(rightY, criterion)
subLoss := (leftLoss * float64(len(left)) / float64(numData)) + (rightLoss * float64(len(right)) / float64(numData))
if subLoss < bestLoss {
bestLoss = subLoss
bestLeft, bestRight = left, right
bestLefty, bestRighty = leftY, rightY
upperNode.Threshold, upperNode.Feature = threshold, int64(i)
upperNode.LeftPred, upperNode.RightPred = leftPred, rightPred
bestLeftLoss, bestRightLoss = leftLoss, rightLoss
}
}
}
}
if bestLoss == origLoss {
upperNode.isNodeNeeded = false
return upperNode, nil
}
if bestLoss > 0 {
if bestLeftLoss > 0 {
tree.triedSplits = append(tree.triedSplits, []float64{float64(upperNode.Feature), upperNode.Threshold})
leftN, err = regressorBestSplit(tree, bestLeft, bestLefty, leftN, criterion, maxDepth, depth)
if err != nil {
return upperNode, err
}
if leftN.isNodeNeeded == true {
upperNode.Left = &leftN
}
}
if bestRightLoss > 0 {
tree.triedSplits = append(tree.triedSplits, []float64{float64(upperNode.Feature), upperNode.Threshold})
rightN, err = regressorBestSplit(tree, bestRight, bestRighty, rightN, criterion, maxDepth, depth)
if err != nil {
return upperNode, err
}
if rightN.isNodeNeeded == true {
upperNode.Right = &rightN
}
}
}
return upperNode, nil
}
// Print Tree for Visualtion - calls regressorPrintTreeFromNode()
func (tree *CARTDecisionTreeRegressor) String() string {
rootNode := *tree.RootNode
return regressorPrintTreeFromNode(rootNode, "")
}
// Recursively explore the entire tree and print out all details such as threshold, feature, prediction
func regressorPrintTreeFromNode(tree regressorNode, spacing string) string {
returnString := ""
returnString += spacing + "Feature "
returnString += strconv.FormatInt(tree.Feature, 10)
returnString += " < "
returnString += fmt.Sprintf("%.3f", tree.Threshold)
returnString += "\n"
if tree.Left == nil {
returnString += spacing + "---> True" + "\n"
returnString += " " + spacing + "PREDICT "
returnString += fmt.Sprintf("%.3f", tree.LeftPred) + "\n"
}
if tree.Right == nil {
returnString += spacing + "---> False" + "\n"
returnString += " " + spacing + "PREDICT "
returnString += fmt.Sprintf("%.3f", tree.RightPred) + "\n"
}
if tree.Left != nil {
returnString += spacing + "---> True" + "\n"
returnString += regressorPrintTreeFromNode(*tree.Left, spacing+" ")
}
if tree.Right != nil {
returnString += spacing + "---> False" + "\n"
returnString += regressorPrintTreeFromNode(*tree.Right, spacing+" ")
}
return returnString
}
// Predict a single data point by navigating to rootNodes.
// Uses a recursive logic
func regressorPredictSingle(tree regressorNode, instance []float64) float64 {
if instance[tree.Feature] < tree.Threshold {
if tree.Left == nil {
return tree.LeftPred
} else {
return regressorPredictSingle(*tree.Left, instance)
}
} else {
if tree.Right == nil {
return tree.RightPred
} else {
return regressorPredictSingle(*tree.Right, instance)
}
}
}
// Predict method for multiple data points.
// First converts input data into usable format, and then calls regressorPredictFromNode
func (tree *CARTDecisionTreeRegressor) Predict(X_test base.FixedDataGrid) []float64 {
root := *tree.RootNode
test := regressorConvertInstancesToProblemVec(X_test)
return regressorPredictFromNode(root, test)
}
// Use tree's root node to print out entire tree.
// Iterates over all data points and calls regressorPredictSingle to predict individual datapoints.
func regressorPredictFromNode(tree regressorNode, test [][]float64) []float64 {
var preds []float64
for i := range test {
i_pred := regressorPredictSingle(tree, test[i])
preds = append(preds, i_pred)
}
return preds
}
// Helper function to convert base.FixedDataGrid into required format. Called in Fit, Predict
func regressorConvertInstancesToProblemVec(X base.FixedDataGrid) [][]float64 {
// Allocate problem array
_, rows := X.Size()
problemVec := make([][]float64, rows)
// Retrieve numeric non-class Attributes
numericAttrs := base.NonClassFloatAttributes(X)
numericAttrSpecs := base.ResolveAttributes(X, numericAttrs)
// Convert each row
X.MapOverRows(numericAttrSpecs, func(row [][]byte, rowNo int) (bool, error) {
// Allocate a new row
probRow := make([]float64, len(numericAttrSpecs))
// Read out the row
for i, _ := range numericAttrSpecs {
probRow[i] = base.UnpackBytesToFloat(row[i])
}
// Add the row
problemVec[rowNo] = probRow
return true, nil
})
return problemVec
}
// Helper function to convert base.FixedDataGrid into required format. Called in Fit, Predict
func regressorConvertInstancesToLabelVec(X base.FixedDataGrid) ([]float64, error) {
// Get the class Attributes
classAttrs := X.AllClassAttributes()
// Only support 1 class Attribute
if len(classAttrs) != 1 {
return []float64{0}, errors.New(fmt.Sprintf("%d ClassAttributes (1 expected)", len(classAttrs)))
}
// ClassAttribute must be numeric
if _, ok := classAttrs[0].(*base.FloatAttribute); !ok {
return []float64{0}, errors.New(fmt.Sprintf("%s: ClassAttribute must be a FloatAttribute", classAttrs[0]))
}
// Allocate return structure
_, rows := X.Size()
labelVec := make([]float64, rows)
// Resolve class Attribute specification
classAttrSpecs := base.ResolveAttributes(X, classAttrs)
X.MapOverRows(classAttrSpecs, func(row [][]byte, rowNo int) (bool, error) {
labelVec[rowNo] = base.UnpackBytesToFloat(row[0])
return true, nil
})
return labelVec, nil
}

104
trees/cart_test.go Normal file
View File

@ -0,0 +1,104 @@
package trees
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func TestRegressor(t *testing.T) {
Convey("Doing a CART Test", t, func() {
// For Classification Trees:
// Is Gini being calculated correctly
gini, giniMaxLabel := computeGiniImpurityAndModeLabel([]int64{1, 0, 0, 1}, []int64{0, 1})
So(gini, ShouldEqual, 0.5)
So(giniMaxLabel, ShouldNotBeNil)
// Is Entropy being calculated correctly
entropy, entropyMaxLabel := computeEntropyAndModeLabel([]int64{1, 0, 0, 1}, []int64{0, 1})
So(entropy, ShouldEqual, 1.0)
So(entropyMaxLabel, ShouldNotBeNil)
// Is Data being split into left and right properly
classifierData := [][]float64{[]float64{1, 3, 6},
[]float64{1, 2, 3},
[]float64{1, 9, 6},
[]float64{1, 11, 1}}
classifiery := []int64{0, 1, 0, 0}
leftdata, rightdata, lefty, righty := classifierCreateSplit(classifierData, 1, classifiery, 5.0)
So(len(leftdata), ShouldEqual, 2)
So(len(lefty), ShouldEqual, 2)
So(len(rightdata), ShouldEqual, 2)
So(len(righty), ShouldEqual, 2)
// Is isolating unique values working properly
So(len(findUnique([]float64{10, 1, 1})), ShouldEqual, 2)
// is data reordered correctly
orderedData, orderedY := classifierReOrderData(getFeature(classifierData, 1), classifierData, classifiery)
So(orderedData[1][1], ShouldEqual, 3.0)
So(orderedY[0], ShouldEqual, 1)
// Is split being updated properly based on threshold
leftdata, lefty, rightdata, righty = classifierUpdateSplit(leftdata, lefty, rightdata, righty, 1, 9.5)
So(len(leftdata), ShouldEqual, 3)
So(len(rightdata), ShouldEqual, 1)
// Is the root Node null when tree is not trained?
tree := NewDecisionTreeClassifier("gini", -1, []int64{0, 1})
So(tree.RootNode, ShouldBeNil)
So(tree.triedSplits, ShouldBeEmpty)
// ------------------------------------------
// For Regression Trees
// Is MAE being calculated correctly
mae, maeMaxLabel := computeMaeImpurityAndAverage([]float64{1, 3, 5})
So(mae, ShouldEqual, (4.0 / 3.0))
So(maeMaxLabel, ShouldNotBeNil)
// Is Entropy being calculated correctly
mse, mseMaxLabel := computeMseImpurityAndAverage([]float64{1, 3, 5})
So(mse, ShouldEqual, (8.0 / 3.0))
So(mseMaxLabel, ShouldNotBeNil)
// Is Data being split into left and right properly
data := [][]float64{[]float64{1, 3, 6},
[]float64{1, 2, 3},
[]float64{1, 9, 6},
[]float64{1, 11, 1}}
y := []float64{1, 2, 3, 4}
leftData, rightData, leftY, rightY := regressorCreateSplit(data, 1, y, 5.0)
So(len(leftData), ShouldEqual, 2)
So(len(leftY), ShouldEqual, 2)
So(len(rightData), ShouldEqual, 2)
So(len(rightY), ShouldEqual, 2)
// is data reordered correctly
regressorOrderedData, regressorOrderedY := regressorReOrderData(getFeature(data, 1), data, y)
So(regressorOrderedData[1][1], ShouldEqual, 3.0)
So(regressorOrderedY[0], ShouldEqual, 2)
// Is split being updated properly based on threshold
leftData, leftY, rightData, rightY = regressorUpdateSplit(leftData, leftY, rightData, rightY, 1, 9.5)
So(len(leftData), ShouldEqual, 3)
So(len(rightData), ShouldEqual, 1)
// Is the root Node null when tree is not trained?
regressorTreetree := NewDecisionTreeRegressor("mae", -1)
So(regressorTreetree.RootNode, ShouldBeNil)
So(regressorTreetree.triedSplits, ShouldBeEmpty)
})
}

65
trees/cart_utils.go Normal file
View File

@ -0,0 +1,65 @@
package trees
import (
"github.com/sjwhitworth/golearn/base"
)
// Isolate only unique values. This way, we can try only unique splits and not redundant ones.
func findUnique(data []float64) []float64 {
keys := make(map[float64]bool)
unique := []float64{}
for _, entry := range data {
if _, value := keys[entry]; !value {
keys[entry] = true
unique = append(unique, entry)
}
}
return unique
}
// Isolate only the feature being considered for splitting. Reduces the complexity in managing splits.
func getFeature(data [][]float64, feature int64) []float64 {
var featureVals []float64
for i := range data {
featureVals = append(featureVals, data[i][feature])
}
return featureVals
}
// Make sure that split being considered has not been done before.
// Else we will unnecessarily try splits that won't improve Impurity.
func validate(triedSplits [][]float64, feature int64, threshold float64) bool {
for i := range triedSplits {
split := triedSplits[i]
featureTried, thresholdTried := split[0], split[1]
if int64(featureTried) == feature && thresholdTried == threshold {
return false
}
}
return true
}
// Helper function to convert base.FixedDataGrid into required format. Called in Fit, Predict
func convertInstancesToProblemVec(X base.FixedDataGrid) [][]float64 {
// Allocate problem array
_, rows := X.Size()
problemVec := make([][]float64, rows)
// Retrieve numeric non-class Attributes
numericAttrs := base.NonClassFloatAttributes(X)
numericAttrSpecs := base.ResolveAttributes(X, numericAttrs)
// Convert each row
X.MapOverRows(numericAttrSpecs, func(row [][]byte, rowNo int) (bool, error) {
// Allocate a new row
probRow := make([]float64, len(numericAttrSpecs))
// Read out the row
for i, _ := range numericAttrSpecs {
probRow[i] = base.UnpackBytesToFloat(row[i])
}
// Add the row
problemVec[rowNo] = probRow
return true, nil
})
return problemVec
}

24
trees/sorter.go Normal file
View File

@ -0,0 +1,24 @@
package trees
import (
"sort"
)
type Slice struct {
sort.Float64Slice
Idx []int
}
func (s Slice) Swap(i, j int) {
s.Float64Slice.Swap(i, j)
s.Idx[i], s.Idx[j] = s.Idx[j], s.Idx[i]
}
func NewSlice(n []float64) *Slice {
s := &Slice{Float64Slice: sort.Float64Slice(n), Idx: make([]int, len(n))}
for i := range s.Idx {
s.Idx[i] = i
}
return s
}

BIN
trees/tmp

Binary file not shown.

View File

@ -11,6 +11,14 @@
present, so discretise beforehand (see
filters)
CART (Classification and Regression Trees):
Builds a binary decision tree using the CART algorithm
using a greedy approach to find the best split at each node.
Can be used for regression and classficiation.
Attributes have to be FloatAttributes even for classification.
Hence, convert to Integer Labels before hand for Classficiation.
RandomTree:
Builds a decision tree using the ID3 algorithm
by picking the Attribute amongst those