/***********************************************************************
This file is part of KEEL-software, the Data Mining tool for regression,
classification, clustering, pattern mining and so on.
Copyright (C) 2004-2010
F. Herrera (herrera@decsai.ugr.es)
L. S�nchez (luciano@uniovi.es)
J. Alcal�-Fdez (jalcala@decsai.ugr.es)
S. Garc�a (sglopez@ujaen.es)
A. Fern�ndez (alberto.fernandez@ujaen.es)
J. Luengo (julianlm@decsai.ugr.es)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/
**********************************************************************/
package keel.Algorithms.Associative_Classification.ClassifierCBA;
import java.util.*;
import org.core.*;
/**
* This class contains the representation of a Rule Set.
*
* @author Written by Jesus Alcala (University of Granada) 09/02/2010
* @version 1.0
* @since JDK1.5
*/
public class RuleBase {
ArrayList<Rule> ruleBase;
ArrayList<Rule> U;
ArrayList<Rule> Q;
ArrayList<Selected> C;
ArrayList<Structure> A;
DataBase dataBase;
myDataset train;
int n_variables, defaultClass;
double fitness;
/**
* <p>
* Default Constructor
* </p>
*/
public RuleBase() {
}
/**
* <p>
* Parameters Constructor
* </p>
* @param dataBase Set of training data which is necessary to generate a rule
* @param train Training data set with information to construct the rule base (mainly, the training examples)
*/
public RuleBase(DataBase dataBase, myDataset train) {
this.ruleBase = new ArrayList<Rule> ();
this.dataBase = dataBase;
this.train = train;
this.n_variables = dataBase.numVariables();
this.fitness = 0.0;
this.defaultClass = -1;
}
/**
* <p>
* It adds a rule to the rule base
* </p>
* @param rule Rule to be added
*/
public void add(Rule rule) {
this.ruleBase.add(rule);
}
/**
* <p>
* It adds a rule to the rule base from an itemset and a time
* </p>
* @param itemset Itemset to be translated to an array to insert it in the rule base
* @param time Position in time when the rule has been added
*/
public void add(Itemset itemset, long time) {
int i;
Item item;
int[] antecedent = new int[n_variables];
for (i=0; i < n_variables; i++) antecedent[i] = -1; // Don't care
for (i=0; i < itemset.size(); i++) {
item = itemset.get(i);
antecedent[item.getVariable()] = item.getValue();
}
Rule r = new Rule(this.dataBase);
r.asignaAntecedente(antecedent);
r.setConsequent(itemset.getClas());
r.setConfidence(itemset.getSupportClass() / itemset.getSupport());
r.setSupport(itemset.getSupportClass());
r.setTime(time);
this.ruleBase.add(r);
}
/**
* <p>
* Function to get a rule from the rule base
* </p>
* @param pos Position in the rule base where the desired rule is stored
* @return Rule The desired rule
*/
public Rule get(int pos) {
return (this.ruleBase.get(pos));
}
/**
* <p>
* It sets the default class for the rule base
* </p>
* @param defaultClass int Default class to set
*/
public void setDefaultClass (int defaultClass) {
this.defaultClass = defaultClass;
}
/**
* <p>
* It returns the number of rules in the rule base
* </p>
* @return int Rule base's size
*/
public int size() {
return (this.ruleBase.size());
}
/**
* <p>
* Function to sort the rule base
* </p>
*/
public void sort () {
Collections.sort(this.ruleBase);
}
/**
* <p>
* It removes the rule stored in the given position
* </p>
* @param pos Position where the rule we want to remove is
* @return Rule Removed rule
*/
public Rule remove(int pos) {
return (this.ruleBase.remove(pos));
}
/**
* <p>
* Function to evaluate the whole rule base by using the training dataset
* </p>
* @return double Fitness of the rule base
*/
public double evaluate() {
int nHits, Prediction;
nHits = 0;
for (int j = 0; j < train.size(); j++) {
Prediction = this.FRM_WR(train.getExample(j));
if (train.getOutputAsInteger(j) == Prediction) nHits++;
}
this.fitness = (100.0 * nHits) / (1.0 * this.train.size());
return (this.fitness);
}
/**
* <p>
* It returns the class which better fits to the given example
* </p>
* @param example Example to be classified
* @return int Output class
*/
public int FRM(int[] example) {
return FRM_WR(example);
}
private int FRM_WR(int[] example) {
Rule r;
for (int i = 0; i < this.ruleBase.size(); i++) {
r = this.ruleBase.get(i);
if (r.matching(example) > 0.0) return (r.getClas());
}
return this.defaultClass;
}
/**
* <p>
* It returns the accuracy of the rule base
* </p>
* @return double Accuracy of the rule base
*/
public double getAccuracy() {
return (this.fitness);
}
/**
* Classifier Builder (Method 2)
*/
public void CBACBM2() {
this.U = new ArrayList<Rule> ();
this.Q = new ArrayList<Rule> ();
this.A = new ArrayList<Structure> ();
this.sort();
this.stage1();
this.stage2();
this.stage3();
}
private boolean isNew (ArrayList<Rule> rb, Rule rule) {
int i;
Rule r;
for (i=0; i < rb.size(); i++) {
r = rb.get(i);
if (rule.isEqual(r)) return (false);
}
return (true);
}
private void stage1() {
int i, j, cRule, wRule, y;
int [] example;
Rule rule;
this.U.clear();
this.Q.clear();
this.A.clear();
for (i=0; i < this.train.size(); i++) {
example = this.train.getExample(i);
y = train.getOutputAsInteger(i);
cRule = -1;
wRule = -1;
for (j=0; j < this.size() && (cRule < 0 || wRule < 0); j++) {
rule = this.ruleBase.get(j);
if (rule.matching(example) > 0.0) {
if ((cRule < 0) && (y == rule.getClas())) cRule = j;
if ((wRule < 0) && (y != rule.getClas())) wRule = j;
}
}
if (cRule > -1) {
rule = this.ruleBase.get(cRule);
if (this.isNew (this.U, rule)) this.U.add(rule);
rule.incrCovered(y);
if ((cRule < wRule) || (wRule < 0)) {
rule.onMark();
if (this.isNew (this.Q, rule)) this.Q.add(rule);
}
else {
Structure str = new Structure (i, y, cRule, wRule);
this.A.add(str);
}
}
else if (wRule > -1) {
Structure str = new Structure (i, y, cRule, wRule);
this.A.add(str);
}
}
}
private void stage2() {
int i, j, poscRule, poswRule;
Structure str;
Rule cRule, wRule, rule;
for (i=0; i < this.A.size(); i++) {
str = this.A.get(i);
poscRule = str.getcRule();
poswRule = str.getwRule();
wRule = this.ruleBase.get(poswRule);
if (wRule.isMark()) {
if (poscRule > -1) this.ruleBase.get(poscRule).decrCovered(str.gety());
wRule.incrCovered(str.gety());
}
else {
for (j=0; j < this.U.size(); j++) {
rule = this.U.get(j);
if (rule.matching(this.train.getExample(str.getdID())) > 0.0) {
if (rule.getClas() != str.gety()) {
if (poscRule > -1) {
cRule = this.ruleBase.get(poscRule);
if (rule.isPrecedence(cRule)) {
rule.addReplace(new Replace(poscRule,str.getdID(),str.gety()));
rule.incrCovered(str.gety());
if (this.isNew (this.Q, rule)) this.Q.add(rule);
}
}
else {
rule.addReplace(new Replace(poscRule,str.getdID(),str.gety()));
rule.incrCovered(str.gety());
if (this.isNew (this.Q, rule)) this.Q.add(rule);
}
}
}
}
}
}
}
private void stage3() {
int i, j, ruleErrors, totalErrors, defaultErrors, errorsOfRule, defaultClass, posLowest, lowestTotalErrors;
int [] compClassDistr;
int [] exampleCovered;
int [] example;
Rule rule;
Replace rep;
Selected sel;
this.C = new ArrayList<Selected> ();
compClassDistr = new int[this.train.getnClasses()];
for (i=0; i < this.train.getnClasses(); i++) compClassDistr[i] = this.train.numberInstances(i);
ruleErrors = 0;
exampleCovered = new int[this.train.size()];
for (i=0; i < this.train.size(); i++) exampleCovered[i] = 0;
Collections.sort(this.Q);
for (i=0; i < this.Q.size(); i++) {
rule = this.Q.get(i);
if (rule.getclassCasesCovered(rule.getClas()) > 0) {
for (j=0; j < rule.getnReplace(); j++) {
rep = rule.getReplace(j);
if (exampleCovered[rep.getdID()] > 0) rule.decrCovered(rep.gety());
else {
if (rep.getcRule() > -1) {
this.ruleBase.get(rep.getcRule()).decrCovered(rep.gety());
}
}
}
errorsOfRule = 0;
for (j = 0; j < this.train.size(); j++) {
if (exampleCovered[j] < 1) {
example = this.train.getExample(j);
if (rule.matching(example) > 0.0) {
exampleCovered[j] = 1;
compClassDistr[train.getOutputAsInteger(j)]--;
if (rule.getClas() != train.getOutputAsInteger(j)) errorsOfRule++;
}
}
}
ruleErrors += errorsOfRule;
defaultClass = 0;
for (j=1; j < this.train.getnClasses(); j++) {
if (compClassDistr[defaultClass] < compClassDistr[j]) defaultClass = j;
}
defaultErrors = 0;
for (j=0; j < this.train.getnClasses(); j++) {
if (j != defaultClass) defaultErrors += compClassDistr[j];
}
totalErrors = ruleErrors + defaultErrors;
this.C.add (new Selected (rule, defaultClass, totalErrors));
}
}
if (this.C.size() > 0) {
lowestTotalErrors = this.C.get(0).getTotalErrors();
posLowest = 0;
for (i = 1; i < this.C.size(); i++) {
sel = this.C.get(i);
if (sel.getTotalErrors() < lowestTotalErrors) {
lowestTotalErrors = sel.getTotalErrors();
posLowest = i;
}
}
while (this.C.size() > (posLowest+1)) this.C.remove(posLowest+1);
}
}
/**
* <p>
* Function to get stored classifier
* </p>
* @return RuleBase The whole classifier
*/
public RuleBase getClassifier() {
int i, defaultClass;
RuleBase rb = new RuleBase(this.dataBase, this.train);
Selected sel;
if (this.C.size() > 0) {
for (i=0; i < this.C.size(); i++) {
sel = this.C.get(i);
rb.add (sel.getRule().clone());
}
sel = this.C.get(this.C.size() - 1);
rb.setDefaultClass (sel.getDefaultClass());
}
else {
defaultClass = 0;
for (i=1; i < this.train.getnClasses(); i++) {
if (this.train.numberInstances(i) > this.train.numberInstances(defaultClass)) defaultClass = i;
}
rb.setDefaultClass (defaultClass);
}
return (rb);
}
/**
* <p>
* It prints the whole rulebase
* </p>
* @return String The whole rulebase
*/
public String printString() {
int i, j, ant;
String [] names = train.names();
String [] clases = train.clases();
String stringOut = new String("");
ant = 0;
for (i = 0; i < this.ruleBase.size(); i++) {
Rule r = this.ruleBase.get(i);
stringOut += (i+1)+": ";
for (j = 0; j < n_variables && r.antecedent[j] < 0; j++);
if (j < n_variables && r.antecedent[j] >= 0) {
stringOut += names[j]+" IS " + r.dataBase.print(j,r.antecedent[j]);
ant++;
}
for (j++; j < n_variables-1; j++) {
if (r.antecedent[j] >=0) {
stringOut += " AND " + names[j]+" IS " + r.dataBase.print(j,r.antecedent[j]);
ant++;
}
}
if (j < n_variables && r.antecedent[j] >= 0) {
stringOut += " AND " + names[j]+" IS " + r.dataBase.print(j,r.antecedent[j]) + ": " + clases[r.clas] + "\n";
ant++;
}
else stringOut += ": " + clases[r.clas] + "\n";
}
stringOut += "Default: " + clases[this.defaultClass] + "\n";
stringOut += "\n\n";
stringOut += "@supp and conf:\n\n";
for (i = 0; i < this.ruleBase.size(); i++) {
Rule rule = this.ruleBase.get(i);
stringOut += (i+1)+": ";
stringOut += "supp: " + rule.getSupport() + " AND conf: " + rule.getConfidence() + "\n";
}
if (this.ruleBase.size() > 0) stringOut = "@Number of rules: " + (this.ruleBase.size() + 1) + " Number of Antecedents by rule: " + ant * 1.0 / this.ruleBase.size() + "\n\n" + stringOut;
else stringOut = "@Number of rules: 1 Number of Antecedents by rule: 0\n\n" + stringOut;
return (stringOut);
}
/**
* <p>
* It stores the rule base in a given file
* </p>
* @param filename String Name for the rulebase file
*/
public void saveFile(String filename) {
String stringOut = new String("");
stringOut = printString();
Fichero.escribeFichero(filename, stringOut);
}
}