public class NaiveBayes extends DistributionClassifier
implements OptionHandler, WeightedInstancesHandler {
/** Los estimadores de atributo. */
protected Estimator [][] m_Distributions;
/** El estimados de la clase. */
protected Estimator m_ClassDistribution;
/**
* Usar el estimador del kernel en vez de la Normal
*/
protected boolean m_UseKernelEstimator;
/** La cantidad de clases (o 1 si es numerica) */
protected int m_NumClasses;
/** El encabezado para entregar el modelo
*/
protected Instances m_Instances;
/*** El parametro de precision para los atributos numericos */
protected static final double DEFAULT_NUM_PRECISION = 0.01;
/**
* Genera el clasificador
*
* @param instances Instancias producidas de datos de entrada
* @exception Exception Si el clasificador no es generado exitosamente
*/
public void buildClassifier(Instances instances) throws Exception {
if (instances.checkForStringAttributes()) {
throw new Exception("Atributos numericos solamente");
}
if (instances.classAttribute().isNumeric()) {
throw new Exception("Naive Bayes: Clase es numerica!");
}
m_NumClasses = instances.numClasses();
if (m_NumClasses < 0) {
throw new Exception ("Dataset no tiene atributos de clase");
}
// Copiar las instancias
m_Instances = new Instances(instances);
// Reserva espacio para las distribuciones
m_Distributions = new Estimator[m_Instances.numAttributes() - 1]
[m_Instances.numClasses()];
m_ClassDistribution = new DiscreteEstimator(m_Instances.numClasses(),
true);
int attIndex = 0;
Enumeration enum = m_Instances.enumerateAttributes();
while (enum.hasMoreElements()) {
Attribute attribute = (Attribute) enum.nextElement();
// Si el atributo es numerico, determina la distribucion
// Precision numerica para diferencia entre atributos adyacentes
double numPrecision = DEFAULT_NUM_PRECISION;
if (attribute.type() == Attribute.NUMERIC) {
m_Instances.sort(attribute);
if ((m_Instances.numInstances() > 0)
&& !m_Instances.instance(0).isMissing(attribute)) {
double lastVal = m_Instances.instance(0).value(attribute);
double currentVal, deltaSum = 0;
int distinct = 0;
for (int i = 1; i < m_Instances.numInstances(); i++) {
Instance currentInst = m_Instances.instance(i);
if (currentInst.isMissing(attribute)) {
break;
}
currentVal = currentInst.value(attribute);
if (currentVal != lastVal) {
deltaSum += currentVal - lastVal;
lastVal = currentVal;
distinct++;
}
}
if (distinct > 0) {
numPrecision = deltaSum / distinct;
}
}
}
for (int j = 0; j < m_Instances.numClasses(); j++) {
switch (attribute.type()) {
case Attribute.NUMERIC:
if (m_UseKernelEstimator) {
m_Distributions[attIndex][j] =
new KernelEstimator(numPrecision);
} else {
m_Distributions[attIndex][j] =
new NormalEstimator(numPrecision);
}
break;
case Attribute.NOMINAL:
m_Distributions[attIndex][j] =
new DiscreteEstimator(attribute.numValues(), true);
break;
default:
throw new Exception("Tipo desconocido");
}
}
attIndex++;
}
// Calcula conteo
double sum;
Enumeration enumInsts = m_Instances.enumerateInstances();
while (enumInsts.hasMoreElements()) {
Instance instance =
(Instance) enumInsts.nextElement();
if (!instance.classIsMissing()) {
Enumeration enumAtts = m_Instances.enumerateAttributes();
attIndex = 0;
while (enumAtts.hasMoreElements()) {
Attribute attribute = (Attribute) enumAtts.nextElement();
if (!instance.isMissing(attribute)) {
m_Distributions[attIndex][(int)instance.classValue()].
addValue(instance.value(attribute), instance.weight());
}
attIndex++;
}
m_ClassDistribution.addValue(instance.classValue(),
instance.weight());
}
}
// Reserva espacio
m_Instances = new Instances(m_Instances, 0);
}
/**
* Calcula las probabilidades de la instancia
*
* @param instance Instancia a ser clasificada
* @return predicted Distribucion probabilistica de la clase
* @exception Exception Por si hay problemas
*/
public double [] distributionForInstance(Instance instance)
throws Exception {
double [] probs = new double[m_NumClasses];
for (int j = 0; j < m_NumClasses; j++) {
probs[j] = m_ClassDistribution.getProbability(j);
}
Enumeration enumAtts = instance.enumerateAttributes();
int attIndex = 0;
while (enumAtts.hasMoreElements()) {
Attribute attribute = (Attribute) enumAtts.nextElement();
if (!instance.isMissing(attribute)) {
double temp, max = 0;
for (int j = 0; j < m_NumClasses; j++) {
temp = Math.max(1e-75, m_Distributions[attIndex][j].
getProbability(instance.value(attribute)));
probs[j] *= temp;
if (probs[j] > max) {
max = probs[j];
}
if (Double.isNaN(probs[j])) {
throw new Exception("NaN retornado del estimador del atributo "
+ attribute.name() + ":\n"
+ m_Distributions[attIndex][j].toString());
}
}
if ((max > 0) && (max < 1e-75)) { // Peligro de subflujo de probabilidades
for (int j = 0; j < m_NumClasses; j++) {
probs[j] *= 1e75;
}
}
}
attIndex++;
}
// Mostrar Probabilidades
Utils.normalize(probs);
return probs;
}
/**
* Retorna las opciones actuales
*
* @return Arreglo de opciones
*/
public String [] getOptions() {
String [] options = new String [1];
int current = 0;
if (m_UseKernelEstimator) {
options[current++] = "-K";
}
while (current < options.length) {
options[current++] = "";
}
return options;
}
/**
* Si se usa estimador del kernel
*
* @return Valor de m_UseKernelEstimatory.
*/
public boolean getUseKernelEstimator() {
return m_UseKernelEstimator;
}
/**
* Retorna enumeracion de opciones disponibles
*
* @return Retorna enumeracion de opciones disponibles
*/
public Enumeration listOptions() {
Vector newVector = new Vector(1);
newVector.addElement(
new Option("\tUsar estimador de la densidad del kernel en vez de la\n"
+"\tdistribucion normal para atributos numericos ",
"K", 0,"-K"));
return newVector.elements();
}
/**
* Main para probar la clase
*
* @param argv Las opciones
*/
public static void main(String [] argv) {
try {
System.out.println(Evaluation.evaluateModel(new NaiveBayes(), argv));
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
/**
* Parsea opciones dadas<p>
*
* -K <br>
* Usar estimador de kernel<p>
*
* @param options Arreglo de opciones
* @exception Exception Si una opcion no es aceptada
*/
public void setOptions(String[] options) throws Exception {
m_UseKernelEstimator = Utils.getFlag('K', options);
Utils.checkForRemainingOptions(options);
}
/**
* Fija si se usa estimador del kernel
*
* @param v Valor a asignar a m_UseKernelEstimatory.
*/
public void setUseKernelEstimator(boolean v) {
m_UseKernelEstimator = v;
}
/**
* Retorna descripcion del clasificador
*
* @return Una descripcion del clasificador
*/
public String toString() {
StringBuffer text = new StringBuffer();
text.append("Clasificador Naive Bayes");
if (m_Instances == null) {
text.append(": No hay modelo aún");
} else {
try {
for (int i = 0; i < m_Distributions[0].length; i++) {
text.append("\n\nClase " + m_Instances.classAttribute().value(i) +
": Probabilidad a priori = " + Utils.
doubleToString(m_ClassDistribution.getProbability(i),
4, 2) + "\n\n");
Enumeration enumAtts = m_Instances.enumerateAttributes();
int attIndex = 0;
while (enumAtts.hasMoreElements()) {
Attribute attribute = (Attribute) enumAtts.nextElement();
text.append(attribute.name() + ": "
+ m_Distributions[attIndex][i]);
attIndex++;
}
}
} catch (Exception ex) {
text.append(ex.getMessage());
}
}
return text.toString();
}
}
Copyright Nelson Flores 2001.
Departamento de Ciencias de la Computacion, Universidad de Chile.