package dt;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.util.FastMath;
class Instance {
/** DO NOT CHANGE this class */
static int FTSVALUERANGE = 4; // fts can only be 0,1,2,3
int label; // label=1 ==> +; label=0 ==> -; label=-1 ==> cannot decide
int[] fts; // mapping a t c g TO 0,1,2,3. For example fts[3]=2 means 3rd
// feature is 'c'
int uniqueId;// every instance will have an uniqe Id;
public Instance(String line, int id) {
this.uniqueId = id;
String[] temp = line.split(" ");
// set this.label
if (temp[1].equals("+")) {
label = 1;
} else {
label = 0;
}
// set this.fts
char[] charfts = temp[0].toCharArray();
this.fts = new int[charfts.length];
for (int i = 0; i < charfts.length; i++) {
if (charfts[i] == 'a') {
fts[i] = 0;
} else if (charfts[i] == 't') {
fts[i] = 1;
} else if (charfts[i] == 'c') {
fts[i] = 2;
} else {
fts[i] = 3;
}
}
}
}
class Node {
Node parent;
Node children[];
/**
* The test feature for internal node
*/
int testFts;
int numOfFts;
List instances;
int predictedLabel = -1;
Node(Node parent, List instances) {
this.parent = parent;
children = new Node[Instance.FTSVALUERANGE];
this.instances = instances;
numOfFts = instances.get(0).fts.length;
testFts = -1;
int count[] = { 0, 0 };
for (Instance t : this.instances)
count[t.label]++;
predictedLabel = count[0] > count[1] ? 0 : 1;
}
public int classify(Instance t) {
// System.out.println("test\t" + testFts);
if (testFts == -1) {
return predictedLabel;
} else {
if (children[t.fts[testFts]] != null) {
return children[t.fts[testFts]].classify(t);
} else {
return -1;// cannot decide; return parent label
}
}
}
/**
* Exports the decision tree rooted from this node to a TreeML file.
*
* @param filename
* The name of the target file
*/
public void writeTreeXML(String filename) {
try {
PrintWriter writer = new PrintWriter(new FileWriter(filename));
writer.println("");
writer.println("");
writer.println("");
writer.println(" ");
writer.println(" ");
writer.println(" ");
writeTreeML(writer);
writer.println(" ");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeTreeML(PrintWriter writer) {
if (testFts != -1)
writer.println("");
else
writer.println("");
writer.print(" ");
if (testFts != -1) {
for (int i = 0; i < Instance.FTSVALUERANGE; i++) {
if (children[i] != null)
children[i].writeTreeML(writer);
}
writer.println(" ");
} else {
writer.println(" ");
writer.println("");
}
}
}
public class ID3 {
Node root;
/**
* @author Congle Zhang
*/
public abstract static class ImpurityFunction {
public abstract double calc(int a, int b);
}
/**
* Impurity function using entropy.
*/
public static ImpurityFunction impurity_entropy = new ImpurityFunction() {
public double calc(int a, int b) {
double pa = a / ((double) a + (double) b);
double pb = b / ((double) a + (double) b);
double res = 0;
if (a > 0)
res += -pa * FastMath.log(pa);
if (b > 0)
res += -pb * FastMath.log(pb);
return res / FastMath.log(2);
}
};
/**
* Impurity function using misclassification.
*/
public static ImpurityFunction impurity_misclassification = new ImpurityFunction() {
public double calc(int a, int b) {
if (a > b) {
return b / (double) (a + b);
}
return a / (double) (a + b);
}
};
/**
* Generates the decision tree with the given impurity function and
* chi-square test.
*
* @param tuples
* The training data.
* @param f
* The impurity function.
* @return The trained decision tree.
*
*/
public static Node generate(List instances, ImpurityFunction f) {
Node root = new Node(null, instances);
expand(root, f, ID3.chi_square_100, 0);// when use ID3.chi_square_100,
// there is no prunning,
return root;
}
public static Node generate(List instances, ImpurityFunction f,
ChiSquareTest cst) {
Node root = new Node(null, instances);
expand(root, f, cst, 0);
return root;
}
static void expand(Node node, ImpurityFunction impurityFunction,
ChiSquareTest cst, int depth) {
double maxGain = -100000;
int maxGainDecision = -1;
int num = node.instances.size();
int ftsNum = node.instances.get(0).fts.length;
int mcount[][] = new int[Instance.FTSVALUERANGE][2];
int parentPos = 0, parentNeg = 0;
for (int i = 0; i < node.instances.size(); i++) {
if (node.instances.get(i).label == 1) {
parentPos++;
} else {
parentNeg++;
}
}
/* Iterate over all attributes, find the best attribute */
for (int s = 0; s < node.numOfFts; ++s) {
int count[][] = new int[Instance.FTSVALUERANGE][2];
for (Instance t : node.instances) {
if (t.label == 1)
count[t.fts[s]][1]++;
else
count[t.fts[s]][0]++;
}
double gain = impurityFunction.calc(parentPos, parentNeg);
for (int i = 0; i < Instance.FTSVALUERANGE; i++) {
gain -= 1.0 * (count[i][0] + count[i][1])
/ (parentPos + parentNeg)
* impurityFunction.calc(count[i][0], count[i][1]);
// error += FastMath.min(count[i][0], count[i][1]);
}
if (gain > maxGain) {
maxGain = gain;
maxGainDecision = s;
for (int i = 0; i < Instance.FTSVALUERANGE; i++) {
mcount[i][0] = count[i][0];
mcount[i][1] = count[i][1];
}
}
}
/** If maxGain ==0 then the node is pure, stop growing the tree */
if (maxGain > 1e-10 && cst.test(mcount)) {
node.testFts = maxGainDecision;
ArrayList> ts = new ArrayList>();
for (int i = 0; i < Instance.FTSVALUERANGE; ++i) {
ts.add(new ArrayList());
}
for (Instance t : node.instances)
ts.get(t.fts[maxGainDecision]).add(t);
/* Grow the tree recursively */
for (int i = 0; i < Instance.FTSVALUERANGE; i++) {
if (maxGainDecision == 16 && i == 2) {
int x = 0;
}
if (ts.get(i).size() > 0) {
node.children[i] = new Node(node, ts.get(i));
expand(node.children[i], impurityFunction, cst, depth + 1);
}
}
}
}
public void learn(ArrayList instances, ImpurityFunction f,
ChiSquareTest cts) {
this.root = generate(instances, f, cts);
}
public void learn(ArrayList instances, ImpurityFunction f) {
this.root = generate(instances, f);
}
public void learn(List instances) {
this.root = generate(instances, ID3.impurity_entropy);
}
public static class ChiSquareTest {
double threshold;
/**
* Creates a chi-square test with the given threshold.
*
* @param threshold
* The value of the threshold.
*/
ChiSquareTest(double threshold) {
this.threshold = threshold;
}
/**
* Calculate the chi-square statistic.
*
* @param count
* The splitting result.
*
* @return true if the chi-square statistic is greater than the
* threshold.
*/
public boolean test_old(int[][] count) {
double chi_square = 0;
double num = count[0][0] + count[0][1] + count[1][0] + count[1][1];
int j = 0;
for (int i = 0; i < 2; ++i) {
double ne = (count[j][0] + count[j][1]) / num
* (count[0][i] + count[1][i]);
double nl = count[j][i];
chi_square += (nl - ne) * (nl - ne) / ne;
}
return chi_square > threshold + 1e-8;
}
public boolean test(int[][] count) {
double chi_square = 0;
int m_x_l = count.length;
int m_y_l = count[0].length;
double m_x[] = new double[m_x_l];// margin for x
double m_y[] = new double[m_y_l];// margin for y
double m = 0;
for (int i = 0; i < m_x_l; i++) {
for (int j = 0; j < m_y_l; j++) {
m_x[i] += count[i][j];
m += count[i][j];
}
}
for (int j = 0; j < m_y_l; j++) {
for (int i = 0; i < m_x_l; i++) {
m_y[j] += count[i][j];
}
}
for (int i = 0; i < m_x_l; i++) {
for (int j = 0; j < m_y_l; j++) {
double e_ij = 1.0 * m_x[i] * m_y[j] / m;
double o_ij = count[i][j];
if (e_ij > 0) {
chi_square += (e_ij - o_ij) * (e_ij - o_ij) / e_ij;
}
}
}
return chi_square > threshold + 1e-8;
}
}
/**
* Chi-square test with threshold .001.
*/
public static ChiSquareTest chi_square_001 = new ChiSquareTest(16.27);
/**
* Chi-square test with threshold .01.
*/
public static ChiSquareTest chi_square_01 = new ChiSquareTest(11.34);
/**
* Chi-square test with threshold .05.
*/
public static ChiSquareTest chi_square_05 = new ChiSquareTest(7.82);
/**
* Chi-square test with threshold 1.
*/
public static ChiSquareTest chi_square_100 = new ChiSquareTest(0);
public List classify(List testInstances) {
List predictions = new ArrayList();
for (Instance t : testInstances) {
// System.out.println("instance" + t.uniqueId);
int predictedCategory = root.classify(t);
predictions.add(predictedCategory);
}
return predictions;
}
public static void load(String trainfile, String testfile,
List trainInstances, List testInstances) {
int UNIQEID = 0;
try {
BufferedReader br = new BufferedReader(new FileReader(trainfile));
String line;
while ((line = br.readLine()) != null) {
Instance ins = new Instance(line, UNIQEID++);
trainInstances.add(ins);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
BufferedReader br = new BufferedReader(new FileReader(testfile));
String line;
while ((line = br.readLine()) != null) {
Instance ins = new Instance(line, UNIQEID++);
testInstances.add(ins);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static double computeAccuracy(List predictions,
List testInstances) {
if (predictions.size() != testInstances.size()) {
return 0;
} else {
int right = 0, wrong = 0;
for (int i = 0; i < predictions.size(); i++) {
if (predictions.get(i) == null) {
wrong++;
} else if (predictions.get(i) == testInstances.get(i).label) {
right++;
} else {
wrong++;
}
}
return right * 1.0 / (right + wrong);
}
}
/**Usage:
* javac ID3
* java ID3*/
public static void main(String[] args) {
ArrayList trainInstances = new ArrayList();
ArrayList testInstances = new ArrayList();
load("training.txt", "test.txt", trainInstances,
testInstances);
{
ID3 id3 = new ID3();
id3.learn(trainInstances);//by default ID3: no pruning; information gain;
id3.root.writeTreeXML("tree_fulltree.xml");
List trainpredictions = id3.classify(trainInstances);
System.out.println("ID3 with full tree on training\t"
+ id3.computeAccuracy(trainpredictions, trainInstances));
List predictions = id3.classify(testInstances);
System.out.println("ID3 with full tree on test\t"
+ id3.computeAccuracy(predictions, testInstances));
}
}
}
Ads help maintain this website.