Beruflich Dokumente
Kultur Dokumente
FACULTAD DE INGENIERIA
UNIVERSIDAD DE BUENOS AIRES
FEBRERO 2002
Algoritmos TDIDT aplicados a la Minería de Datos Inteligente
RESUMEN
La Minería de Datos (Data Mining) es la búsqueda de patrones interesantes y de regularidades
importantes en grandes bases de datos. El Aprendizaje Automático es el campo de la Ingeniería
Informática en el que se estudian y desarrollan algoritmos que implementan los distintos modelos de
aprendizaje y su aplicación a la resolución de problemas prácticos. La minería de datos inteligente utiliza
métodos de aprendizaje automático para descubrir y enumerar patrones presentes en los datos.
Uno de los métodos más conocidos para describir los atributos de una entidad de una base de datos es
utilizar un árbol de decisión o de clasificación, que puede transformarse sin inconveniente a un conjunto
de reglas de decisión.
En este contexto, el propósito de este proyecto es estudiar de que manera la familia TDIDT, que aborda el
problema de inducir árboles de decisión, puede utilizarse para descubrir automáticamente reglas de
negocio a partir de la información disponible en una base de datos. Se trabajó en particular con los
métodos ID3 y C4.5, miembros de dicha familia. El trabajo contempla el diseño, especificación e
implementación de un ambiente de minería de datos que integra ambos algoritmos. Además, se desarrolló
un método de evaluación de los resultados para determinar la calidad de las reglas obtenidas.
Palabras clave: minería de datos, aprendizaje automático, árboles de decisión, reglas de decisión,
TDIDT, ID3, C4.5
ABSTRACT
Data mining is the search of interesting patterns and relevant regularities in large data bases. Machine
Learning is the Informatic Engineering’s field devoted to the analysis and development of algorithms
implementing the different learning models and their application to the solution of practical problems.
Intelligent data mining uses machine learning methods to find and list the patterns present in the data.
One of the best known methods to describe the attributes of an entity of a data base is the use of a
decision or classification tree, which can easily be turned into a set of decision rules.
Within this context, the purpose of the present project is to analyze the way in which the TDIDT family,
which studies the problem of inducing decision trees, can be used to discover automatically business rules
from the information available in a data base.
The work was focused in the ID3 and C4.5 methods, which belong to the family above mentioned. The
work includes the design, specification and implementation of a data mining system that combines both
algorithms. Furthermore, to determine the quality of the rules obtained, an evaluation method of the
corresponding results was developed.
Keywords: data mining, machine learning, decision trees, decision rules, TDIDT, ID3, C4.5
INDICE
CAPÍTULO 1: INTRODUCCIÓN 1
4.3. ID3 55
4.3.1. Descripción del ID3 56
4.3.1.1. Algoritmo ID3 57
4.3.1.2. Poda de los árboles de decisión 57
4.3.1.3. Pasaje a reglas de decisión 57
4.3.1.4. Atributos desconocidos 58
4.4. C4.5 64
4.4.1. Algoritmo C4.5 64
4.4.2. Características particulares del C4.5 65
4.4.2.1. Pruebas utilizadas 65
4.4.2.2. Pruebas sobre atributos continuos 65
4.4.2.2. Atributos desconocidos 66
4.4.3. Poda de los Árboles de Decisión 68
4.4.3.1. ¿Cuándo debemos simplificar? 69
4.4.3.2. Poda en Base a Errores 70
4.4.4. Estimación de la Proporción de Errores para los Árboles de Decisión 72
4.4.5. Construcción de un árbol de decisión utilizando el C4.5 72
4.4.6. Generalización de reglas 76
4.4.6.1. Conjuntos de Reglas 77
4.4.6.2. Orden de las clases y elección de la clase por defecto 80
4.4.6.3. Generalización de un árbol de decisión a reglas de decisión utilizando el C4.5 80
5.5. Comparación de los resultados obtenidos con el ID3 y con el C4.5 177
5.5.1. Créditos 177
5.5.2. Cardiología 180
5.5.3. Votaciones 181
5.5.4. Estudio sobre hongos 185
REFERENCIAS 341
CAPÍTULO 1
INTRODUCCIÓN
Los métodos tradicionales de Análisis de Datos incluyen el trabajo con variables estadísticas, varianza,
desviación estándar, covarianza y correlación entre los atributos; análisis de componentes (determinación
de combinaciones lineales ortogonales que maximizan una varianza determinada), análisis de factores
(determinación de grupos correlacionados de atributos), análisis de clusters (determinación de grupos de
conceptos que están cercanos según una función de distancia dada), análisis de regresión (búsqueda de los
coeficientes de una ecuación de los puntos dados como datos), análisis multivariable de la varianza, y
análisis de los discriminantes [Michalski et al., 1982]. Todos estos métodos están orientados
numéricamente. Son esencialmente cuantitativos.
En las bases de datos las entidades se caracterizan generalmente por el valor de los atributos y no por las
relaciones entre ellas, con lo cual se utilizan métodos atribucionales. Uno de los métodos más conocidos
para describir los atributos de una entidad es utilizar un árbol de decisión o de clasificación [Michalski et
al., 1998Grossman et al., 1999], cuyos nodos corresponden a los atributos, las ramas que salen de ellos
son los valores de los atributos, y las hojas son corresponden a clases individuales. La gran ventaja de los
árboles de clasificación es que se pueden transformar sin inconveniente a un conjunto de reglas de
decisión.
En este contexto, el propósito de este proyecto es estudiar de que manera la familia TDIDT [Quinlan,
1986; 1990], que aborda el problema de inducir árboles de decisión, puede utilizarse para descubrir
automáticamente reglas de negocio a partir de la información disponible en una base de datos.
En el capítulo 2 se presenta el estado actual del Aprendizaje Automático (Sección 2.1), en múltiples
aspectos, que incluyen la clasificación general de este tipo de sistemas (Sección 2.1.1) el esquema general
de un sistema de Aprendizaje Automático (Sección 2.1.2) y los distintos tipos de aprendizaje en general
(Sección 2.1.3). Luego, se presenta el estado actual de la Minería de Datos (Sección 2.2), incluyendo
descripciones de: el descubrimiento de conocimientos (Sección 2.2.1), los problemas inherentes al
aprendizaje de conceptos (Sección 2.2.2), las tareas realizadas por un sistema de Minería de Datos
(Sección 2.2.3), los principales métodos de la Minería de Datos (Sección 2.2.4) y sus componentes
(Sección 2.2.5). A partir de la presentación de estos dos grandes temas, se presentan varias aplicaciones
en las que se realiza Minería de Datos con sistemas de Aprendizaje Automático (Sección 2.3.1), entre los
cuales se encuentra la familia TDIDT (Top-Down-Induction-Trees) (Sección 2.4) en la cual centraremos
nuestra atención. Se explica la construcción de los árboles TDIDT (Sección 2.4.1), el tratamiento de los
atributos desconocidos (Sección 2.4.2), y la transformación de los árboles a reglas de decisión (Sección
2.4.3). Finalmente, se presentan varios métodos utilizados actualmente para evaluar los distintos métodos
de Aprendizaje Automático (Sección 2.5)
En el capítulo 3 se presenta el contexto de nuestro problema de interés (Sección 3.1) y las cuestiones que
los algoritmos ID3 y C4.5 pertenecientes a la familia TDIDT deben resolver (Sección 3.2).
En el capítulo 4 se presentan todos los aspectos de la solución propuesta. Para ello se describen las
características generales de la misma (Sección 4.1): el marco teórico (Sección 4.1.1), las condiciones que
deben cumplir los datos sobre los que se realiza la Minería de Datos (Sección 4.1.2) y los tipos de
resultados obtenidos (Sección 4.1.3). En la Sección 4.2 se presenta una descripción general de los
algoritmos ID3 y C4.5 que se utilizan para estudiar el problema. Se explica cómo realizar la división de
los datos de entrada (Sección 4.2.1) y la elección del criterio de división (Sección 4.2.1.1). Luego, se
presenta una descripción detallada del algoritmo ID3 (Sección 4.3.1) y de sus limitaciones (Sección
4.3.2). A modo de ejemplo, se muestra la utilización del ID3 para generar un árbol y reglas de decisión
(Sección 4.3.1.5). También se detalla el algoritmo C4.5 (Sección 4.4) y sus características particulares
(Sección 4.4.2), las cuales lo diferencian del ID3, resaltando la poda de los árboles de decisión (Sección
4.4.3) y la estimación de errores en dichos árboles (Sección 4.4.4). En la sección 4.4.5 se ejemplifican los
métodos anteriores. La generalización de las reglas de decisión realizada por el C4.5 se explica en la
sección 4.4.6. A continuación, se detalla el diseño del sistema integrador utilizado para estudiar el éxito
de la solución propuesta (Sección 4.5). Para este sistema se presenta una descripción general (Sección
4.5.1) y el diseño de las secciones del sistema para el ID3 (Sección 4.5.2.1) y para el C4.5 (Sección
4.5.2.2)
En el capítulo 5 se presentan los resultados obtenidos. Primero, se explica la manera en que debe
realizarse la interpretación de los mismos (Sección 5.1), se presentan los formatos de los árboles y las
reglas de decisión tanto para el ID3 (Sección 5.1.1) como para el C4.5 (Sección 5.1.2). Luego, se
describen los dominios de datos sobre los que se trabajó (Sección 5.2), y se analizan los resultados
obtenidos con el ID3 (Sección 5.3) y con el C4.5 (Sección 5.4). En cada caso se presenta, el árbol y las
reglas de decisión obtenidos, y la evaluación de resultados sobre el conjunto de datos de prueba. En la
sección 5.5 se comparan los resultados obtenidos con el ID3 y con el C4.5 en los distintos dominios. Y en
la sección 5.6 se realiza un análisis general de los resultados.
En el capítulo 6 se presentan las conclusiones del trabajo realizado. Primero se destacan los conceptos
más importantes a tener en cuenta a la hora de aplicar algún método como el ID3 y el C4.5 (Sección
6.1.1). Luego, se analiza la búsqueda que realizan estos dos métodos en el espacio de hipótesis (Sección
6.1.2). Se extraen conclusiones a partir de los resultados obtenidos (Sección 6.2) y se analiza la solución
propuesta (Sección 6.3). Finalmente, se plantean mejoras y temas a tener en cuenta para continuar con el
desarrollo de este tipo de algoritmos de aprendizaje aplicados a la Minería de Datos (Sección 6.4).
El Anexo A describe el sistema en forma de un resumido manual del usuario. Se describen las
características generales del sistema (Sección A.1) y las funciones de cada uno de los menúes (Sección
A.2), detallando las acciones que pueden realizarse con cada una de las opciones disponibles.
En el Anexo B se detallan los conjuntos de datos utilizados para realizar las pruebas descriptas en el
Capítulo 5. Para cada uno de los dominios, se muestran los conjuntos de datos de entrenamiento y prueba.
Los datos presentados corresponden a los siguientes dominios: Créditos (Sección B.1), Cardiología
(Sección B.2), Votaciones (Sección B.3), Estudio sobre hongos (Sección B.4), Elita: Base de Asteroides
(Sección B.5), Hipotiroidismo (Sección B.6), Identificación de vidrios (Sección B.7).
Finalmente, en el anexo D se presenta el código fuente del sistema desarrollado. En la sección D.1 se
presenta el código fuente del archivo TDIDT.cpp que es el archivo inicial del proyecto y es quien
inicializa al resto de los formularios requeridos. En la sección D.2 se presenta el código fuente de las
pantallas principales. A continuación, se detallan los códigos fuentes de los módulos de opciones
generales (Sección D:3), de los módulos de minería de datos (Sección D.4) y de los módulos de clases o
estructuras de datos (Sección D.5)
En las Referencias se detallan la bibliografía y las referencias utilizadas para realizar el trabajo.
CAPÍTULO 2:
Este capítulo presenta el estado actual del Aprendizaje Automático (Sección 2.1), en múltiples
aspectos, que incluyen la clasificación general de este tipo de sistemas (Sección 2.1.1) el
esquema general de un sistema de Aprendizaje Automático (Sección 2.1.2) y los distintos tipos
de aprendizaje en general (Sección 2.1.3). Luego, se presenta el estado actual de la Minería de
Datos (Sección 2.2), incluyendo descripciones de: el descubrimiento de conocimientos (Sección
2.2.1), los problemas inherentes al aprendizaje de conceptos (Sección 2.2.2), las tareas realizadas
por un sistema de Minería de Datos (Sección 2.2.3), los principales métodos de la Minería de
Datos (Sección 2.2.4) y sus componentes (Sección 2.2.5). A partir de la presentación de estos
dos grandes temas, se presentan varias aplicaciones en las que se realiza Minería de Datos con
sistemas de Aprendizaje Automático (Sección 2.3.1), entre los cuales se encuentra la familia
TDIDT (Top-Down-Induction-Trees) (Sección 2.4) en la cual centraremos nuestra atención. Se
explica la construcción de los árboles TDIDT (Sección 2.4.1), el tratamiento de los atributos
desconocidos (Sección 2.4.2), y la transformación de los árboles a reglas de decisión (Sección
2.4.3). Finalmente, se presentan varios métodos utilizados actualmente para evaluar los distintos
métodos de Aprendizaje Automático (Sección 2.5)
Estos dos campos han ido creciendo a lo largo de los años, y han cobrado una importancia considerable.
Hoy en día, como se almacenan grandes volúmenes de información en todas las actividades humanas, la
Minería de Datos está cobrando gran importancia, se busca obtener información valiosa a partir de los
datos guardados. La Minería de Datos utiliza métodos y estrategias de otras áreas o ciencias, entre las
cuales podemos nombrar al Aprendizaje Automático. Cuando este tipo de técnicas se utilizan para realizar
la minería, decimos que estamos ante una Minería de Datos Inteligente.
una familia de métodos de inducción conocida como la familia TDIDT (Top Down Induction Trees), y en
particular en los algoritmos ID3 y C4.5 desarrollados por Quinlan, pertenecientes a la misma.
¿Cómo sabemos si un sistema ha adquirido algún conocimiento? Siguiendo el análisis de Witten [Witten
y Frank, 2000], podemos plantearnos las siguientes preguntas: ¿qué es el aprendizaje?, y ¿qué es el
Aprendizaje Automático? Si buscamos la definición de Aprendizaje en la Enciclopedia, encontraremos
las siguientes definiciones o alguna similar: “Adquirir el conocimiento de alguna cosa por medio del
estudio, de la experiencia o al ser instruido; Concebir alguna cosa por meras apariencias o con poco
fundamento; Tomar algo en la memoria; Ser informado de; recibir instrucción” [Espasa-Calpe, 1974].
Todas estas definiciones se aplican con facilidad a los seres humanos, veamos si pueden aplicarse
también a los sistemas informáticos. En términos de Aprendizaje Automático lo primero que debemos ser
capaces de hacer es determinar si un sistema informático ha aprendido o no. En el caso de las dos
primeras definiciones esto es imposible: no existe ninguna manera de preguntarle si ha adquirido
conocimiento, ya que si le hacemos preguntas acerca de las cosas que debería haber aprendido, no
estaríamos midiendo sus nuevos conocimientos, sino su capacidad de responder preguntas. En cuanto a
las últimas dos definiciones, el hecho de guardar en memoria y recibir instrucciones son triviales para una
computadora, son actividades “vitales” para ella que realiza todo el tiempo. De esto se deduce que una
computadora sería capaz de aprender a la luz de las últimas dos definiciones.
Esta afirmación no tiene nada de novedoso para nosotros. Si un sistema de información es capaz de
aprender, entonces debe ser capaz de aplicar los conocimientos memorizados o instruidos en una nueva
situación. Esto es justamente lo que evaluamos para saber si un ser humano ha aprendido o no, evaluamos
su respuesta ante una nueva situación. Siguiendo esta línea de pensamiento, podemos afirmar entonces
que las cosas (y los seres humanos también) aprenden cuando cambian su comportamiento de manera tal
que les permite desarrollarse mejor en el futuro. A la luz de esta definición que asocia el aprendizaje a la
performance más que al conocimiento, el aprendizaje es más fácil de medir: podemos analizar si un
sistema ha aprendido al ponerlo en una situación en la que estuvo anteriormente y observando si se
desempeña mejor.
Esta definición sigue siendo engañosa. Una gran variedad de objetos modifican su comportamiento de
manera tal que los hacen desempeñarse mejor y, sin embargo, no se puede decir que hayan aprendido.
Tomemos por ejemplo, nuestras mejores pantuflas, ¿podemos decir que han aprendido la forma de
nuestro pie? Sin embargo, han cambiado su forma, su comportamiento para adaptarse mejor a nuestro pie
y ciertamente son más cómodas que cuando eran nuevas. Llamamos entrenamiento al tipo de aprendizaje
que se realiza sin pensar. Entrenamos a los animales y a las plantas, pero hablar del entrenamiento de una
pantufla sería ridículo. El aprendizaje, a diferencia del entrenamiento, requiere pensar, implica tener el
propósito, directo o indirecto, de aprender; alguien o algo que aprende debe tener la intención de hacerlo.
El aprendizaje sin pensar es meramente un entrenamiento. Los sistemas de Aprendizaje Automático
tienen la intención de construir un modelo a partir de los datos de entrada y cambian su comportamiento
de manera tal que son capaces de clasificar nuevos datos y desarrollarse mejor en antiguas situaciones. En
fin, podemos afirmar que los sistemas son capaces de aprender. Aún no sabemos, sin embargo, cómo
hacer para que los sistemas aprendan en el mismo grado que los humanos. No obstante, se han creado
algoritmos efectivos en ciertas tareas de aprendizaje, y el entendimiento teórico del aprendizaje está
comenzando a emerger [Mitchell, 1997].
Para complementar el enfoque anterior, debemos preguntarnos cómo aprenden los seres humanos y
analizar si los si los sistemas son capaces de aprender de la misma manera. Los humanos aprendemos
mediante tres mecanismos distinguibles: inducción, deducción y abducción. Podemos afirmar que los
sistemas aprenden cuando son capaces de generar nuevos conocimientos, por cualquiera de los tres
métodos anteriores [Monter, 2001]. En un sistema de Aprendizaje Automático, dicha generación de
conocimientos se realiza cuando los datos se transforman a un nivel superior que nos es más útil. Por
ejemplo, cuando los datos presentes en una base de datos se transforman en un modelo de datos que los
clasifica según sus características o atributos.
Los algoritmos de la familia TDIDT que analizaremos, justamente construyen un modelo de nivel
superior. ¿Podemos afirmar entonces que son sistemas de Aprendizaje Automático?
El tema fundamental para construir un sistema de aprendizaje automático es, según Mitchell [Mitchell,
1997], plantear el problema de aprendizaje de manera correcta. Para ello, debe contar con las tres partes
esenciales de la siguiente definición:
“Se puede afirmar que un programa computacional es capaz de aprender a partir de la experiencia E con
respecto a un grupo de tareas T y según la medida de performance P, si su performance en las tareas T,
medida según P, mejora con la experiencia E.”
ejemplos
descripción de conceptos
conocimientos previos C
Los conocimientos previos contienen la información acerca del lenguaje utilizado para describir los
ejemplos y los conceptos, es decir, son una suerte de metalenguaje. El sistema utiliza entonces, los
conocimientos previos para interpretar los ejemplos y para generar descripciones a partir de ellos.
comparten todas las aves y que las diferencias del resto de los seres vivos). A la luz de esta definición
vemos que si los límites entre conceptos no están claramente definidos, puede no ser fácil clasificar un
ejemplo en particular. Por ejemplo, cuál es el límite entre un edificio alto y un edificio bajo, entre una
mujer atractiva y una no atractiva.
Existen dos técnicas de inferencia generales que se utilizan para extraer descripciones a partir de los
conceptos: la deducción y la inducción. La deducción es la técnica que infiere información como una
consecuencia lógica de los ejemplos y conocimientos de base [Holsheimer, Siebes, 1994]. La inducción
es la técnica que infiere información generalizada de los ejemplos y conocimientos de base.
En la inducción, podemos trabajar con jerarquías de generalización, representadas por árboles o grafos
[Michalski et al, 1998]. En una jerarquía de generalización, un concepto puede describirse por los objetos
del nivel base o por cualquier objeto en un nivel superior. Analizando la Figura 2.2, podemos describir a
la Universidad de Buenos Aires con los objetos de nivel base, en cuyo caso decimos que es una
institución educativa, universitaria y pública; o podemos describirla con los objetos de nivel superior
diciendo que la Universidad de Buenos Aires es una institución educativa.
Instituciones
Educativas
Públicas Privadas
UBA UTN
En este tipo de jerarquías, podemos identificar tres nociones que relacionan los conceptos: efecto de nivel
básico (basic-level effect), tipicalidad (typicality) y dependencia contextual (contextual dependency)
[Michalski et al, 1998]. El efecto de nivel básico hace referencia al hecho de que los conceptos de nivel
base pueden ser descriptos por características fácilmente identificables por los humanos, lo cual hace que
su aprendizaje sea simple para nosotros. Mientras que los conceptos de nivel superior se definen como
grupos de conceptos de nivel básico que comparten alguna característica en común. La segunda noción,
la tipicalidad, analiza cuán típico es un concepto. Puede medirse de acuerdo a la cantidad de
características comunes que comparte con otros conceptos, y a la cantidad de características heredadas de
los superconceptos (conceptos de nivel superior). En el aprendizaje, la tipicalidad es muy importante, por
ejemplo, tratar de enseñar el concepto de pájaro con los ejemplos de un pingüino, un ganso y un avestruz,
no será muy efectivo. En cambio, utilizar una golondrina, un gorrión y una paloma será exitoso. Por
último, la dependencia contextual es importante porque los conceptos aprendidos dependen del contexto
en el que estamos. Al definir estudiantes podemos estar pensando en estudiantes universitarios,
estudiantes primarios, o estudiantes del curso de Análisis Matemático; el concepto que estamos tratando
de enseñar, dependerá del contexto en el que estamos.
En la lógica de orden cero, los ejemplos y conceptos se describen como conjunciones de constantes
booleanas que representan valores de los atributos. El poder descriptivo de este tipo de lógica es bajo, por
lo cual, el Aprendizaje Automático lo utiliza únicamente para describir conceptos muy simples. Un
ejemplo de una cláusula en lógica de orden cero es:
Para solucionar el problema del bajo poder descriptivo de la lógica de orden cero, puede utilizarse la
lógica de atributos. La idea básica detrás de la lógica de atributos es caracterizar los ejemplos y conceptos
como valores de algunos atributos predefinidos. En lugar de utilizar conjunciones de valores fijos, cada
atributo es una variable. El poder descriptivo de la lógica de atributos es mayor que el de la lógica de
orden cero, aunque en sentido matemático la expresividad es la misma. Una cláusula en lógica de
atributos podría ser “Juego_Tenis <= Pronóstico ^ Viento ^ Humedad). Los ejemplos generalmente se
presentan en una tabla donde cada fila representa un ejemplo y cada columna, un atributo. La tabla 2.1
contiene ejemplos positivos y negativos para los días en que es posible jugar al tenis.
Como lenguaje descriptivo, la lógica de atributos es mucho más práctica que la lógica de orden cero. Por
eso, es utilizada en muchos programas de Aprendizaje Automático, como los de la familia TDIDT
(Árboles inductivos de arriba hacia abajo - Top-Down Induction Trees).
La lógica de predicados de primer orden utiliza las cláusulas de Horn para representar conceptos. Estas
cláusulas simplifican las descripciones complicadas mediante el uso de predicados y variables. Son
bastante potentes, incluso permiten la expresión de conceptos recursivos. El lenguaje Prolog se basa en la
lógica de predicados de primer orden. Este tipo de lógica se utiliza en algunos programas de Aprendizaje
Automático, como el algoritmo FOIL. Un ejemplo de una cláusula de Horn sería:
Por último, la lógica de predicados de segundo orden considera a los nombres de los predicados como
variables. La expresión anterior quedaría de la forma:
Este tipo de lógica es la de mayor poder descriptivo. Sin embargo, dada su complejidad rara vez se utiliza
en los sistemas de Aprendizaje Automático.
2.1.3. Aprendizaje
• Observación pasiva
• Experimentación activa
En el aprendizaje por memorización los sistemas reciben conocimientos del medio ambiente y los
guardan sin ningún tipo de procesamiento. Su complejidad se encuentra en el almacenamiento de los
conocimientos y no en su adquisición. Lo importante en estos casos es que la información esté disponible
1
También conocido como formación de clusters.
cuando se requiera; no hay ningún tipo de inferencia ni procesamiento, por lo tanto, los conocimientos
deben ser adquiridos y almacenados en un nivel que los haga directamente utilizables.
En el caso del aprendizaje por instrucción, los conocimientos son provistos por un instructor o experto en
la materia (aprendizaje supervisado). La información provista es abstracta o de índole general, por lo
tanto, el sistema tendrá que inferir los detalles. Es decir, el sistema deberá transformar la información
provista en términos abstractos de alto nivel, a reglas que puedan ser utilizadas directamente en la tarea
del sistema.
El aprendizaje por deducción o aprendizaje guiado por la especificación destaca o especifica las
relaciones existentes entre conceptos. El sistema transforma las especificaciones recibidas como entrada
en un algoritmo que actualiza relaciones.
En el aprendizaje por analogía, el sistema, que recibe información relevante a problemas análogos a los
que está tratando de resolver, debe descubrir las analogías e inferir reglas aplicables al problema. Se trata
de generar nuevos conocimientos utilizando información preexistente.
En el aprendizaje por inducción, el sistema genera nuevos conocimientos que no están presentes en forma
implícita dentro del conocimiento disponible. El aprendizaje por inducción abarca el aprendizaje por
ejemplos y el aprendizaje por observación y descubrimiento.
En el aprendizaje por ejemplos, el sistema recibe varios ejemplos como entrada y debe generalizarlos en
un proceso inductivo para presentarlos como salida. Generalmente, en este tipo de aprendizaje existen dos
tipos de ejemplos, los positivos y los negativos. Los ejemplos positivos fuerzan la generalización,
mientras que los ejemplos negativos previenen que esta sea excesiva. Se trata de que el conocimiento
adquirido cubra todos los ejemplos positivos y ningún ejemplo negativo. A este tipo de aprendizaje
pertenece la familia TDIDT. Debe tenerse en cuenta, que los ejemplos a partir de los cuales aprende el
sistema, deben ser representativos de los conceptos que se está tratando de enseñar. Además, la
distribución de las clases en el conjunto de ejemplos de entrenamiento, a partir de los que el sistema
aprende, debe ser similar a la distribución existente en los datos sobre los cuales se aplicará el modelo
resultante.
2.1.3.3.1 Aprendizaje AQ
El aprendizaje AQ se basa en la idea de cubrir progresivamente los datos de entrenamiento a medida que
se generan reglas de decisión. Su esencia está en la búsqueda de un conjunto de reglas (conjunciones de
pares atributo-valor o predicados arbitrarios) que cubran todos los ejemplos positivos y ningún ejemplo
negativo. En lugar de dividir los ejemplos en subconjuntos, el aprendizaje AQ generaliza, paso a paso, las
descripciones de los ejemplos positivos seleccionados [Michalski et al, 1998].
El aprendizaje “divide y reinarás” particiona el conjunto de ejemplos en subconjuntos sobre los cuales se
puede trabajar con mayor facilidad. En la lógica proposicional, por ejemplo, se parte el conjunto de
acuerdo a los valores de un atributo en particular, entonces, todos los miembros de un subconjunto
tendrán un mismo valor para dicho atributo. Dentro de este tipo de aprendizaje, encontramos la familia
TDIDT (Top-Down Induction Trees), la cual se explica con mayor detalle en la Sección 2.4
La Minería de Datos busca generar información similar a la que podría producir un experto humano, que
además satisfaga el Principio de Comprensibilidad. La Minería de Datos es el proceso de descubrir
conocimientos interesantes, como patrones, asociaciones, cambios, anomalías y estructuras significativas
a partir de grandes cantidades de datos almacenadas en bases de datos, data warehouses, o cualquier otro
medio de almacenamiento de información.
La Minería de Datos es un campo en pleno desarrollo en el que se aplican métodos de varias disciplinas
como los presentes en sistemas de bases de datos, data warehousing, estadística, el Aprendizaje
Automático, visualización de datos, obtención de información y computación de alta performance.
Además también se utilizan métodos de las áreas de redes neuronales, reconocimiento de patrones,
análisis espacial de datos, bases de datos de imágenes, procesamiento de señales y programación lógica
inductiva (ILP). Numerosos especialistas señalan que la Minería de Datos necesita de la integración de
enfoques de múltiples disciplinas [Mitchell, 1997].
Una gran cantidad de métodos de análisis de datos han sido desarrollados en estadística. El Aprendizaje
Automático ha contribuido en el área de clasificación e inducción. Las redes neuronales, por su lado, son
efectivas en la clasificación, predicción y clustering de datos. Sin embargo, con la gran cantidad de datos
almacenados en las bases de datos sobre los cuales se debe hacer la minería de datos, todos estos métodos
deben re-analizarse o escalarse para ser efectivos.
Además para procesar grandes volúmenes de datos de los cuales deben extraerse patrones
automáticamente, es necesario contar con una gran capacidad computacional de procesamiento. Es
necesario, entonces, desarrollar métodos de minería de datos distribuidos, paralelos e incrementales.
• Selección de datos (Data selection) extracción de los datos relevantes al área de análisis del
almacenamiento de datos.
• Minería de Datos: proceso esencial donde se aplican diversos métodos para extraer patrones de los
datos.
Con los sistemas de bases de datos relacionales existentes hoy en día, los cuatro procesos iniciales:
limpieza, integración, selección y transformación de datos pueden realizarse mediante la construcción de
data warehouses. Los procesos de minería de datos, evaluación de patrones y presentación de
conocimientos generalmente se agrupan en el proceso que se conoce como Minería de Datos. De ahí la
confusión que puede llegar a existir con el nombre.
• Predicción cualitativa
Cabe destacar que muchos de estos problemas son los mismos con los que se enfrenta un sistema de
Aprendizaje Automático que aprende a partir de ejemplos, que pueden tener imperfecciones o pueden
estar incompletos, o incluso, no ser representativos del problema que se está analizando.
• Predicción: esta función de la minería predice los valores posibles de datos faltantes o la distribución
de valores de ciertos atributos en un conjunto de objetos.
• Clustering: identifica clusters en los datos, donde un cluster es una colección de datos “similares”.
La similitud puede medirse mediante funciones de distancia, especificadas por los usuarios o por
expertos. La Minería de Datos trata de encontrar clusters de buena calidad que sean escalables a
grandes bases de datos y a data warehouses multidimensionales.
• Análisis de series a través del tiempo: analiza un gran conjunto de datos obtenidos con el correr del
tiempo para encontrar en él regularidades y características interesantes, incluyendo la búsqueda de
patrones secuenciales, periódicos, modas y desviaciones.
• Aprendizaje a partir de datos tabulados y no tabulados (Learning from labeled and unlabeled
data): en muchas aplicaciones el problema no está en la obtención de los datos, sino en la tabulación
de los mismos.
• Aprendiendo a partir de bases de datos de gran tamaño (Learning from extremely large
databases): muchas bases de datos son demasiado grandes como para ser leídas y procesadas por una
computadora más de una vez. Lo cual imposibilita el uso de algoritmos que requieran múltiples
pasadas sobre los datos. Debe afrontarse entonces, el desafío de encontrar algoritmos inteligentes que
sean escalables eficientemente a grandes bases de datos.
• Aprendiendo con conocimientos previos (Learning with prior knowledge): En muchos casos, se
poseen conocimientos efectivos acerca del fenómeno en estudio. Deben existir métodos capaces de
incorporar conocimientos previos tanto abstractos, como diversos o inciertos.
• Aprendiendo a partir de datos de distintos tipos (Learning from mixed media data): Muchos
juegos de datos contienen varios tipos de datos. Un buen ejemplo, se presenta en las bases de datos
médicas que contienen estadísticas acerca de los pacientes, gráficos, rayos X, etc. La gran mayoría de
los algoritmos actuales sólo pueden trabajar con un único tipo de datos, con lo cual deben encontrarse
métodos capaces de manejar los distintos tipos o formatos de datos presentes en una base de datos.
En el Clustering o Clasificación se analizan los datos y se generan conjuntos de reglas que agrupen y
clasifiquen los datos futuros. Debe tenerse en cuenta que en la Minería de Datos se busca obtener reglas
que particionen los datos en clases predefinidas, esto se torna complicado cuando hay una gran cantidad
de atributos y millones de registros.
Una regla de asociación es una regla que implica o presenta ciertas relaciones entre un grupo de objetos
en una base de datos. En el proceso de la Minería de Datos se obtienen varias reglas de este tipo con
distintos niveles de abstracción. Nuevamente, no debemos olvidar que esto puede implicar el análisis
iterativo de bases de datos transaccionales o relacionales, con millones de registros, lo cual presenta un
elevado costo operativo. Por lo tanto, la obtención de reglas a partir de bases de datos relacionales o
transaccionales es un importante tema de estudio.
Por último, el análisis de secuencias trata de encontrar patrones que ocurren con una secuencia
determinada. Trabaja sobre datos que aparecen en distintas transacciones – a diferencia de los datos que
aparecen relacionados mediante reglas dentro de una misma transacción -.
A continuación se presentan ejemplos de algoritmos de Minería de Datos existentes, de cada uno de los
tipos presentados.
A continuación se describen brevemente aquellos métodos de Aprendizaje Automático que han sido
aplicados a la Minería de Datos con cierto éxito:
• Algoritmos estadísticos: Muchos algoritmos estadísticos han sido utilizados por los analistas para
detectar patrones inusuales en los datos y explicar dichos patrones mediante la utilización de modelos
estadísticos, como, por ejemplo, los modelos lineales. Estos métodos se han ganado su lugar y
seguirán siendo utilizados en los años venideros.
• Redes Neuronales: las redes neuronales imitan la capacidad de la mente humana para encontrar
patrones. Han sido aplicadas con éxito en aplicaciones que trabajan sobre la clasificación de los
datos.
• Método del vecino más cercano: es una técnica que clasifica cada registro de un conjunto de datos
en base a la combinación de las clases de los k registros más similares. Generalmente se utiliza en
bases de datos históricas.
• Visualización de los datos: la interpretación visual de las relaciones entre datos multidimensionales
Los métodos de aprendizaje basados en reglas de clasificación buscan obtener reglas o árboles de decisión
que particionen un grupo de datos en clases predefinidas. Para cualquier dominio real, el espacio de datos
es demasiado grande como para realizar una búsqueda exhaustiva en el mismo.
En cuanto a los métodos inductivos, la elección del atributo para cada uno de los nodos se basa en la
ganancia de entropía generada por cada uno de los atributos. Una vez que se ha recopilado la información
acerca de la distribución de todas las clases, la ganancia en la entropía se calcula utilizando la teoría de la
información o bien el índice de Gini [Joshi, 1997].
transacciones, donde cada transacción es un conjunto de ítems, una regla de asociación es una expresión
de la forma XY, donde X e Y son conjuntos de ítems. Un ejemplo de regla de asociación sería: “30% de
las transacciones que contienen niños, también contienen pañales; 2% de las transacciones contienen
ambas cosas”. En este caso el 30% es el nivel de confianza de la regla y 2% es la cantidad de casos que
respaldan la regla. La cuestión está en encontrar todas las reglas de asociación que satisfagan los
requerimientos de confianza mínima y máxima impuestos por el usuario.
codificación de los
ejemplos ejemplos
E C ML
En la figura 2.6, el entorno E representa el mundo real, el entorno sobre el cual se realiza el aprendizaje. E
representa un número finito de observaciones u objetos que son codificados en algún formato legible para
Aprendizaje Automático. El conjunto de ejemplos codificados constituye el conjunto de entrenamiento
para el sistema de aprendizaje automático.
Por su lado, en la figura 2.7, la codificación C es reemplazada por una base de datos, que modela el
entorno. Cada estado en la base de datos refleja algún estado de E, y cada transición de estados en la base
22 Magdalena Servente Estado del Arte
Algoritmos TDIDT aplicados a la Minería de Datos Inteligente
de datos representa una transición de estados en E. El algoritmo utilizado para realizar la minería de datos
construye entonces un modelo a partir de los datos en la base de datos.
codificación de los
ejemplos ejemplos
E DM
BD
Aunque a simple vista, la Minería de Datos parece muy similar a Aprendizaje Automático, hay
importantes diferencias que deben tenerse en cuenta. La base de datos generalmente se construye con
fines distintos a la Minería de Datos, con lo cual la base se diseña según los requerimientos del sistema y
no según los requerimientos del algoritmo de aprendizaje.
2.3.1. Aplicaciones
A continuación se describen algunos algoritmos de Aprendizaje Automático que han sido utilizados con
éxito en la Minería de Datos. Algunos de ellos son generales y pueden ser utilizados en varios dominios
de conocimiento, mientras que otros fueron diseñados para dominios particulares.
2.3.1.1. ID3
Este sistema ha sido el que más impacto ha tenido en la Minería de Datos. Desarrollado en los años
ochenta por Quinlan, ID3 significa Induction Decision Trees, y es un sistema de aprendizaje supervisado
que construye árboles de decisión a partir de un conjunto de ejemplos. Estos ejemplos son tuplas
compuestas por varios atributos y una única clase. El dominio de cada atributo de estas tuplas está
limitado a un conjunto de valores. Las primeras versiones del ID3 generaban descripciones para dos
clases: positiva y negativa. En las versiones posteriores, se eliminó esta restricción, pero se mantuvo la
restricción de clases disjuntas. ID3 genera descripciones que clasifican cada uno de los ejemplos del
conjunto de entrenamiento.
Este sistema tiene una buena performance en un amplio rango de aplicaciones, entre las cuales podemos
nombrar, aplicaciones de dominios médicos, artificiales y el análisis de juegos de ajedrez. El nivel de
precisión en la clasificación es alto. Sin embargo, el sistema no hace uso del conocimiento del dominio.
Además, muchas veces los árboles son demasiado frondosos, lo cual conlleva a una difícil interpretación.
En estos casos pueden ser transformados en reglas de decisión para hacerlos más comprensibles.
2.3.1.2. C4.5
El C4.5 es una extensión del ID3 que permite trabajar con valores continuos para los atributos, separando
los posibles resultados en dos ramas: una para aquellos Ai<=N y otra para Ai>N. Este algoritmo fue
propuesto por Quinlan en 1993. El algoritmo C4.5 genera un árbol de decisión a partir de los datos
mediante particiones realizadas recursivamente. El árbol se construye mediante la estrategia de
profundidad-primero (depth-first). El algoritmo considera todas las pruebas posibles que pueden dividir el
conjunto de datos y selecciona la prueba que resulta en la mayor ganancia de información. Para cada
atributo discreto, se considera una prueba con n resultados, siendo n el número de valores posibles que
puede tomar el atributo. Para cada atributo continuo, se realiza una prueba binaria sobre cada uno de los
valores que toma el atributo en los datos.
2.3.1.3. AQ15
El AQ15 fue desarrollado por Michalski. Es un sistema de aprendizaje inductivo que genera reglas de
decisión, donde el antecedente es una fórmula lógica. Una característica particular de este sistema es la
inducción constructiva (constructive induction), es decir, el uso de conocimientos del dominio para
generar nuevos atributos que no están presentes en los datos de entrada.
Al igual que el ID3, el AQ15 está diseñado para la generación de reglas fuertes, es decir, que para cada
clase, se construye una regla que cubre todos los ejemplos positivos y ningún ejemplo negativo. El
sistema soluciona el problema de los ejemplos incompletos o inconsistentes mediante un pre o post
procesamiento. En el post procesamiento, además, se reduce de forma drástica la cantidad de reglas
generadas mediante el truncamiento de reglas, el cual no afecta la precisión de las reglas obtenidas.
AQ15 ha sido testeado en dominios médicos, como el diagnóstico en la limfografía, diagnóstico de cáncer
de mama y la ubicación del tumor primario. En estos casos, se obtuvieron reglas con el mismo nivel de
precisión que el de los expertos humanos. En todos los casos, los datos de entrenamiento son conjuntos
chicos, de unos cientos de ejemplos.
2.3.1.4. CN2
El sistema CN2, desarrollado por Clark y Niblett, es una adaptación del AQ15. La gran desventaja del
AQ15 es que elimina los ruidos mediante pre y post procesamiento y no durante la ejecución del
algoritmo. El objetivo del CN2 es, entonces, incorporar el manejo de datos ruidosos al algoritmo en sí.
Combina entonces las técnicas de poda utilizadas en el ID3, con las técnicas de reglas condicionales
utilizadas en el AQ15.
El CN2 genera reglas simples y comprensibles en dominios donde los datos pueden tener ruido.
Construye reglas probabilísticas, es decir, el antecedente en cada regla cubre ejemplos positivos de una
clase, pero también puede cubrir ejemplos de otra clase en menor número. De esta forma no restringe el
espacio de búsqueda únicamente a aquellas reglas inferibles a partir de los ejemplos.
La performance el ID3, AQ15 y CN2 ha sido comparada en dominios médicos y artificiales. Las
estructuras de conocimiento generadas en cada caso son de similar calidad y complejidad.
2.3.1.5. DBLearn
El sistema DBLearn fue diseñado por Cai, Han y Cercone y utiliza conocimientos del dominio para
generar descripciones para subconjuntos predefinidos de una base de datos relacional. Las características
especiales de este sistema son su estrategia de búsqueda de abajo hacia arriba (bottom up); el uso de
conocimientos del dominio como jerarquías de valores de atributos y el uso del álgebra relacional. El
conjunto de entrenamiento es una tabla de datos relacional con n-tuplas.
El sistema DBLearn es relativamente simple, ya que utiliza solo dos operaciones de generalización para
construir los descriptores. La generalización está orientada a los atributos, lo cual limita el conjunto de
descriptores que pueden ser construidos. La performance del sistema es buena, y la complejidad en el
tiempo está en el orden de los O(N logN), siendo N la cantidad inicial de tuplas.
2.3.1.6. Meta-Dendral
El sistema Meta-Dendral es un sistema especial para la generación de reglas de conocimiento en la
estereoscopia. Esta ciencia estudia la estructura tridimensional de la molécula. El Meta-Dendral es
interesante porque utiliza un sistema de representación de conocimientos totalmente diferente a los
anteriores. Al buscar generar reglas que puedan predecir dónde se romperá la estructura de una molécula,
toma las estructuras moleculares como entrada.
El sistema ha sido exitoso para encontrar reglas de fragmentación desconocidas hasta el momento. Sin
embargo, la estrategia de búsqueda es ineficiente, ya que genera muchas reglas de decisión que luego son
eliminadas en la etapa de optimización. Es muy difícil encontrar heurísticas que guíen la búsqueda y no
existen técnicas explícitas que ayuden a eliminar ruidos o a destacar casos especiales.
2.3.1.7. RADIX/RX
El sistema RX se utiliza para el descubrimiento de relaciones en bases de datos clínicas. La diferencia
importante con otros sistemas es que incorpora la noción de tiempo: un dato es un conjunto de ejemplos
que guardan información de un paciente en diferentes momentos, y los conocimientos generados son de
naturaleza causal. El sistema divide su proceso de descubrimiento en dos etapas: primero genera hipótesis
y, luego, utiliza técnicas avanzadas de estadística para validarlas.
El sistema RX fue utilizado en una base de reumatología y sirvió para probar hipótesis acerca de la
cantidad de droga prodnisone que aumenta el colesterol en la sangre. Sin embargo, la principal desventaja
de este sistema es que no utiliza información del dominio para guiar la búsqueda. Una versión mejorada
del RX, el RADIX, sí lo hace.
2.3.1.8. BACON
El sistema BACON utiliza algoritmos de análisis de datos para descubrir relaciones matemáticas entre
datos numéricos. Ha redescubierto leyes como la ley de Ohm para circuitos eléctricos y la ley de
desplazamiento de Arquímides. Los datos de entrenamiento son numéricos y, normalmente, son
generadas en algún experimento previo. Cada tupla esta constituida por los valores de las mediciones
durante el experimento.
El sistema BACON tiene varias desventajas: no considera el ruido en los datos, ni la inconsistencia o los
datos incompletos. Además, considera que todas las variables son relevantes, y explora todas las
soluciones posibles utilizando un grafo, lo cual empeora considerablemente su performance.
2.3.1.9. SLIQ
El algoritmo SLIQ (Supervised Learning In Quest) fue desarrollado por el equipo Quest de IBM. Este
algoritmo utiliza los árboles de decisión para clasificar grandes cantidades de datos. El uso de técnicas de
pre-ordenamiento en la etapa de crecimiento del árbol, evita los costos de ordenamiento en cada uno de
los nodos. SLIQ mantiene una lista ordenada independiente de cada uno de los valores de los atributos
continuos y una lista separada de cada una de las clases. Un registro en la lista ordenada de atributos
consiste en el valor del atributo y un índice a la clase correspondiente en la lista de clases. SLIQ
construye el árbol de forma ancho-primero (breadth-first). Para cada uno de los atributos busca en la lista
correspondiente y calcula los valores de entropía para cada uno de los nodos de la frontera
simultáneamente. A partir de la información obtenida se particionan los nodos de la frontera, y se
expanden para obtener una nueva frontera.
Aunque SLIQ trabaja con datos que pueden estar en disco mientras se ejecuta el algoritmo, necesita que
cierta información resida en memoria permanentemente durante la totalidad de la ejecución del mismo.
Dicha información crece proporcionalmente a la cantidad de registros de entrada, lo cual limita en gran
medida la cantidad de registros de entrenamiento. Para solucionar este problema el equipo de desarrollo
del Quest, ha desarrollado otro algoritmo de clasificación basado en árboles de decisión: el SPRINT
(Scalable PaRallelizable INduction of decision Trees). El SPRINT elimina todas las restricciones de
memoria presentes en el SLIQ.
El árbol de decisión es una hoja, pero la clase asociada debe ser determinada por información
que no pertenece a T. Por ejemplo, una hoja puede escogerse de acuerdo a conocimientos de base
del dominio, como ser la clase mayoritaria.
En este caso, la idea es refinar T en subconjuntos de casos que tiendan, o parezcan tender hacia
una colección de casos pertenecientes a una única clase. Se elige una prueba basada en un único
atributo, que tiene uno o más resultados, mutuamente excluyentes {O1, O2,. . ., On}. T se
particiona en los subconjuntos T1, T2,. . ., Tn donde Ti contiene todos los casos de T que tienen el
resultado Oi para la prueba elegida. El árbol de decisión para T consiste en un nodo de decisión
identificando la prueba, con una rama para cada resultado posible. El mecanismo de construcción
del árbol se aplica recursivamente a cada subconjunto de datos de entrenamientos, para que la i-
ésima rama lleve al árbol de decisión construido por el subconjunto Ti de datos de entrenamiento.
Supongamos que tenemos ejemplos positivos y negativos. En este contexto la entropía del subconjunto Si,
H(Si), puede calcularse como:
Donde pi+ es la probabilidad de que un ejemplo tomado al azar de Si sea positivo. Esta probabilidad
puede calcularse como
ni+
pi+ = (2.2)
ni+ + ni−
+
Siendo ni la cantidad de ejemplos positivos de Si, y ni− la cantidad de ejemplos negativos.
La probabilidad pi− se calcula en forma análoga a pi+ , reemplazando la cantidad de ejemplos positivos
por la cantidad de ejemplos negativos, y viceversa.
Generalizando la expresión (2.1) para cualquier tipo de ejemplos, obtenemos la fórmula general de la
entropía:
n
H ( S i ) = ∑ − p i log p i (2.3)
i =1
Si el atributo at divide el conjunto S en los subconjuntos Si, i = 1,2, . .. . . , n, entonces, la entropía total
del sistema de subconjuntos será:
n
H ( S , at ) = ∑ P(S i ) ⋅ H (S i ) (2.4)
i =1
Si
P(S i ) = (2.5)
S
I (S , at ) = H (S ) − H (S , at ) (2.6)
El uso de la entropía para evaluar el mejor atributo no es el único método existente o utilizado en
Aprendizaje Automático. Sin embargo, es el utilizado por Quinlan al desarrollar el ID3 y su sucesor el
C4.5.
Para solucionar este problema, puede recurrirse a la binarización. Este método consiste en formar dos
rangos de valores de acuerdo al valor de un atributo, que pueden tomarse como simbólicos. Por ejemplo,
si en un día hubo 100ml de lluvia, pueden crearse los intervalos [0,100) y [100, +∝) y el cálculo de la
entropía se realiza como si los dos intervalos fueran los dos valores simbólicos que puede tomar el
atributo.
Existen dos enfoques para podar los árboles: la pre-poda (preprunning) y la post-poda (postprunning). En
el primer caso se detiene el crecimiento del árbol cuando la ganancia de información producida al dividir
un conjunto no supera un umbral determinado. En la post-poda se podan algunas ramas una vez que se ha
terminado de construir el árbol.
El primer enfoque, tiene la atracción de que no se pierde tiempo en construir una estructura que luego será
simplificada en el árbol final. El método típico en estos casos es buscar la mejor manera de partir el
subconjunto y evaluar la partición desde el punto de vista estadístico mediante la teoría de la ganancia de
información, reducción de errores, etc. Si esta evaluación es menor que un límite predeterminado, la
división se descarta y el árbol para el subconjunto es simplemente la hoja más apropiada. Sin embargo,
este tipo de método tiene la contra de que no es fácil detener un particionamiento en el momento
adecuado, un límite muy alto puede terminar con la partición antes de que los beneficios de particiones
subsiguientes parezcan evidentes, mientras que un límite demasiado bajo resulta en una simplificación
demasiado leve.
El segundo enfoque es, entonces, el utilizado por el ID3 y el C4.5. Una vez construido el árbol se procede
a su simplificación según los criterios propios de cada uno de los algoritmos.
Existe un principio de la ciencia conocido como Afeitadora de Occam (Occam’s Razor) [Mitchell,
2000a], que determina que cuando todas las condiciones son iguales, se prefieren las teorías simples. Es
decir, la mejor teoría científica es aquella que explica todos los hechos y tiene el menor tamaño. Como
Einstein sostuvo: “Todo debe hacerse lo más simple posible, pero no más simple que eso”. ¿Cómo
aplicamos la Afeitadora de Occam al Aprendizaje Automático? En el caso de estos sistemas, todas las
teorías generadas contienen errores, podemos decir que estos errores son como las excepciones a la
misma. Entonces, para asegurarnos que todas las condiciones sean iguales, debemos incluir las
excepciones en la teoría.
El Principio de Longitud de Descripción Mínima (MDL) [Joachims et al, 1995], [Mitchell, 2000b],
[Quinlan, 1993d;1995], [Quinlan y Cameron-Jones, 1995] sostiene que la mejor teoría para un conjunto
de datos es aquella que minimiza el tamaño de la teoría y la cantidad de información necesaria para
especificar las excepciones. Desde el punto de vista del Aprendizaje Automático esto significa que dado
un conjunto de instancias, un sistema de aprendizaje infiere una teoría a partir de ellas. Supongamos una
analogía con el campo de las comunicaciones: la teoría con las excepciones debe ser transmitida por un
canal perfecto. El MDL sostiene que la mejor generalización es aquella que requiere la menor cantidad de
bits para transmitir la generalización junto con los ejemplos a partir de la cual fue generada. Esto evita las
teorías que satisfacen los datos al extremo sobreajuste, ya que los ejemplos se transmiten también, y las
teorías demasiado extensas serán penalizadas. Por otro lado, también se puede transmitir la teoría nula
que no ayuda en lo más mínimo al transmitir los ejemplos. Entonces, pueden transmitirse tanto las teorías
simples como aquellas muy complejas y el MDL provee una forma de medir la performance de los
algoritmos basándose en los datos de entrenamiento únicamente. Esta parece ser la solución ideal al
problema de medir la performance.
Veamos cómo aplicamos el principio MDL. Supongamos que un sistema de aprendizaje genera una teoría
T, basada en un conjunto de entrenamiento E, y requiere una cierta cantidad de bits L[T] para codificar la
teoría. Dada la teoría, el conjunto de entrenamiento puede codificarse en una cantidad L[E/T] de bits.
L[E/T] está dada por la función de ganancia de información sumando todos los miembros del conjunto de
entrenamiento. La longitud de descripción total de la teoría es L[E]+L[E/T]. El principio MDL
recomienda la teoría T que minimiza esta suma.
Recordemos que los algoritmos de la familia TDIDT realizan una búsqueda en el espacio de hipótesis
posibles, constituido por todos los árboles de decisión posibles. Su sesgo inductivo, siguiendo el principio
de la Afeitadora de Occam, es una preferencia sobre los árboles pequeños frente a los árboles más
profundos y frondosos.
¿Qué pasa cuando los datos están incompletos como ocurre generalmente con cualquier conjunto de datos
de la vida real? Podemos tomar dos caminos posibles ante los datos incompletos: descartar una
proporción importante de los datos por incompletos y declarar algunos casos como inclasificables, o
adaptar los algoritmos para poder trabajar con valores de atributos faltantes. En la mayoría de los casos, la
primera opción es inaceptable. Para poder aplicar la segunda opción, hay tres cuestiones importantes que
deben ser tenidas en cuenta:
1. Selección de una prueba en la cual la partición del conjunto de entrenamiento se realiza en base a un
criterio heurístico como ser la ganancia o la proporción de ganancia. Si dos pruebas distintas utilizan
atributos con distinta cantidad de valores desconocidos, ¿cómo debe tenerse esto en cuenta al medir
su importancia relativa?
2. Una vez que una prueba ha sido seleccionada, los casos de entrenamiento con valores desconocidos
para los atributos relevantes no pueden ser asociados con una respuesta particular de la prueba, y, por
lo tanto, no pueden asignarse a un subconjunto {Ti}. ¿Cómo deben tratarse estos casos durante la
partición?
3. Cuando el árbol de decisión se utiliza para clasificar un caso nuevo, ¿cómo debe proceder el sistema
al encontrarse con un valor de atributo desconocido para el nodo de decisión que está tratando de
evaluar?
Varios autores han tratado de resolver estos problemas, generalmente rellenando los valores desconocidos
con los valores más frecuentes. En un estudio realizado por Quinlan, [Quinlan, 1989], se comparan las
soluciones más comunes a este problema. El autor llega a la conclusión general de que existen varios
enfoques que son notablemente inferiores, pero no existe ningún enfoque que sea claramente superior. A
continuación se presenta un resumen del estudio.
Todos los enfoques descriptos a continuación fueron implementados como variantes de un programa que
construye un árbol de decisión utilizando la proporción de ganancia [Quinlan, 1989]. Los árboles
producidos no fueron podados. Varios enfoques para solucionar los tres problemas planteados fueron
explorados. Cada uno de ellos tiene una letra identificatoria, tal que una combinación de letras implica
una combinación de métodos.
C- Completar los valores de A con el valor más frecuente para el atributo antes de calcular la
ganancia.
I- Ignorar el caso
P- Asignar el caso a uno de los subconjuntos con probabilidad proporcional al número de casos
con valores conocidos en cada subconjunto.
F- Asignar una fracción del caso a cada subconjunto utilizando las proporciones explicadas en
el inciso anterior.
Al clasificar un caso nuevo con un valor desconocido del atributo A que debe ser evaluado.
F- Explorar todas las ramas, combinando los resultados para reflejar las probabilidades de los
distintos resultados.
• Valores desconocidos al particionar: los resultados de las pruebas revelan una clara superioridad del
RFF (asignar casos fraccionales a los subconjuntos) y una clara desventaja del RIF (ignorar los casos
de entrenamiento con valores desconocidos).
• Valores desconocidos al clasificar: la estrategia de parar ante los valores desconocidos dio muy
malos resultados, mientras que todos las otras estrategias dieron resultados similares
• Valores desconocidos al seleccionar las pruebas: ignorar los valores desconocidos dio resultados
peores que reducir la ganancia o completar los valores, pero no existió un método claramente
superior entre estos dos últimos.
• En la evaluación de pruebas, los enfoques que ignoran los casos con valores desconocidos (y por lo
tanto no tienen en cuenta la proporción de desconocimiento) presentan malos resultados cuando esta
proporción varía de atributo en atributo.
• Cuando el conjunto de entrenamiento se divide ignorando los casos con valores desconocidos para el
atributo probado, se obtienen resultados pobres (esta es la forma en que el ID3 realiza las
particiones). El enfoque de dividir los casos entre los subconjuntos resultó muy bueno.
Durante la clasificación, tratar de determinar el resultado más probable de una prueba, funciona bien en
algunos dominios (aquellos en los cuales la sustitución puede realizarse con confianza), pero muy mal en
otros. La combinación de todos los resultados posibles es más resilente, dando una mayor certeza en la
clasificación general.
En cualquier árbol de decisión, las condiciones que deben satisfacerse cuando un caso se clasifica por una
hoja pueden encontrarse analizando los resultados de las pruebas en el camino recorrido desde la raíz. Es
más, si el camino fuese transformado directamente en una regla de producción, dicha regla podría ser
expresada como una conjunción de todas las condiciones que deben ser satisfechas para llegar a la hoja.
Consecuentemente, todos los antecedentes de las reglas generadas de esta manera serían mutuamente
excluyentes y exhaustivos.
Entonces claseK
Diremos que una regla cubre un caso si el caso satisface todas las condiciones en el antecedente de la
misma.
Si se cuenta con una gran cantidad de datos, la evaluación no es problema: se genera un modelo a partir
de un conjunto grande de entrenamiento y, luego, se lo prueba con otro gran conjunto de datos. Sin
embargo, aunque la Minería de Datos implica por su definición trabajar con grandes cantidades de datos,
los conjuntos de datos de buena calidad son pocos. Los datos de entrenamiento deben ser cuidadosamente
generados y analizados por expertos humanos, un recurso que escasea.
• Eficiencia: un sistema debe ser capaz de generar descripciones correctas con un número mínimo de
ejemplos. Un instructor no siempre puede dotar al sistema de una cantidad infinita de ejemplos, y la
velocidad en el aprendizaje es un indicador de inteligencia. Dentro de la eficiencia, debemos evaluar
también los requerimientos computacionales. Estos se miden en función a la cantidad de tiempo y
recursos que un sistema necesita para llegar a una buena descripción.
• Robustez: contra el ruido y contra los ejemplos incompletos. Cada sistema maneja estos dos
problemas de forma diferente, con lo cual debe evaluarse en cada sistema en particular.
• Requerimientos especiales: en algunos dominios, se requiere que un sistema aprenda a medida que
llegan los ejemplos. Esto se conoce como aprendizaje incremental y es, especialmente, importante en
aquellas áreas en que los conceptos evolucionan, cambian su significado a través del tiempo.
Por supuesto, lo que nos interesa es estimar la proporción de errores sobre los nuevos datos y no sobre los
datos de entrenamiento, los cuales ya están clasificados. ¿Podemos decir que la proporción de error
estimada a partir de los datos de entrenamiento es correcta para los datos futuros? No, si los datos sobre
los que se estimó el error fueron utilizados al generar el clasificador. La proporción de error sobre los
datos de entrenamiento no es un buen indicador de los errores futuros; como el clasificador se generó a
partir de estos datos, la proporción de error es subjetiva y totalmente optimista. La proporción de error
generada a partir de los datos de entrenamiento se conoce como error de sustitución, ya que se calcula al
sustituir las instancias en un clasificador que fue construido a partir de ellas. A pesar de que no es un buen
estimador para la predicción de futuros errores, es muy útil conocerlo.
Para predecir la performance del clasificador en los datos futuros, necesitamos evaluar la proporción de
error sobre datos no utilizados durante la construcción del mismo. El conjunto independiente de datos
utilizado con este propósito es el conjunto de prueba. Es esencial que el conjunto de prueba no haya sido
utilizado para nada en la generación del clasificador. Entonces, aquellos esquemas en que la construcción
se realiza en dos etapas o requieren probar el clasificador, trabajan con dos conjuntos de datos: el de
entrenamiento y el de prueba.
Podemos decir que a mayor cantidad de datos, mejor clasificador y mejor estimador de error. El problema
está cuando hay una pequeña cantidad de datos de entrenamiento. En muchas situaciones, los datos de
entrenamiento y prueba deben clasificarse manualmente. Debemos encontrar la forma de encontrar un
buen estimador de error, aún cuando los datos de prueba escasean. A continuación, se explican varios
métodos para evaluar los algoritmos de clasificación.
Por supuesto, siempre cabe la posibilidad de que los datos utilizados para entrenamiento y prueba no sean
representativos de los datos sobre los que se utilizará el modelo posteriormente. En general, no se puede
afirmar si un conjunto es representativo o no, pero existe una prueba muy simple que vale la pena
realizar: cada una de las clases del conjunto total de datos debería estar representada en la misma
proporción en los datos de entrenamiento y prueba, y esta proporción debería ser similar a la que se
presentará cuando se aplique el modelo generado al caso real. Supongamos que todos los ejemplos de una
clase determinada no entran en el conjunto de entrenamiento, el clasificador generado será incorrecto. Al
trabajar con la hipótesis del Universo Cerrado, no podemos pretender que clasifique correctamente los
miembros de una clase que no sabía que existía. Si en el conjunto de prueba hay datos de esa clase, la
proporción de error obtenida será muy grande. Entonces, al dividir al azar los datos preclasificados entre
los conjuntos de entrenamiento y prueba, debemos garantizar que cada clase esté correctamente
representada tanto en los datos de prueba como en los de entrenamiento. Este procedimiento se conoce
como estratificación (stratification), y podemos hablar de una retención estratificada.
Una manera de evitar la tendencia introducida por los datos retenidos, es repetir el proceso completo
(entrenamiento y prueba) varias veces con distintas divisiones de los datos. En cada iteración, una misma
proporción de los datos se retiene al azar para las pruebas y el resto se utiliza para el entrenamiento. Las
proporciones de error obtenidas en las múltiples iteraciones se promedian para obtener una proporción de
error general. Este método se conoce como retención repetida (repeated holdout).
En un procedimiento de retención general podríamos elegir cambiar los roles de los datos de
entrenamiento y de prueba entre sí. No obstante, esto es factible únicamente si trabajamos con una
proporción 50:50, lo cual no es ideal ya que conviene utilizar más del 50% de los datos para el
entrenamiento. Para solucionar este problema utilizamos una variación del método estadístico de
validación cruzada (cross-validation).
En la validación cruzada, se determina con anterioridad una cierta cantidad de pliegos o particiones de los
datos. Supongamos que utilizamos tres, es decir, los datos se dividen al azar en tres particiones de
aproximadamente la misma cantidad, y cada una a su turno se utiliza para prueba mientras que las otras
dos se utilizan para entrenamiento. Por lo tanto, utilizamos un tercio para prueba y dos tercios para
entrenamiento, y repetimos el procedimiento tres veces. Las tres proporciones de error obtenidas se
promedian para llegar a una proporción de error general. Este procedimiento conocido como validación
cruzada de tres pliegues (threefold cross-validation), puede trabajar con datos estratificados, en cuyo caso
sería validación cruzada de tres pliegues estratificada.
Podemos generalizar el método para llegar a una validación cruzada de n pliegues, estratificada o no. El
caso más utilizado para predecir la proporción de error de una técnica de aprendizaje es utilizar una
validación cruzada de diez pliegues. Además, las pruebas han demostrado que el uso de la estratificación
mejora los resultados levemente [Witten y Frank, 2000]. Utilizar una validación cruzada de diez pliegues
puede no ser suficiente para obtener un buen estimador de la proporción de error. Distintas validaciones
cruzadas de diez pliegues dan distintos resultados dado que la división en pliegues se realiza al azar. La
estratificación reduce la variación entre los resultados pero no la elimina. Si se quiere obtener una
estimación de la proporción de error muy precisa, generalmente, se repite la validación cruzada de diez
pliegues diez veces y se promedian los resultados. Esto implica invocar al algoritmo de aprendizaje cien
veces con conjuntos de datos del tamaño del 90% del conjunto original. Obtener una buena medida de la
performance es un método que consume grandes recursos computacionales.
Este procedimiento es atractivo por dos razones [Witten y Frank, 2000]. Primero, se utiliza la mayor
cantidad de ejemplos posibles para el entrenamiento, lo cual se presume incrementa la posibilidad de que
el clasificador sea correcto. Segundo, el procedimiento es determinístico: no se parten los datos al azar.
Además, no tiene sentido repetir el procedimiento diez ni cien veces, ya que siempre se obtendrá el
mismo resultado. Debe tenerse en cuenta que dado el alto costo computacional de aplicar este método, no
es factible utilizarlo para grandes conjunto de datos. Sin embargo, este método es el mejor para pequeños
conjuntos de datos porque, en cierta medida, evalúa todas las posibilidades.
2.5.2.3. Bootstrap
Este método está basado en el procedimiento estadístico de obtener muestras con sustitución. En los
métodos anteriores, cuando se tomaba una muestra de los datos de entrenamiento o de prueba, se lo hacía
sin reemplazo. Es decir, la misma instancia, una vez seleccionada, no podía seleccionarse nuevamente. La
mayoría de las técnicas de aprendizaje pueden, no obstante, utilizar la misma instancia dos veces, y el
hecho de repetirse genera una diferencia.
La idea del bootstrap es tomar muestras del conjunto de datos con remplazo para formar un conjunto de
entrenamiento. Para ello, un conjunto de n instancias se muestrea n veces, con reemplazo, y se obtiene
otro conjunto de datos de n instancias. Como algunas instancias del segundo conjunto estarán repetidas,
deben existir algunas instancias del conjunto original que no fueron seleccionadas. Utilizaremos estas
instancias para el conjunto de prueba.
La probabilidad de que una instancia particular sea elegida para el conjunto de entrenamiento es de 1/n, y,
por lo tanto, hay un 1-1/n de probabilidad de que no sea elegida. Si multiplicamos esto según las n
oportunidades de ser elegida, obtenemos la siguiente probabilidad de que no sea escogida:
n
1
1 - = e = 0.368
-1
n
El error estimado sobre el conjunto de prueba será pesimista porque el clasificador tiene en cuenta sólo el
63% de los datos del conjunto original, lo cual es poco frente al 90% de la validación cruzada de diez
pliegues. Para compensar el error del conjunto de entrenamiento se combina con el error en el conjunto de
prueba de la siguiente manera:
Luego, todo el proceso de bootstrap se repite varias veces, y todos los estimadores de error se promedian.
Si los costos son conocidos, pueden incluirse en el análisis de los métodos. Restringiremos nuestro
análisis a los casos que tienen clases sí y no únicamente. Los cuatro resultados posibles de una predicción
pueden listarse en una matriz de confusión como la que se muestra a continuación.
Clase predicha
Sí No
Clase verdadera Sí Verdadero positivo Falso Negativo
No Falso Positivo Verdadero Negativo
Los verdaderos positivos y verdaderos negativos son los casos sin error. Los falsos positivos
corresponden a aquellas instancias negativas que fueron clasificadas como positivas, mientras que los
falsos negativos son aquellas instancias clasificadas como negativas cuando en realidad son positivas.
Estos dos casos de errores generalmente tienen distintos costos, como los casos clasificados
correctamente tienen distintos beneficios. El hecho de pensar en el costo genera mejores decisiones.
CAPÍTULO 3
En este capítulo se presenta el contexto de nuestro problema de interés (Sección 3.1) y las
cuestiones que los algoritmos ID3 y C4.5 pertenecientes a la familia TDIDT deben resolver
(Sección 3.2).
Existen muchos tipos de análisis y muchos tipos de resultados obtenibles. Por ejemplo, podemos realizar
un análisis estadístico sobre la distribución de una población de ardillas en un bosque a lo largo de los
años; u obtener la varianza y desviación estándar de los depósitos japoneses en el mercado americano de
la última década; o realizar un análisis de componentes para un determinado experimento físico, entre
otros.
Los métodos nombrados hasta ahora son esencialmente cuantitativos. Sin embargo, ¿qué pasa cuando más
allá de los modelos matemáticos encerrados en los datos, nos interesan los modelos lógicos? ¿Cuándo
más allá de las direcciones para hacer un mailing de la base de personas, nos interesa hacer un mailing
sólo a los potenciales clientes? ¿Cómo distinguimos a los potenciales clientes del resto de la gente? ¿Qué
características tienen en común? ¿Qué datos los distinguen? Cuando el análisis de los datos que estamos
buscando excede los alcances de un modelo cuantitativo y está orientado hacia una descripción cualitativa
de los datos, debemos utilizar los algoritmos inteligentes.
Estos algoritmos del Aprendizaje Automático están orientados hacia el desarrollo de descripciones
simbólicas de los datos que puedan caracterizar a uno o más conceptos, diferenciar entre clases de
conceptos y describir porqué razón un objeto pertenece a una clase y no a otra. Con este tipo de
algoritmos nuestro problema del análisis de los potenciales clientes estaría resuelto.
Existen muchos tipos de algoritmos de Aprendizaje Automático; no obstante, los más útiles para nosotros
son aquellos que no quedan encerrados en el “cerebro” de la computadora, sino que pueden adaptarse a
nuestra forma de pensar. Si el resultado de realizar Minería de Datos inteligente sobre una base es una red
neuronal, ésta puede ser muy útil para clasificar nuevos ejemplos, en la medida en que sean clasificados
por medio de un programa entrenado para ello. Una persona jamás será capaz de aplicar rápida y
efectivamente una red neuronal u otro modelo similar para clasificar distintos datos. En cambio si la
persona se encuentra ante un árbol de decisión o un conjunto de reglas de decisión que debe aplicar en
orden como resultado de la Minería, la clasificación del nuevo caso es tan fácil como la lectura del árbol
desde la raíz hasta las hojas.
Este tipo de modelo de datos que representa los conceptos inherentes y ocultos en los datos, de forma tal
que son fáciles de interpretar, utilizar e incorporar para la persona humana son los que más enriquecen
nuestro conocimiento y, como tales, aquellos sobre los cuales focalizaremos nuestra atención.
En las bases de datos, las entidades se caracterizan, generalmente, por un conjunto de atributos que las
describen. Los atributos pueden ser tanto discretos como continuos, pero lo importante es que todas las
entidades están descriptas por el mismo grupo de atributos. En los problemas de clasificación, un atributo
particular, conocido como clase, describe al tipo de concepto.
¿En qué medida los algoritmos de la familia TDIDT pueden usarse para generar modelos válidos en los
problemas de clasificación?
Analizar la aplicación de todos los Algoritmos Inteligentes a la Minería de Datos excedería los alcances
de este trabajo; por ende, vamos a centrar nuestro análisis en aquellos algoritmos que generan árboles y
reglas de decisión que son modelos que un ser humano puede interpretar y aplicar fácilmente. Un buen
modelo de datos para clasificar fenómenos meteorológicos descriptos en función de varias medidas
ambientales tomadas en los días previos, pueden ayudar a un meteorólogo a determinar si habrá sol,
lloverá, o habrá un tornado.
Cuestión 1:
Cuestión 2:
¿Qué condiciones deben cumplir los datos para que su análisis con el ID3 o el C4.5 sea útil
y válido?
Cuestión 3:
Cuestión 4:
Cuestión 5:
CAPÍTULO 4
SOLUCIÓN PROPUESTA
En este capítulo se presentan todos los aspectos de la solución propuesta. Para ello se describen
las características generales de la misma (Sección 4.1): el marco teórico (Sección 4.1.1), las
condiciones que deben cumplir los datos sobre los que se realiza la Minería de Datos (Sección
4.1.2) y los tipos de resultados obtenidos (Sección 4.1.3). En la Sección 4.2 se presenta una
descripción general de los algoritmos ID3 y C4.5 que se utilizan para estudiar el problema. Se
explica cómo realizar la división de los datos de entrada (Sección 4.2.1) y la elección del criterio
de división (Sección 4.2.1.1). Luego, se presenta una descripción detallada del algoritmo ID3
(Sección 4.3.1) y de sus limitaciones (Sección 4.3.2). A modo de ejemplo, se muestra la
utilización del ID3 para generar un árbol y reglas de decisión (Sección 4.3.1.5). También se
detalla el algoritmo C4.5 (Sección 4.4) y sus características particulares (Sección 4.4.2), las
cuales lo diferencian del ID3, resaltando la poda de los árboles de decisión (Sección 4.4.3) y la
estimación de errores en dichos árboles (Sección 4.4.4). En la sección 4.4.5 se ejemplifican los
métodos anteriores. La generalización de las reglas de decisión realizada por el C4.5 se explica
en la sección 4.4.6. A continuación, se detalla el diseño del sistema integrador utilizado para
estudiar el éxito de la solución propuesta (Sección 4.5). Para este sistema se presenta una
descripción general (Sección 4.5.1) y el diseño de las secciones del sistema para el ID3 (Sección
4.5.2.1) y para el C4.5 (Sección 4.5.2.2)
Dados:
• un conjunto C de clases,
Encontrar:
∀ e ∈ E: H ∩ e = c ∧ H ∩ e ≠ c’ (4.1)
• un árbol de decisión,
• Clases predefinidas: las categorías a las cuales se asignan los casos deben estar establecidas de
antemano. Esto significa que los algoritmos se aplican sobre un conjunto de datos de entrenamiento
previamente clasificados, del tipo {valor_atributo1, valor_atributo2, ...., valor_atributon, clasek}. En
la terminología del Aprendizaje Automático, esto se conoce como aprendizaje supervisado, en
contraposición al aprendizaje no supervisado en el cual la agrupación de casos se encuentra mediante
y durante el análisis.
• Clases discretas y disjuntas: las clases a las cuales se asignan los casos deben ser totalmente
disjuntas: un caso pertenece o no pertenece a una clase, pero no puede pertenecer a dos clases a la
vez. Además, deben existir muchos más casos que clases para que el modelo generado sea válido en
el dominio analizado. Por otro lado, dado la naturaleza de los árboles de decisión, las clases deben ser
discretas o discretizarse en caso de ser continuas.
• Datos suficientes: los patrones generados por la generalización inductiva no serán válidos si no se
los pueden distinguir de las casualidades. Como esta diferenciación se basa generalmente en pruebas
estadísticas, deben existir casos suficientes para que dichas pruebas sean efectivas. La cantidad de
datos requeridos está afectada por factores como la cantidad de propiedades y clases, y la
complejidad del modelo de clasificación; a medida que estos se incrementan, se necesitan más datos
para construir un modelo confiable.
2
El hecho de que los datos deben poder expresarse como un archivo plano, no restringe la aplicación del ID3 o del C4.5 a una única
tabla de una base de datos, sino que para aplicarse a múltiples tablas, éstas deben ser desnormalizadas hasta obtener una tabla con
los atributos que se desea analizar.
• Los datos de entrenamiento pueden contener errores: según Mitchell, los métodos de aprendizaje
utilizando árboles de decisión son robustos frente a los errores, tanto en los valores de las clases
como en los valores de los atributos de los datos de entrenamiento [Mitchell 1997].
• Los datos de entrenamiento pueden contener valores de atributos faltantes: los métodos de la
familia TDIDT pueden utilizarse aún cuando no se conocen todos los valores de todos los atributos
de los datos de entrenamiento. El tratamiento de valores faltantes varía de un algoritmo a otro y será
explicado para el ID3 en la sección 4.3.1.3 y para el C4.5 en la sección 4.4.2.2.
• Modelos lógicos generados: los programas sólo construyen clasificadores que pueden ser
expresados como árboles de decisión o como un conjunto de reglas de producción. Estos modelos
restringen las descripciones de clases a una expresión lógica cuyas primitivas son afirmaciones
acerca de los valores de atributos particulares. La expresión lógica representada por un árbol de
decisión es una disyunción de conjunciones. Todos aquellos casos que requieran un modelo de otra
índole no podrán ser analizados por los algoritmos ID3 o C4.5.
d1(p1)
Nodos
descriptores F(d1(p1),d2(p2))=c
d2(p2) c
Salida: c
Hojas o Nodos de
c c
clasificadores
Salida
Figura 4.1: Estructura de un árbol de decisión
Un árbol de decisión puede analizarse como una disyunción de conjunciones. Cada camino desde la raíz
hasta las hojas representa una conjunción, y todos los caminos son alternativos, es decir, son
disyunciones.
Donde el antecedente es una conjunción entre distintas pruebas de valor sobre los valores de los atributos;
y el consecuente es una clase para todos los casos que satisfagan el antecedente. Por ejemplo,
Las reglas de decisión se presentan en orden, y deben interpretarse de esa manera. El orden determina
cuáles reglas deben ejecutarse primero. Al clasificar un nuevo caso se avanza en la lista hasta llegar a un
antecedente que sea satisfecho por el caso, entonces la clase del caso es la correspondiente al consecuente
de dicha regla. El C4.5 en particular, agregar una última regla a la lista, ésta no tiene antecedente, es la
regla con la clase por defecto, es decir, si el caso no satisfizo ninguna de las reglas anteriores, entonces es
de la clase indicada por la última regla que no tiene antecedente.
En el caso de las reglas de decisión, agregar una nueva regla implica simplemente añadirla a la lista de
reglas sin necesidad de hacer cambios de estructura, mientras que agregar una nueva regla en un árbol
implicaría rehacer la estructura del mismo.
• Un nodo de decisión que especifica alguna prueba a ser realizada sobre un único atributo, con una
rama y subárbol para cada valor posible de la prueba.
El árbol de decisión generado por el C4.5 cuenta con varias características particulares: cada hoja tiene
asociados dos números, que indican el número de casos de entrenamientos cubiertos por cada hoja y la
cantidad de ellos clasificados erróneamente por la hoja. Es en cierta manera, un estimador del éxito del
árbol sobre los casos de entrenamiento. El ID3, en cambio, no clasifica erróneamente a los datos de
entrenamiento, con lo cual no son necesarios este tipo de indicadores. Es por ello, que este algoritmo, a
diferencia del C4.5, corre el riesgo de caer en sobreajuste.
tanto, tenga poder predictivo. Para ello, necesitamos un número importante de casos en cada hoja o, dicho
de otra manera, la partición debe tener la menor cantidad de clases posibles. En el caso ideal, nos gustaría
elegir en cada paso la prueba que genere el árbol más pequeño.
Entonces, estamos buscando un árbol de decisión compacto que sea consistente con los datos de
entrenamiento. Podríamos explorar todos los árboles posibles y elegir el más simple.
Desafortunadamente, un número exponencial de árboles debería ser analizado. El problema de encontrar
el árbol de decisión más pequeño consistente con un conjunto de entrenamiento es de complejidad NP-
completa.
La solución propuesta permite la utilización de ambos criterios. Se estudiarán y compararán los resultados
obtenidos con el ID3 y con el C4.5 utilizando la ganancia y la proporción de ganancia.
Recordemos la definición de ganancia presentada en la ecuación 2.6. Supongamos que tenemos una
prueba posible con n resultados que particionan al conjunto T de entrenamiento en los subconjuntos T1,
T2,. . ., Tn. Si la prueba se realiza sin explorar las divisiones subsiguientes de los subconjuntos Ti, la única
información disponible para evaluar la partición es la distribución de clases en T y sus subconjuntos.
Consideremos una medida similar luego de que T ha sido particionado de acuerdo a los n resultados de la
prueba X. La información esperada (entropía) puede determinarse como la suma ponderada de los
subconjuntos, de la siguiente manera
n Ti
H (T , X ) = ∑ × H (Ti ) (4.2)
i =1 T
La cantidad
I (T , X ) = H (T ) − H (T , X ) (4.3)
El criterio de ganancia tiene un defecto muy serio: presenta una tendencia muy fuerte a favorecer las
pruebas con muchos resultados. Analicemos una prueba sobre un atributo que sea la clave primaria de un
conjunto de datos, en la cual, obtendremos un único subconjunto para cada caso, y para cada subconjunto
tendremos I (T,X) = 0, entonces la ganancia de información será máxima. Desde el punto de vista de la
predicción, este tipo de división no es útil.
Esta tendencia inherente al criterio de ganancia puede corregirse mediante una suerte de normalización,
en la cual se ajusta la ganancia aparente, atribuible a pruebas con muchos resultados. Consideremos el
contenido de información de un mensaje correspondiente a los resultados de las pruebas. Por analogía a la
definición de la I(S) tenemos:
n Ti Ti
I _ división(X ) = −∑ × log 2
(4.4)
i =1 T T
I (T , X )
proporción _ de _ ganancia( X ) =
I _ división( X ) (4.5)
4.3. ID3
El algoritmo ID3 fue diseñado en 1993 por J. Ross Quinlan [Quinlan, 93a, Quinlan, 1993b]. El ID3 toma
objetos de una clase conocida y los describe en términos de una colección fija de propiedades o de
atributos, y produce un árbol de decisión sobre estos atributos que clasifica correctamente todos los
objetos [Quinlan, 1993b]. Hay ciertas cualidades que diferencian a este algoritmo de otros sistemas
generales de inferencia. La primera se basa en la forma en que el esfuerzo requerido para realizar una
tarea de inducción crece con la dificultad de la tarea. El ID3 fue diseñado específicamente para trabajar
con masas de objetos, y el tiempo requerido para procesar los datos crece sólo linealmente con la
dificultad, como producto de:
• la complejidad del concepto a ser desarrollado (medido por la cantidad de nodos en el árbol de
decisión)
Esta linealidad se consigue a costo del poder descriptivo: los conceptos desarrollados por el ID3 sólo
toman la forma de árboles de decisión basados en los atributos dados, y este “lenguaje” es mucho más
restrictivo que la lógica de primer orden o la lógica multivaluada, en la cual otros sistemas expresan sus
conceptos [Quinlan, 1993b].
El ID3 fue presentado como descendiente del CLS creado por Hunt. El ID3, como contrapartida de su
antecesor, es un mecanismo mucho más simple para el descubrimiento de una colección de objetos
pertenecientes a dos o más clases. Cada objeto debe estar descripto en términos de un conjunto fijo de
atributos, cada uno de los cuales cuenta con su conjunto de posibles valores de atributos. Por ejemplo, el
atributo humedad puede tener los valores {alta, baja}, y el atributo clima, {soleado, nublado, lluvioso}.
Una regla de clasificación en la forma de un árbol de decisión puede construirse para cualquier conjunto
C de atributos de esa forma [Quinlan, 1993b]. Si C está vacío, entonces se lo asocia arbitrariamente a
cualquiera de las clases. Si no, C contiene los representantes de varias clases; se selecciona un atributo y
se particiona C en conjuntos disjuntos C1, C2,..., Cn, donde Ci contiene aquellos miembros de C que tienen
el valor i para el atributo seleccionado. Cada una de estos subconjuntos se maneja con la misma
estrategia. El resultado es un árbol en el cual cada hoja contiene un nombre de clase y cada nodo interior
especifica un atributo para ser testeado con una rama correspondiente al valor del atributo.
Dados:
• Un conjunto de datos
Se desea obtener:
• Un árbol de decisión simple basándose en la entropía, donde los nodos pueden ser:
Este procedimiento de formación de reglas funcionará siempre dado que no existen dos objetos
pertenecientes a distintas clases pero con idéntico valor para cada uno de sus atributos; si este caso
llegara a presentarse, los atributos son inadecuados para el proceso de clasificación.
Hay dos conceptos importantes a tener en cuenta en el algoritmo ID3[Blurock, 1996]: la entropía y el
árbol de decisión. La entropía se utiliza para encontrar el parámetro más significativo en la
caracterización de un clasificador. El árbol de decisión es un medio eficiente e intuitivo para organizar los
descriptores que pueden ser utilizados con funciones predictivas.
56 Magdalena Servente Manual del usuario
Algoritmos TDIDT aplicados a la Minería de Datos Inteligente
Función ID3
(R: conjunto de atributos no clasificadores,
C: atributo clasificador,
S: conjunto de entrenamiento) devuelve un árbol de decisión;
Comienzo
Si S está vacío,
devolver un único nodo con Valor Falla;
Si todos los registros de S tienen el mismo valor para el atributo clasificador,
Devolver un único nodo con dicho valor;
Si R está vacío, entonces
devolver un único nodo con el valor más frecuente del atributo clasificador en
los registros de S [Nota: habrá errores, es decir, registros que no estarán bien
clasificados en este caso];
Si R no está vacío, entonces
D ! atributo con mayor Ganancia(D,S) entre los atributos de R;
Sean {dj| j=1,2, .., m} los valores del atributo D;
Sean {Sj| j=1,2, .., m} los subconjuntos de S correspondientes a los valores de
dj respectivamente;
Devolver un árbol con la raíz nombrada como D y con los arcos nombrados d1, d2,
.., dm que van respectivamente a los árboles
ID3(R-{D}, C, S1), ID3(R-{D}, C, S2), .., ID3(R-{D}, C, Sm);
Fin
En el caso de este ejemplo, los árboles y las reglas obtenidos utilizando la ganancia y la proporción de
ganancia son iguales. Se mostrarán ambos ejemplos juntos con fines prácticos.
A partir de todos los datos disponibles, el ID3 analiza todas las divisiones posibles según los distintos
atributos y calcula la ganancia y/o la proporción de ganancia. Comecemos analizando el atributo Estado.
Para calcular la ganancia y, por lo tanto, también la proporción de ganancia, es necesario calcular la
entropía del conjunto. Entonces,
10 10 4 4
H ( S ) = − p Si log 2 p Si − p No log 2 p No = − log 2 − log 2 = 0.86312bits
14 14 14 14
Calculamos ahora la entropía que tendrían los conjuntos resultantes de la división de datos según este
atributo.
2
5 1 4 4 0 4 5 3 2
H ( S , Estado) = ∑ P(S i ) ⋅ H (S i ) =
1 4 0 4 3 2
− log 2 − log 2 + − log 2 − log 2 + − log 2 − log 2
i =1 14 5 5 5 5 14 4 4 4 4 14 5 5 5 5
5 4 5
H ( S , Estado) = × 0.7219 + × 0 + 0.97095 = 0.6046bits
14 14 14
Ahora calculamos la ganancia resultante de dividir al subconjunto según el atributo Estado, tendremos:
Para calcular la proporción de ganancia debemos conocer primero la información de la división que se
calcula como:
Si
= − 5 × log 2 5 − 4 × log 2 4 − 5 × log 2 5 = 1.577bits
n Si
I _ división(S ) = −∑ × log 2
S 14 14 14 14 14
i =1 S 14
Ganancia( S )
proporción _ de _ ganancia( S ) = = 0.491042bits
I _ división( S )
De la misma manera en que calculamos la ganancia y la proporción de ganancia para el caso anterior,
calculamos para el atributo Humedad los siguientes valores:
Una vez que hemos calculado las ganancias y proporciones de ganancia para todos los atributos
disponibles, debemos elegir el atributo según el cual dividiremos a este conjunto de datos. Recordemos
que tanto en el caso de la ganancia como en el de la proporción de ganancia, el mejor atributo para la
división es aquel que la maximiza. En este ejemplo, la división según el atributo Estado es la que mayor
ganancia y proporción de ganancia ofrece. Esto significa que el nodo raíz del árbol será un nodo que
evalúa el atributo Estado.
La figura 4.2 esquematiza la construcción de un árbol de decisión utilizando el ID3 para el conjunto de
datos en cuestión. La figura 4.3 presenta el árbol de decisión obtenido.
Como se explicó en la sección 4.3.1.3 para pasar un árbol de decisión a reglas de decisión, el ID3 lo
recorre en preorden y cada vez que llega a una hoja, escribe la regla que tiene como consecuente el valor
de la misma, y como antecedente, la conjunción de las pruebas de valor especificados en todos los nodos
recorridos desde la raíz para llegar a dicha hoja. Analicemos el pasaje del árbol de la figura 4.3 a reglas de
decisión.
El recorrido del árbol comienza por la raíz Estado, continúa por los nodos Viento y Humedad hasta llegar
a la hoja “SI”. La regla generada para este recorrido será:
Regla 0
SI Estado = Lluvia
Y Viento = Fuerte
Y Humedad = Alta
ENTONCES JuegoTenis = Si
Si seguimos el recorrido preorden, llegamos a continuación a la hoja “NO”, obteniendo en este caso la
siguiente regla:
Regla 1
SI Estado = Lluvia
Y Viento = Fuerte
Y Humedad = Normal
ENTONCES JuegoTenis = No
Lluvia
Estado Soleado
Nublado
Estado Humedad Viento JuegoTenis Estado Humedad Viento JuegoTenis Estado Humedad Viento JuegoTenis
Lluvia Alta Leve Si Nublado Alta Leve Si Soleado Alta Leve No
Lluvia Normal Leve Si Nublado Normal Fuerte Si Soleado Alta Fuerte No
Lluvia Normal Fuerte No Nublado Alta Fuerte Si Soleado Alta Leve No
Lluvia Normal Leve Si Nublado Normal Leve Si Soleado Normal Leve Si
Lluvia Alta Fuerte Si Soleado Normal Fuerte Si
SI
Viento HUMEDAD
Ganancia=0.170951 Humedad
Proporción de ganancia =0.358525 HUMEDAD
Ganancia=0.970951
Proporción de ganancai=2.03632
VIENTO
Ganancia=0.321928
Proporción de ganancia =0.675162 Alta VIENTO
Normal
Ganancia=0.0199731
Proporción de ganancia=0.0418885
Leve
Normal
Alta
Estado
Lluvia Soleado
Nublado
Viento Humedad
SI
SI NO SI
Humedad
Normal
Alta
SI NO
Recorriendo en este sentido el árbol, el resto de las reglas obtenidas se muestran a continuación.
Regla 2
SI Estado = Lluvia
Y Viento = Leve
ENTONCES JuegoTenis = Si
Regla 3
SI Estado = Nublado
ENTONCES JuegoTenis = Si
Regla 4
SI Estado = Soleado
Y Humedad = Alta
ENTONCES JuegoTenis = No
Regla 5
SI Estado = Soleado
Y Humedad = Normal
ENTONCES JuegoTenis = Si
Existen varias maneras de solucionar este problema del ID3, como la agrupación de valores presentada en
[Gallion et al, 1993] o la discretización de los mismos explicada en [Blurock, 1996], [Quinlan, 1993d]. El
C4.5 resolvió el problema de los atributos continuos mediante la discretización, explicada en la Sección
2.4.1.2.
4.3.2.3. Ventanas
El proceso descripto para la construcción de árboles de decisión asume que las operaciones de cálculo,
especialmente, las de evaluación de las frecuencias relativas (en las que se deben contar elementos) del
conjunto C, pueden ser realizadas eficientemente, lo cual significa, en la práctica, que para que el proceso
sea rápido, C debe residir en memoria. ¿Qué pasa si C es tan grande que no cabe en memoria? La
solución aplicada por ID3 es una solución iterativa, que crea sucesivos árboles de decisión de precisión
cada vez mayor, hasta llegar al árbol de decisión óptimo. El método puede resumirse como [Quinlan,
1993b]:
El proceso termina cuando se forma una regla que no tenga excepciones y sea correcta para todo C. Se
han testeado dos métodos para formar la nueva ventana. En el primero, la ventana actual crece por la
adición de excepciones hasta un número especificado. El segundo método trata de identificar los objetos
“claves” en la ventana actual y reemplaza el resto por excepciones. Ambos métodos fueron probados con
un problema de clasificación no trivial de 14 atributos y cerca de 20.000 objetos para los cuales un árbol
de decisión correcto contenía 20 nodos [Quinlan, 1993b]. Las conclusiones obtenidas de estas pruebas
fueron:
• Los métodos convergen rápidamente; generalmente, se precisaron sólo 4 iteraciones para llegar a un
árbol de decisión correcto.
• Fue posible desarrollar un árbol correcto a partir de la ventana final que contenía sólo una pequeña
fracción del total de los objetos
• El tiempo requerido para obtener un árbol de decisión correcto para el problema de clasificación
crece linealmente con la cantidad de ejemplos que están siendo analizados.
4.4. C4.5
El C4.5 se basa en el ID3, por lo tanto, la estructura principal de ambos métodos es la misma. El C4.5
construye un árbol de decisión mediante el algoritmo “divide y reinarás” y evalúa la información en cada
caso utilizando los criterios de entropía y ganancia o proporción de ganancia, según sea el caso. A
continuación, se explicarán las características particulares de este método que lo diferencian de su
antecesor.
Función C4.5
(R: conjunto de atributos no clasificadores,
C: atributo clasificador,
S: conjunto de entrenamiento) devuelve un árbol de decisión;
Comienzo
Si S está vacío,
devolver un único nodo con Valor Falla;
Si todos los registros de S tienen el mismo valor para el atributo clasificador,
Devolver un único nodo con dicho valor;
Si R está vacío, entonces
devolver un único nodo con el valor más frecuente del atributo clasificador en
los registros de S [Nota: habrá errores, es decir, registros que no estarán bien
clasificados en este caso];
Si R no está vacío, entonces
D ! atributo con mayor Proporción de Ganancia(D,S) entre los atributos de R;
Sean {dj| j=1,2, .., m} los valores del atributo D;
Sean {Sj| j=1,2, .., m} los subconjuntos de S correspondientes a los valores de
dj respectivamente;
Devolver un árbol con la raíz nombrada como D y con los arcos nombrados d1, d2,
.., dm que van respectivamente a los árboles
C4.5(R-{D}, C, S1), C4.5(R-{D}, C, S2), .., C4.5(R-{D}, C, Sm);
Fin
1. La prueba “estándar” para los atributos discretos, con un resultado y una rama para cada valor
posible del atributo.
2. Una prueba más compleja, basada en un atributo discreto, en donde los valores posibles son
asignados a un número variable de grupos con un resultado posible para cada grupo, en lugar de para
cada valor.
3. Si un atributo A tiene valores numéricos continuos, se realiza una prueba binaria con resultados A ≤ Z
y A > Z, para lo cual debe determinarse el valor límite Z.
Todas estas pruebas se evalúan de la misma manera, mirando el resultado de la proporción de ganancia, o
alternativamente, el de la ganancia, resultante de la división que producen. Ha sido útil agregar una
restricción adicional: para cualquier división, al menos dos de los subconjuntos Ti deben contener un
número razonable de casos. Esta restricción, que evita las subdivisiones casi triviales es tenida en cuenta
solamente cuando el conjunto T es pequeño.
Sean {v1, v2,. . ., vm} los valores que toma el atributo A. Cualquier valor límite entre vi y vi+1 tendrá el
mismo efecto al dividir los casos entre aquellos cuyo valor para A pertenece al subconjunto {v1, v2,. . .,
vi} y aquellos cuyo valor pertenece a {vi+1, vi+2,. . ., vm}. Entonces, existen sólo m – 1 divisiones posibles
de según el valor de A y todas son examinadas. Al estar ordenados, las sucesivas pruebas para todos los
valores, pueden realizarse en una única pasada.
Típicamente se elige el punto medio del intervalo como valor límite representativo, entonces el iésimo
valor límite sería:
vi + v i +1 (4.6)
2
C4.5 se diferencia de otros algoritmos en que elige el mayor valor de A en todo el conjunto de casos de
entrenamiento que no excede el punto medio presentado, en lugar del punto medio en sí mismo, como
valor límite; de esta manera se asegura que todos los valores límites que aparezcan en el árbol y/o las
reglas ocurran al menos una vez en los datos.
El método utilizado para la binarización de atributos tiene una gran desventaja. Mientras que todas las
operaciones de construcción de un árbol de decisión crecen linealmente con el número de casos de
entrenamiento, el ordenamiento de d valores continuos crece proporcionalmente a d x log(d). Entonces, el
tiempo requerido para construir un árbol a partir de un gran conjunto de datos de entrenamiento, puede
estar dominado por el ordenamiento de datos con valores continuos.
La modificación del criterio de ganancia es bastante directa. La ganancia de una prueba mide la
información sobre la pertenencia a una clase que puede esperarse como resultado de partir un conjunto de
datos de entrenamiento, calculada al restar la información que se espera que sea necesaria para identificar
la clase de un objeto después de la partición a la misma cantidad antes de la partición. Es evidente que
una prueba no puede proveer información de pertenencia a una clase si no se conoce el valor de un
atributo.
Sea T el conjunto de datos de entrenamiento y X una prueba basada en un atributo A, supongamos que el
valor de A se conoce únicamente en una fracción F de casos en T. Sean I(T) e IX(T) calculadas según la
ecuación 2.4, excepto que sólo se tienen en cuenta los casos para los cuales el valor de A es conocido. La
definición de ganancia puede corregirse a:
o, en otras palabras, la ganancia aparente de mirar a los casos con valores conocidos, multiplicada por la
fracción de dichos casos en el conjunto de entrenamiento.
n +1 Ti Ti
I _ división(X ) = −∑ × log 2
i =1 T +1 T + 1
(4.8)
Una prueba puede seleccionar del conjunto de pruebas posibles, como antes, pero utilizando las versiones
modificadas de ganancia e información de la división. Si la prueba X con resultados O1, O2, ..., ON es
escogida y tiene algunos valores desconocidos para algunos de los datos de entrenamiento, el concepto de
particionamiento debe ser generalizado, según un criterio probabilístico.
Cuando un caso T con un resultado conocido Oi es asignado al subconjunto Ti, esto significa que la
probabilidad de que el caso pertenezca a Ti es 1 y de que pertenezca a todos los otros subconjuntos es 0.
Cuando el resultado es desconocido, sólo se puede realizar una afirmación estadística más débil.
Entonces, se asocia con cada caso del subconjunto Ti un peso representando la probabilidad de que el caso
pertenezca a cada subconjunto. Si el resultado para el caso es conocido, entonces el peso es 1; si el caso
tiene un resultado desconocido, entonces el peso es simplemente la probabilidad del resultado Oi en este
punto. Cada subconjunto Ti es una colección de casos fraccionales posibles, tal que |Ti| debe ser
reinterpretada como la suma de los pesos fraccionales de los casos pertenecientes al subconjunto.
Los casos de entrenamiento en T pueden tener pesos no unitarios, ya que T puede ser el resultado de una
partición previa. Entonces, en general, un caso de T con peso p cuyo resultado no se conoce, es asignado a
cada subconjunto Ti con peso:
P x probabilidad_de_resultado_Oi (4.9)
La probabilidad_de_resultado_Oi se estima como la suma de los pesos de los casos en T con valores
conocidos que tienen resultado Oi, sobre la suma de los pesos de los casos en T con resultado conocidos
para la prueba.
Se toma un enfoque similar cuando el árbol de decisión es utilizado para clasificar un caso. Si en un nodo
de decisión el atributo relevante no se conoce, de manera tal que el resultado de la prueba no puede
determinarse, el sistema explora todos los resultados posibles y combina aritméticamente las
clasificaciones resultantes. Como para cada atributo pueden existir múltiples caminos desde la raíz del
árbol hasta las hojas, una “clasificación” es una distribución de clases más que una única clase. Cuando la
distribución de clases total para un caso nuevo ha sido establecida de esta manera, la clase con la
probabilidad más alta, es asignada como “la” clase predicha.
Cada hoja en el árbol de decisión resultante tiene asociados dos valores: (N/E). N es la suma de los casos
fraccionales que llegan a la hoja; y E es el número de casos cubiertos por la hoja, que no pertenecen a la
clase de la misma.
Para entender este problema, supongamos que tenemos un conjunto de datos dos clases, donde una
proporción p ≥ 0.5 de los casos pertenecen a la clase mayoritaria. Si un clasificador asigna todos los casos
con valores indeterminados a la clase mayoritaria, la proporción esperada de error es claramente 1 – p. Si,
en cambio, el clasificador asigna un caso a la clase mayoritaria con probabilidad p y a la otra clase con
probabilidad 1 - p, su proporción esperada de error es la suma de:
• la probabilidad de que un caso perteneciente a la clase mayoritaria sea asignado a la otra clase, p x (1
– p), y
• la probabilidad de que un caso perteneciente a la otra clase sea asignado a la clase mayoritaria, (1 –
p) x p
que da como resultado 2 x p (1 – p). Como p es al menos 0.5, esto es generalmente superior a 1 – p,
entonces el segundo clasificador tendrá una mayor proporción de errores. Un árbol de decisión complejo
tiene una gran similitud con este segundo tipo de clasificador. Los casos no se relacionan a una clase,
entonces, el árbol manda cada caso al azar a alguna de las hojas.
Un árbol de decisión no se simplifica borrando todo el árbol a favor de una rama, sino que se eliminan las
partes del árbol que no contribuyen a la exactitud de la clasificación para los nuevos casos, produciendo
un árbol menos complejo, y por lo tanto, más comprensible.
El primer enfoque, conocido como pre-poda, tiene la ventaja de que no se pierde tiempo en construir una
estructura que luego será simplificada en el árbol final. Los sistemas que lo aplican, generalmente buscan
la mejor manera de partir el subconjunto y evalúan la partición desde el punto de vista estadístico
mediante la teoría de la ganancia de información, reducción de errores, etc. Si esta evaluación es menor
que un límite predeterminado, la división se descarta y el árbol para el subconjunto es simplemente la
hoja más apropiada. Sin embargo, este tipo de método tiene la desventaja de que no es fácil detener un
particionamiento en el momento adecuado, un límite muy alto puede terminar con la partición antes de
que los beneficios de particiones subsiguientes parezcan evidentes, mientras que un límite demasiado bajo
resulta en una simplificación demasiado leve.
El C4.5 utiliza el segundo enfoque, el método de divide y reinarás procesa los datos de entrenamiento
libremente, y el árbol sobreajustado producido es podado después. Los procesos computacionales extras
invertidos en la construcción de partes del árbol que luego serán podadas pueden ser sustanciales, pero el
costo no supera los beneficios de explorar una mayor cantidad de particiones posibles. El crecimiento y
la poda de los árboles son más lentos, pero más confiables.
La poda de los árboles de decisión llevará, sin duda, a clasificar erróneamente una mayor cantidad de los
casos de entrenamiento. Por lo tanto, las hojas de un árbol podado no contendrán necesariamente una
única clase sino una distribución de clases, como se explicó con anterioridad. Asociado a cada hoja, habrá
una distribución de clases especificando, para cada clase, la probabilidad de que un caso de entrenamiento
en la hoja pertenezca a dicha clase.
Supongamos que fuera posible predecir la proporción de errores de un árbol y sus subárboles. Esto
inmediatamente llevaría al siguiente método de poda: “Comenzar por las hojas y examinar cada subárbol.
Si un reemplazo del subárbol por una hoja o por su rama más frecuentemente utilizada, lleva a una
proporción de errores predicha (predicted error rate) menor, entonces podar el árbol de acuerdo a ello,
recordando que las proporciones de errores predichas para todos los subárboles que lo contienen se verán
afectadas”. Como la proporción de errores predicha para un árbol disminuye si disminuyen las
proporciones de errores predichas en cada una de sus ramas, este proceso generaría un árbol con una
proporción de errores predicha mínima.
¿Cómo podemos predecir la proporción de errores? Está claro que calcular la proporción de errores a
partir de los datos de entrenamiento para los cuales el árbol fue construido, no es un estimador útil, ya que
en lo que respecta al conjunto de entrenamiento, la poda siempre aumenta la proporción de errores.
Existen dos familias de técnicas para predecir la proporción de errores. La primer familia predice la
proporción de errores de un árbol y sus subárboles utilizando un nuevo conjunto de casos distinto del
conjunto de entrenamiento. Como estos casos no fueron examinados durante la construcción del árbol, los
estimadores obtenidos a partir de ellos son insesgados y, de existir suficientes casos, confiables. Ejemplos
de esta familia son:
• Poda según la complejidad del costo (Cost-complexity pruning) [Breinman et al, 1984] en la cual la
proporción de errores predicha para un árbol se modela como la suma ponderada de su complejidad y
sus errores en los casos de entrenamiento, con los casos extras utilizados para determinar los
coeficientes de la ponderación.
La desventaja de esta familia de técnicas es simplemente que una parte del conjunto de datos
preclasificados a partir de los que se construyó el árbol debe guardarse para determinar los errores,
entonces el árbol debe ser construido a partir de un conjunto de datos más pequeño. Una solución a este
problema es utilizar un enfoque de referencias cruzadas. Este consiste en dividir los casos disponibles en
bloques iguales de tamaño C y, para cada bloque, un árbol se construye con todos los otros bloques y se
miden los errores con el bloque elegido. Para valores moderados de C, se asume que el árbol construido a
partir de todos los bloques menos uno será muy similar al construido a partir de todos los datos. Por
supuesto que deben construirse C árboles y no uno solo.
El enfoque tomado por el C4.5 pertenece a la segunda familia de técnicas que utilizan únicamente el
conjunto de entrenamiento a partir del cual se construyó el árbol. La estimación de la proporción de
errores pura se ajusta para reflejar su propia tendencia. El método utilizado por el C4.5 se describe a
continuación.
Cuando una hoja cubre N casos de entrenamiento, E de ellos en forma errónea, el estimador de la
proporción de errores de resubstitución para dicha hoja es N/E. Podemos entender esto de manera naif
como E “eventos” en N pruebas. Si el conjunto de N casos de entrenamiento se tomase como una muestra
(lo cual, por supuesto, no es cierto), nos podríamos preguntar qué nos dice este resultado acerca de la
probabilidad de un evento (error) en la totalidad de la población de casos cubiertos por la hoja. La
probabilidad de error no puede determinarse de forma exacta, pero cuenta con límites de confianza. Para
un límite de confianza CF, el límite superior de esta probabilidad puede encontrarse a partir de los límites
de confianza para la distribución binomial; el límite superior se expresa como UCF(E,N). Como en la
distribución binomial los límites superior e inferior son simétricos, la probabilidad de que el promedio
real de errores exceda UCF(E,N)es CF/2. El C4.5 simplemente iguala el estimador de error predicho de la
hoja con su límite superior, bajo el argumento de que el árbol fue construido para minimizar la
proporción de error observada. Aunque los fundamentos de esta heurística son cuestionables y violan
algunos principios estadísticos, las estimaciones producidas presentan frecuentemente resultados
aceptables.
Para simplificar el cálculo, las proporciones de error para las hojas y subárboles se computan asumiendo
que fueron utilizados para clasificar un conjunto de nuevos casos del mismo tamaño del conjunto de
entrenamiento. Entonces, una hoja que cubre N casos de entrenamiento con un estimador de error
predicho de UCF(E,N) generaría N x UCF(E,N) errores predichos. Análogamente, la cantidad de errores
predichos asociados con un (sub)árbol es la suma de los errores predichos para cada una de sus ramas.
La suma de los errores predichos en las hojas, dividido el número de casos de entrenamiento, es un
estimador inmediato del error de un árbol podado sobre nuevos casos.
Este es el mismo conjunto de datos que fue utilizado en la sección 4.3.1.5 para construir un árbol
utilizando el ID3 con la diferencia que es el valor del atributo Estado para el primer caso es desconocido.
Primero calculamos la entropía del conjunto. Recordemos que, como se explicó en la sección 4.4.2.2, no
debemos tener en cuenta los atributos desconocidos. Entonces, trabajamos sobre un total de 13 casos, de
los cuales 3 son positivos. Tendremos,
3 3 10 10
H (S ) = − log 2 − log 2 = 0.7793bits
13 13 13 13
Calculamos ahora la entropía que tendrían los conjuntos resultantes de la división de datos según este
atributo.
4 2 2 2 2 4 0 0 4 4 5 1 1 4 4
H ( S , Estado) = − log 2 − log 2 + − log 2 − log 2 + − log 2 − log 2 = 0.58536bits
13 4 4 4 4 13 4 4 4 4 13 5 5 5 5
Ahora calculamos la ganancia resultante de dividir al subconjunto según el atributo Estado, tendremos:
Al calcular al información de la división, debemos tener en cuenta una categoría extra para el valor
desconocido para el atributo. La información de la división se calcula como:
Ganancia( S )
proporción _ de _ ganancia( S ) = = 0.098bits
I _ división( S )
De la misma manera en que calculamos la ganancia y la proporción de ganancia para el caso anterior,
calculamos para el atributo Humedad los siguientes valores:
Al igual que con el ID3, conviene dividir el conjunto según el atributo Estado tanto si trabajamos con la
ganancia como si trabajamos con la proporción de ganancia. Al dividir los 14 casos para continuar con la
construcción del árbol, los 13 casos para los que el valor de Estado es conocido, no presentan problemas y
se reparten según el valor de Estado. Mientras que el caso en que no se conoce el valor de Estado, se
reparte entre los conjuntos que tienen Soleado, Nublado y Lluvia con los pesos 4/13, 4/16 y 5/13
respectivamente.
Tomemos por ejemplo, la división de los datos para el valor Nublado del atributo Estado. Los datos que
se tienen en cuenta en este caso son:
En este caso, vemos que la división del conjunto de datos no ofrece ninguna mejora, por lo tanto,
colapsamos el árbol a la hoja Si, que es la que mayor peso tiene. La cantidad de casos cubiertos por la
hoja, es decir, el N asociado a la misma, es 4.3. Y la cantidad de casos cubiertos incorrectamente, o el E
asociado a la hoja, por la hoja son 0.3.
La figura 4.4 muestra un esquema de todos los pasos para la construcción del árbol de decisión en este
caso. A continuación se muestra el árbol obtenido.
Estado
Lluvia Soleado
Estado Humedad Viento Juego Peso Estado Humedad Viento Juego Peso
Tenis Tenis
? Alta Leve No 5/13 ? Alta Leve No 4/13
Lluvia Alta Leve Si 1 Nublado Soleado Alta Fuerte No 1
Lluvia Normal Leve Si 1 Soleado Alta Leve No 1
Lluvia Normal Fuerte No 1 Soleado Normal Leve Si 1
Lluvia Normal Leve Si 1 Soleado Normal Fuerte Si 1
Lluvia Alta Fuerte Si 1
HUMEDAD
HUMEDAD Ganancia=0.996
SI Ganancia=0.029 Estado Humedad Viento Juego Peso Proporción de ganancia =1
N=5.4 Proporción de ganancia =0.029
Tenis
E=1.4 ? Alta Leve No 4/13
VIENTO
VIENTO Ganancia=0.003
Ganancia=0.130 Nublado Alta Leve Si 1 Proporción de ganancia =0.00301
Proporción de ganancia =0.136
Nublado Normal Fuerte Si 1
Nublado Alta Fuerte Si 1
Nublado Normal Leve Si 1 Humedad
SI HUMEDAD
N=4.3 Ganancia=0.068
Proporción de ganancia =0.068
E=0.3 Alta Normal
VIENTO
Ganancia=0.068
Proporción de ganancia =0.068
SI SI
N=2.3 N=2
E=0 E=0
Recordemos que el C4.5 analiza los errores predichos en cada uno de los subárboles y ramas del árbol
generado para analizar si es conveniente simplificarlo. En este caso, el error total predicho para el árbol
estará dado por:
Error _ predicho( Arbol ) = 4.3 × U 25% (0.3,4.3) + 5.4 × U 25% (1.4,5.4) + 2.3 × U 25% (0,2.3) + 2 × U 25% (0,2)
Ahora, calculamos el error total predicho de simplificar el árbol por la hoja “Si”:
El error predicho para el árbol simplificado es menor que el error predicho para el árbol generado.
Entonces, el C4.5 poda el árbol a la siguiente hoja:
Si (14.0/5.76)
Para decidir cuándo una condición debe eliminarse, utilizaremos el siguiente método. Sea R una regla de
la forma:
si A entonces clase C
si A- entonces clase C,
• Y1+E1: casos que satisfacen A- y también X, por lo tanto, también están cubiertos por la regla original
R, E1 de ellos erróneamente ya que pertenecen a clases distintas a C.
• Y2+E2: casos que satisfacen A- pero no X que serán cubiertos por la regla generalizada R- pero no por
la regla original. E2 de estos casos serán clasificados erróneamente.
De acuerdo a varios experimentos desarrollados por Quinlan [Quinlan, 1987] para medir la importancia
de la tabla de contingencia al decidir si una condición X debe ser eliminada o no, se encontró que se
obtienen mejores resultados utilizando una estimación pesimista de la precisión de las reglas R y R- sobre
nuevos casos. No es muy probable que una hoja que cubre N casos con E errores tenga una proporción de
error tan baja como E/N al clasificar nuevos casos. En lugar de utilizar el estimador E/N al estimar la
proporción real de errores de una hoja como el límite superior UCF(E,N) del intervalo de confianza para
algún nivel de confianza CF. Si reemplazamos estos valores por los de las reglas R y R- obtendremos los
siguientes estimadores pesimistas:
Si
U CF ( E1 + E 2 , Y1 + Y2 + E1 + E 2 ) ≤ U CF ( E1 , Y1 + E1 ) (4.10)
Entonces
Durante el proceso de generalización será necesario eliminar más de una condición. En lugar de analizar
todos los subconjuntos posibles de condiciones que podrían eliminarse, el sistema de C4.5 realiza una
eliminación directa golosa (straightforward greedy elimination): De todas las reglas que pueden
eliminarse por el método descripto, se elimina aquella que produce la menor proporción pesimista de
error en la regla generalizada. Como en todos las búsquedas golosas el hecho de buscar el mínimo en cada
paso no nos asegura llegar al mínimo global.
plantea una solución simple: ordenar las reglas y la primera regla que cubre el caso se toma como la regla
operativa. Es necesario, entonces, establecer prioridades para el ordenamiento de las reglas y decidir la
clasificación por defecto a utilizar.
Para establecer las prioridades se siguió un método propuesto por Michie que determina que todas las
reglas de una misma clase deben aparecer juntas y estos subconjuntos de clases son los que están
ordenados en lugar de las reglas en sí. Este agrupamiento hace que las reglas sean más entendibles y tiene
la ventaja que el ordenamiento de las reglas en particular no es importante.
Supongamos que del conjunto de reglas elegimos un subconjunto S de reglas que cubren la clase C. La
performance de este subconjunto puede medirse mediante el número de casos de entrenamiento cubiertos
por S que no pertenecen a la clase C (falsos positivos) y el número de casos de entrenamiento de la clase
C que no son cubiertos por ninguna regla de S (falsos negativos).El valor del subconjunto S se mide
utilizando el Principio de Longitud de Descripción Mínima [Rissanen, 1983], explicado en la sección
2.4.1.4. Recordemos que este principio puede expresarse de la siguiente manera: Un Emisor y un
Receptor cuentan con copias idénticas de un conjunto de casos de entrenamiento, pero los casos del
Emisor también especifican la clase de cada caso, mientras que los casos del Receptor no tienen
información de las clases. El Emisor debe comunicar esta información faltante al Receptor mediante la
transmisión de una teoría de clasificación junto con las excepciones a la misma. El Emisor puede elegir la
complejidad de la teoría que envía (una teoría relativamente simple con muchas excepciones, o una teoría
muy compleja con pocas excepciones). El Principio MDL afirma que la mejor teoría derivable de los
datos de entrenamiento minimizará la cantidad de bits necesarios para codificar el mensaje completo
consistente de la teoría y sus excepciones.
1. Para codificar una regla, debemos especificar cada antecedente. El consecuente no necesita ser
codificado, porque todas las reglas del subconjunto pertenecen a la misma clase C. Existe una
pequeña complicación: las condiciones deben enviarse en algún orden, pero el orden no importa
porque las condiciones pertenecen a una conjunción. Si existen x condiciones en el antecedente,
existen x! ordenamientos posibles que podrían enviarse, todos equivalentes del punto de vista de la
especificación de la regla. Por lo tanto, la cantidad de bits requerida para enviar cualquier
ordenamiento en particular debe ser reducida en un “crédito” de log2(x!).
2. La codificación de un conjunto de reglas requiere la suma de los bits para codificar cada regla, menos
un crédito similar para el ordenamiento de las reglas (ya que todos los ordenamientos de reglas para
una misma clase son equivalentes)
3. Las excepciones se codifican indicando cuáles de los casos cubiertos por las reglas S son falsos
positivos y cuáles falsos negativos. Si las reglas cubren r de los n casos de entrenamiento, con fp
falsos positivos y fn falsos negativos, la cantidad de bits necesarios para codificar la excepción es
r n − r
log 2 + log 2
fp fn (4.11)
El primer término indica los bits necesarios para indicar los falsos positivos entre los casos cubiertos por
las reglas y el segundo término indica los falsos negativos entre los casos no cubiertos por las reglas.
El valor de un subconjunto S en particular se mide con la suma de las longitudes de codificación para las
reglas y excepciones, a menor suma, mejor teoría.
En la práctica, los métodos de codificación tienden a sobrestimar la cantidad de bits requeridos para
codificar una teoría relativa al conjunto de excepciones. Esto se explica por el hecho de que los conjuntos
de atributos generalmente son redundantes, por lo que diferentes teorías pueden ser funcionalmente
idénticas. Como la función de una teoría para una clase es identificar un subconjunto de casos de
entrenamiento, diferentes reglas que identifiquen al mismo conjunto son intercambiables, aún cuando
hayan sido codificadas de manera distinta. Para compensar este efecto, el sistema utiliza la suma
ponderada:
donde W < 1.
El valor apropiado de W dependerá de la probabilidad de que dos teorías representen los mismos casos, lo
cual dependerá del grado de redundancia en los datos. C4.5 utiliza el valor 0.5 por defecto para W, pero
puede ajustarse a un valor menor si se encuentra un gran grado de redundancia en los datos. Sin embargo,
no se ha encontrado que el resultado del algoritmo dependa en gran medida del valor de W.
Entonces, para enviar las reglas debe encontrarse un subconjunto S de reglas para la clase C que minimice
esta codificación total. Esto es similar a la generalización de reglas descripta anteriormente, pero en este
caso la eliminación golosa no parece ser efectiva. En cambio, el sistema analiza todos los subconjuntos
posibles de reglas para una clase, si no son demasiados, y utiliza recocido simulado (simulated annealing)
en caso contrario. En este último caso, el sistema repetidamente elige una regla al azar y considera
incluirla en el subconjunto S (si aún no pertenece al mismo), o eliminarla de S (si ya pertenece). Esta
acción producirá un cambio ∆B en el total de bits necesario para codificar el subconjunto y las
excepciones y, si el caso es beneficioso, entonces se lo acepta inmediatamente. Si la acción incrementa la
longitud total de la codificación tal que ∆B es positivo, el cambio se acepta con una probabilidad de e-∆B/K
donde K es una especia de temperatura sintética. Al reducir gradualmente el valor de K al ir explorando
los cambios, el sistema tiende a converger a un conjunto de reglas con una codificación cerca del mínimo
(near-minimun encoding).
Al decidir el ordenamiento de las clases es importante tener en cuenta los falsos positivos ya que
ocasionarán clasificaciones incorrectas. Entonces, a la hora de decidir sobre el ordenamiento, se elige
primero a la clase que tiene menos falsos positivos. Luego, los falsos positivos de los casos de
entrenamiento que aún no han sido seleccionados se recomputan y se vuelve a elegir la clase con menos
falsos positivos, y así sucesivamente.
Como la clase por defecto será utilizada cuando un caso no sea cubierto por ninguna de las reglas, éstas
reglas deberían tenerse en cuenta para determinar cuál será la clase por defecto. El C4.5 elige como clase
por defecto aquella clase que cubre la mayoría de los casos de entrenamiento no cubiertos por ninguna
regla, resolviendo empates a favor de la clase con la mayor frecuencia absoluta.
Una vez que se ha determinado el ordenamiento y la clase por defecto, el conjunto de reglas se examina
por última vez. Si existe alguna regla cuya eliminación reduzca el número de errores de clasificación, se
la elimina y se recomputan los errores. El conjunto vuelve a chequearse. Este paso fue diseñado para
evaluar el conjunto de reglas en la forma en que será utilizado.
Regla 1
SI Estado = Soleado
Y Humedad = Alta
ENTONCES JuegoTenis = No
Regla 2
SI Estado = Soleado
Y Humedad = Normal
ENTONCES JuegoTenis = Si
Regla 3
SI Estado = Nublado
ENTONCES JuegoTenis = Si
Regla 4
SI Estado = Lluvia
ENTONCES JuegoTenis = Si
A continuación, el C4.5 generaliza cada una de estas reglas, eliminando aquellas condiciones que generan
un estimador de error pesimístico mayor. Calculamos este estimador para cada una de las reglas
presentadas y para las reglas resultantes de eliminar cada una de sus condiciones.
Errores Cant. de casos cubiertos Estimador pesimístico del error Condición ausente
0 2 50% <regla actual>
4 7 75.5% Estado=Soleado
2 4 77.1% Humedad=Alta
Las reglas resultantes de eliminar cualquiera de las dos condiciones del antecedente, tienen un estimador
pesimístico de error superior al de la regla actual, con lo cual no es conveniente eliminar ninguna de las
dos condiciones. Mantenemos, entonces, la regla tal como fue generada, agregándole la precisión de la
misma.
Regla 1
SI Estado = Soleado
Y Humedad = Alta
ENTONCES JuegoTenis = No [50%]
Repetimos estos cálculos para las reglas restantes. En el caso de la regla 2 tendremos:
Errores Cant. De casos cubiertos Estimador pesimístico del error Condición ausente
0 2 50% <regla actual>
1 7 33.8% Estado=Soleado
2 4 77.1% Humedad=Normal
En este caso, la regla resultante de eliminar la primera condición tiene un estimador pesimístico del error
menor que el de la regla actual, entonces, eliminamos esta condición y repetimos los cálculos,
obteniendo:
Errores Cant. De casos cubiertos Estimador pesimístico del error Condición ausente
1 7 33.8% <regla actual>
4 14 41.3% Humedad=Normal
Regla 2
SI Estado = Soleado
Y Humedad = Normal
ENTONCES JuegoTenis = Si [66.2%]
Errores Cant. De casos cubiertos Estimador pesimístico del error Condición ausente
0 4 29.3% <regla actual>
4 14 41.3% Estado=Nublado
Regla 3
SI Estado = Nublado
ENTONCES JuegoTenis = Si [70.7%]
Errores Cant. De casos cubiertos Estimador pesimístico del error Condición ausente
1 5 45.4% <regla actual>
4 14 41.3% Estado=Lluvia
Regla 4
SI Estado = Lluvia
ENTONCES JuegoTenis = Si [54.6%]
Una vez que todas las reglas han sido generalizadas, el C4.5 agrupa las reglas según la clase de su
consecuente y busca los subconjuntos de reglas que generan una codificación mínima para la clase.
Entonces, calcula para cada subconjunto de reglas la cantidad de bits necesarios para codificar las reglas,
y utiliza el método del recocido simulado para determinar cuáles reglas son convenientes utilizar para
representar cada clase. En este caso, las reglas escogidas son la regla 1 para la clase No y la regla 3 para
la clase Si.
Finalmente, el C4.5 ordena las reglas y escoge la clase por defecto. Para ello, primero debemos computar
los falsos positivos de cada una de las reglas para escoger la de menor falsos positivos como primera. En
este caso, los falsos positivos para ambas reglas son nulos. Con lo cual, mantenemos el orden en que
fueron generadas.
Para la elección de la clase por defecto, computamos la cantidad de casos de cada clase no cubiertos por
las reglas escogidas. A continuación se presentan la cantidad de casos de cada clase, especificándose la
cantidad de casos de cada clase no cubiertos por ninguna de las reglas escogidas.
Como la mayoría de los casos no cubiertos, pertenecen a la clase Si, ésta es escogida como clase por
defecto. Las reglas finales para este ejemplo se presentan a continuación.
Regla 1
SI Estado = Soleado
Y Humedad = Alta
ENTONCES JuegoTenis = No [50.0%]
Regla 3
SI Estado = Nublado
ENTONCES JuegoTenis = Si [70.7%]
Regla 5
Clase por defecto = Si
ID3
DATOS DE
ENTRENAMIENTO DATOS DE
PRUEBA
C4.5
Árbol de Transformación a
Generación del Árbol de Poda del árbol Reglas de
decisión reglas de
árbol de decisión decisión de decisión decisión
podado decisión
Evaluación del
Evaluación de los
modelo
resultados
generado
DATOS DE
ENTRENAMIENTO
1
Generación del
árbol de decisión
2
Poda del árbol
de decisión
3 4
Impresión del Transformación a
árbol de decisión reglas de
en pantalla decisión
DATOS
TRANSFORMACION
5
Guardado del
árbol de decisión
a disco
6
Evaluación de los
resultados
ÁRBOL DE
DECISIÓN
Matriz de confusión
Cada uno de los procesos mostrados en la figura 4.6 se describe en las secciones siguientes.
Este proceso genera el árbol de decisión según el algoritmo explicado para el ID3 en la sección 4.3.1.1. El
árbol se construye a partir de los datos de entrenamiento en formato Paradox, seleccionados por el
usuario.
Durante este proceso se genera un archivo de log que guarda todos los cálculos de ganancia y de
proporción de ganancia realizadas en cada paso; y el atributo escogido para realizar la división. El archivo
de log se encuentra en el subdirectorio Log dentro del directorio donde está instalado el programa, y su
nombre está compuesto de la siguiente manera: MétodoDD-MM-AA HH_MM_SSNombreTabla.log. Esto
permite identificar fácilmente el log correspondiente a una corrida. El formato del archivo de log se
especifica en la sección 4.5.2.1.8.
Este proceso realiza la poda del árbol de decisión según la metodología explicada en la sección 4.3.1.2.
Una vez podado, el árbol de decisión se muestra en pantalla, de forma tal que figure la prueba realizada
en cada nodo y el valor de la prueba en cada rama, como aparece en la figura 4.7.
A partir del árbol de decisión podado, se generan las reglas de decisión de acuerdo con el método
explicado en la sección 4.3.1.3. Se guarda en disco un archivo de texto que contiene las reglas obtenidas,
y un archivo de Paradox que contiene las sentencias SQL equivalentes. También se genera otra tabla de
Paradox, “Datos Transformación”, que contiene los datos de necesarios para identificar la transformación
correspondiente a cada sentencia SQL.
A partir del árbol de decisión podado, se generan las reglas de decisión de acuerdo con el método
explicado en la sección 4.3.1.3. Se guarda en disco un archivo de texto que contiene las reglas obtenidas,
y un archivo de Paradox que contiene las sentencias SQL equivalentes. También se genera otra tabla de
Paradox, “Datos Transformación”, que contiene los datos de necesarios para identificar la transformación
correspondiente a cada sentencia SQL.
Una vez presentado el árbol por pantalla, se le ofrece al usuario la posibilidad de guardarlo en el disco
rígido. Si acepta, debe elegir en qué directorio y con qué nombre desea guardarlo. La extensión del
archivo resultante es “tree”.
A partir de una lista de corridas realizadas, el usuario elige cuál corrida del ID3 desea evaluar. Los datos
de las corridas previas están almacenados en la tabla de “Datos Transformación”. Una vez realizada la
elección, se obtienen de la tabla de “Sentencias SQL” las sentencias SQL generadas durante la
transformación del árbol a reglas de decisión (Sección 4.5.2.1.4). Estas sentencias se aplican al conjunto
de datos de prueba determinado por el usuario.
Se genera una matriz de confusión que indica para cada clase la cantidad de casos correctamente
clasificados y los incorrectamente clasificados. Se calcula la probabilidad de que un caso sea clasificado
correctamente como perteneciente a esa clase. Finalmente, se obtienen los totales generales. La matriz de
confusión obtenida se muestra en pantalla. La Figura 4.8 muestra un ejemplo de dicha matriz de
confusión.
Los datos de entrenamiento y prueba deben estar en formato de Paradox 3 (.db o .dbf) para poder ser
procesados por el sistema. Se eligió este formato porque la mayoría de los programas de manejo de datos
pueden exportar sus archivos y tablas al formato de Paradox 3; lo cual permite analizar datos
alamacenados en casi cualquier formato.
Durante la generación del árbol de decisión, se genera un archivo de log que almacena todos los cálculos
realizados. En cada paso, se almacenan:
• Una matriz con la cantidad de instancias pertenecientes a cada clase para cada valor posible del
descriptor.
• El valor de la ganancia
3. La ganancia o la proporción de ganancia máxima (según el criterio de división con el que se esté
trabajando).
Nivel=0
Descriptor=Estado
Lluvia Nublado Soleado
No 1 0 3
Si 4 4 2
5 4 5 14
Gain=0.258521
Gain Ratio=0.491042
--------------------------------------
Descriptor=Humedad
Alta Normal
No 3 1
Si 4 6
7 7 14
Gain=0.0746702
Gain Ratio=0.14934
--------------------------------------
Descriptor=Viento
Fuerte Leve
No 2 2
Si 4 6
6 8 14
Gain=0.00597769
Gain Ratio=0.0122457
--------------------------------------
0.258521
El archivo de reglas de decisión generado durante la transformación a reglas de decisión contiene las
reglas para el árbol analizado. Por cada regla guarda el número de regla y la regla en sí.
Cuando el usuario elige guardar el árbol de decisión a disco, este se almacena en un archivo plano,
tabulándose n veces cada renglón, siendo n el nivel del nodo de decisión. A continuación se muestra el
contenido de un archivo “.tree” a modo de ejemplo.
Estado = Lluvia
Viento = Fuerte
Humedad = Alta
Si
Humedad = Normal
No
Viento = Leve
Si
Estado = Nublado
Si
Estado = Soleado
Humedad = Alta
No
Humedad = Normal
Si
DATOS DE
ENTRENAMIENTO
1
Generación del
árbol de decisión
2
Árbol de decisión sin podar ÁRBOL DE
Poda del árbol
DECISIÓN Y
de decisión
EVALUACIÓN
DE
RESULTADOS
Árbol de decisión podado
Árbol de decisión podado
5
3
Transformación a
Evaluación del
reglas de
árbol de decisión
decisión
REGLAS DE
DATOS DE
Árbol de decisión podado Reglas de decisión DECISIÓN Y
PRUEBA
y Resultados de la evaluación EVALUACIÓN
DE
RESULTADOS
6
4
Evaluación de las
Impresión del
reglas de
árbol de decisión
decisión
en pantalla
La figura 4.9 presenta el DFD general para la sección del C4.5. A simple vista, vemos que el diseño de
este proceso difiere ligeramente del diseño del ID3. Aunque los procesos principales (generación y poda
del árbol de decisión, generación de las reglas de decisión, evaluación de los resultados) se mantienen,
encontramos que hay dos evaluaciones de resultados, una para el árbol de decisión y otra para las reglas.
Esto se debe a que, a diferencia del ID3, las reglas de decisión y el árbol generados por el C4.5 no son
modelos de clasificación equivalentes. Por otro lado, encontramos que tanto el archivo del árbol de
decisión como el de las reglas de decisión se presentan junto a la evaluación de resultados. Recordemos
que, como se explicó en la sección 4.4, el C4.5 evalúa cada rama y cada regla en particular. Por lo tanto,
la evaluación no puede separarse de los modelos generados.
Cada uno de los procesos mostrados en la figura 4.9 se describe en las secciones siguientes.
Este proceso genera el árbol de decisión según el algoritmo explicado para el C4.5 en las secciones 4.4.1
y 4.4.2. El árbol se construye a partir de los datos de entrenamiento en formato Paradox, seleccionados
por el usuario.
Este proceso realiza la poda del árbol de decisión según la metodología explicada en la sección 4.4.3.
El árbol de decisión se evalúa según el algoritmo explicado en la sección 4.4.4. Se le agrega una prueba
de valor a cada rama y se genera una matriz de confusión a partir de la performance del árbol sobre los
datos de prueba.
El árbol de decisión se muestra en pantalla para el usuario, de manera idéntica al árbol generado para el
ID3, explicado en la sección 4.5.2.1.3.
Una vez generadas las reglas de decisión, el sistema las evalúa contra los datos de prueba. Genera una
apreciación de exactitud para cada regla, según lo explicado en la sección 4.4.6, y una matriz de
confusión general. Almacena todos los resultados en un archivo plano.
Al igual que en el caso del ID3, los datos de entrenamiento y prueba deben estar en formato de Paradox 3
(.db o .dbf) para poder ser procesados por el sistema.
Durante la generación del árbol de decisión, su correspondiente poda y su posterior evaluación se genera
un archivo de resultados obtenidos en cada paso. En este archivo se almacenan:
1. el árbol de decisión sin podar, donde cada rama tiene asociados dos números: N y E, cuyo sentido se
detalla a continuación:
• E es la cantidad de errores predichos, según la distribución binomial, para la hoja si una cantidad
N de nuevos casos fuese evaluada por el árbol
Errores = x (y%)
Con:
∑E i
Estimación = i =1
donde:
4. una matriz de confusión que indica para cada clase, la cantidad de casos clasificados erróneamente y
la cantidad de casos de la misma clasificados como pertenencientes a otra clase.
Opciones:
Datos <cardiolo>
Los árboles serán evaluados sobre los datos de prueba
Se utilizará el Gain criterion
Árbol de decisión:
3. Matriz de confusión para los datos de entrenamiento, con el mismo formato que la presentada en la
sección 4.5.2.2.8 para la evaluación de los resultados del árbol de decisión.
4. Evaluación de los datos de prueba con el mismo formato que la evaluación sobre los datos de
entrenamiento.
5. Matriz de confusión para los datos de prueba con el mismo formato que la matriz presentada para los
datos de entrenamiento.
Opciones:
Datos <cardiolo>
Conjuntos de reglas evaluados sobre casos
nuevos
------------------
Procesando el árbol 0
Regla 6:
Dolor de Pecho de Angor = AUSENTE
-> clase NO [95.8%]
Regla 1:
Duración del Angor = MENOS DE 30 MIN
-> clase NO [94.6%]
Regla 5:
Regla 2:
Dolor de Pecho de Angor = TIPICO
Duración del Angor = MAS DE 30 MIN
-> clase SI [84.3%]
Regla 3:
Dolor de Pecho de Angor = ATIPICO
Irradiación del Angor = SI
Duración del Angor = MAS DE 30 MIN
-> clase SI [84.1%]
Regla 4:
Dolor de Pecho de Angor = ATIPICO
Duración del Angor = MAS DE 30 MIN
Respuesta Vasodilatadora = POSITIVO
-> clase SI [70.0%]
CAPÍTULO 5
RESULTADOS OBTENIDOS
En este capítulo se presentan los resultados obtenidos. Primero, se explica la manera en que debe
realizarse la interpretación de los mismos (Sección 5.1), se presentan los formatos de los árboles
y las reglas de decisión tanto para el ID3 (Sección 5.1.1) como para el C4.5 (Sección 5.1.2).
Luego, se describen los dominios de datos sobre los que se trabajó (Sección 5.2), y se analizan
los resultados obtenidos con el ID3 (Sección 5.3) y con el C4.5 (Sección 5.4). En cada caso se
presenta, el árbol y las reglas de decisión obtenidos, y la evaluación de resultados sobre el
conjunto de datos de prueba. En la sección 5.5 se comparan los resultados obtenidos con el ID3 y
con el C4.5 en los distintos dominios. Y en la sección 5.6 se realiza un análisis general de los
resultados.
Para cada dominio, se presentan dos conjuntos de resultados para cada uno de los sistemas: uno utilizando
la ganancia como medida de la ganancia de información en cada iteración del sistema, y otra utilizando la
proporción de ganancia con el mismo propósito. Entonces, para cada combinación dominio - sistema –
medidor de ganancia, se presentan el árbol y las reglas de decisión obtenidas.
Como estimación del éxito del modelo generado se presenta una tabla o matriz obtenida de la evaluación
de los datos de prueba tanto con el árbol como con las reglas de decisión. La tabla generada, presentada
en la sección 4.5.2.1.6, cuenta con las siguientes columnas:
• Correctos: cantidad de casos de los datos de prueba clasificados correctamente para cada clase
• Errores: cantidad de casos de los datos de prueba clasificados erróneamente para cada clase
• Probabilidad: probabilidad de que un nuevo caso sea clasificado correctamente, se obtiene como:
correctos
(5.1)
correctos + errores
Como se explicó previamente, en la sección 4.5.2.2.8, en los árboles sin podar, N es la suma de los casos
fraccionarios que llegan a cada hoja y E es la suma de los casos que pertenecen a una clase distinta de la
correspondiente a la hoja, los falsos positivos. Es decir, que de los N casos cubiertos por la hoja, E casos
son incorrectos. En los árboles podados, N es la cantidad de casos de entrenamiento cubiertos por la hoja,
y E es la cantidad de errores predichos si una cantidad N de casos nuevos fuese clasificada por el árbol,
según la distribución binomial. Con lo cual, puede obtenerse un rápido estimador de errores sobre datos
nuevos de la siguiente manera:
∑E
i =1
i (5.2)
N
Donde k es la cantidad de nodos, Ei es el error en la hoja i, y N es la cantidad total de casos de
entrenamiento.
Para cada uno de los árboles se obtienen dos tablas de evaluación de idéntico formato: una a partir de los
datos de entrenamiento y la otra a partir de los datos de prueba. Una tabla indica en cada caso:
• Errores (porcentaje de error %): los errores indican la cantidad de casos clasificados
erróneamente; mientras que el porcentaje de error es dicha cantidad sobre la cantidad total de casos.
• Estimación: es un estimador del éxito del árbol obtenido según la ecuación 5.1.
Además, para cada uno de los árboles se presenta la matriz de confusión del tipo:
Donde se indica para cada clase, la cantidad de casos que fueron clasificados correctamente y la cantidad
de casos que no fueron clasificados correctamente. Para estos últimos se indica en particular, de qué clase
fueron clasificados.
Además, a continuación de las reglas obtenidas, se presenta una tabla de performance de las mismas sobre
los datos de prueba, con los siguientes datos, descriptos en la sección 4.5.2.2.9:
• Error: estimador del error de la regla (se obtiene restándole a 100 el estimador de éxito presente en
cada regla)
• Error (porcentaje de error %): el error indica la cantidad de casos que fueron clasificados
erróneamente, y el porcentaje de error es dicha cantidad sobre la cantidad de veces en que la regla fue
usada.
• Ventaja: indica la performance del conjunto de reglas, si la regla en cuestión fuese omitida. Se
expresa como: a(b|c), donde b es la cantidad de casos que serían clasificados erróneamente, si esta
regla no existiese; c es la cantidad de casos que serían clasificados correctamente por las reglas
siguientes; y a=b-c es el beneficio neto de eliminar la regla.
Los datos de cada uno de los dominios se presentan en el Apéndice B. En todos los casos, se dividieron
los datos preclasificados en dos subconjuntos: uno de entrenamiento y uno de prueba, según las
proporciones 2:3 y 1:3, respectivamente.
5.2.1. Créditos
a) Descripción
Los ejemplos planteados en esta base de datos fueron provistos por el Gerente del Centro de Cómputos de
Las Malvinas [Montalvetti, 1995] para el análisis de solicitudes de créditos. Los campos de los ejemplos
provistos son los parámetros que se tienen en cuenta al analizar un riesgo crediticio.
b) Atributos
c) Clases:
SI, NO.
SI NO Totales
En el conjunto de entrenamiento 69 30 99
En el conjunto de prueba 35 16 51
Totales 104 46 150
5.2.2. Cardiología
a) Descripción
Los ejemplos planteados en este caso corresponden a la patología de Infarto Agudo de Miocardio,
provistos por un cardiólogo [Montalvetti, 1995]. En este caso, todos los ejemplos responden a personas de
sexo masculino, entre 40 y 50 años, fumadoras, con displidemia e hipertensión arterial presente. Pueden
obtenerse dos diagnósticos de los ejemplos planteados: Si (Infarto Agudo de Miocardio) o No (no es un
Infarto Agudo de Miocardio). Aunque en medicina es difícil realizar un diagnóstico con una cantidad de
variables reducidas, se determinó que las variables planteadas en estos ejemplos alcanzan para realizar un
diagnóstico preliminar de gran ayuda al experto
b) Atributos
c) Clases:
SI, NO
SI NO Totales
En el conjunto de entrenamiento 18 46 64
En el conjunto de prueba 9 22 31
Totales 27 68 95
5.2.3. Votaciones
a) Descripción
Estos datos fueron recolectados a partir del Almanaque Trimestral del Congreso, 2da sesión de 98º
Congreso, 1984, Volumen XL: Congressional Quarterly Inc, Washington, D.C., 1985. Los datos incluyen
los votos de cada congresista de la U.S. House of Representatives en 16 temas claves (CQA). Se
identificaron nueve tipos diferentes de votos: votado a favor, convenio a favor, y pronunciado a favor
(agrupados como “a_favor”), votado en contra, convenio en contra, y pronunciado en contra (agrupados
como “en_contra”), voto en persona, voto en persona para evitar conflictos de intereses, y abstención de
voto o no dio su voto a conocer (agrupados como “desconocido”).
b) Atributos
c) Clases:
demócrata, republicano
Esta base de datos incluye las descripciones de muestras hipotéticas de 23 especies de hongos de las
familias Agaricus y Lepiota. Cada especie es identificada como apta para ser ingerida, absolutamente
venenosa, o de ingestión dudosa y ciertamente no recomendable. Esta última clase fue combinada con la
venenosa. La Guía de donde se obtuvieron los datos explica que no existe una regla simple para
determinar si un hongo es ingerible o no.
b) Atributos
Cantidad de Atributos desconocidos: 2480 (denotados por un "?"), todos para el primer atributo
c) Clases:
Ingerible, Venenoso.
Existen muchas teorías físicas que clasifican a los distintos asteroides en familias identificadas por un
elemento en particular. Esta base de datos, resultado de varias mediciones realizadas sobre múltiples
valores continuos, ayuda a clasificar los asteroides en familias. Según la teoría de Hirayama, que examina
la distribución de los asteroides con respecto a sus elementos orbitales, en particular su movimiento
principal, la inclinación y la excentricidad, permite identificar a las familias KORONIS, EOS, THEMIS,
FLORA, MARIA y PHOCAEA. Esta teoría ha sido comprobada por Arnold
b) Atributos
c) Clases:
Las clases representan las familias de asteroides a las cuales cada asteroide puede pertenecer.
5.2.6. Hipotiroidismo
a) Descripción
Estos datos fueron obtenidos de un estudio realizado sobre múltiples pacientes que presentaban síntomas
de hipotiroidismo en el Garvan Institute.
b) Atributos
Edad: continuo
Sexo: M, F.
Toma tiroxina: f, v.
Duda sobre tiroxina: f, v.
Toma medicación antitiroídea: f, v.
Enfermo: f, v.
Embarazada: f, v.
Cirugía tiroídea: f, v.
Tratamiento I131: f, v.
Es hipotiroide: f, v.
Es hipertiroide: f, v.
Litio: f, v.
bocio: f, v.
tumor: f, v.
Hipopituitario: f, v.
Psicológico: f, v.
Medición TSH: f, v.
TSH: continuo
Medición T3: f, v.
T3: Continuo
Medición TT4: f, v.
TT4: Continuo.
Medición T4U: f, v.
T4U: Continuo.
Medición FTI: f, v.
FTI: Continuo.
Medición TBG: f, v.
TBG: Continuo.
Fuente de referencia: WEST, STMW, SVHC, SVI, SVHD, otros.
c) Clases:
Esta base de datos sirve para clasificar un vidrio como flotante o no. Dicha información es muy
importante para los investigadores criminológicos, ya que cualquier vidrio dejado en la escena del crimen
sirve como evidencia, si está correctamente clasificado. Los datos fueron obtenidos del Central Research
Establishment, Home Office Forensic Science Service de Aldermaston, Reading, Berkshire.
b) Atributos
Id Clave Primaria
IR (índice de refracción) continuo
Na (Sodio medido como el porcentaje del peso del óxido continuo
correspondiente, esto se aplica para los demás minerales)
Mg (Magnesio) continuo
Al (Aluminio) continuo
Si (Silicio) continuo
K (Potasio) continuo
Ca (Calcio) continuo
Ba (Bario) continuo
Fe (Hierro) continuo
c) Clases:
Vidrios float para Vidrios para Vidrios float contenedo vajilla lamparitas Totales
construcciones construcciones para vehículos res
no float
En el conjunto 47 51 11 9 6 19 143
de
entrenamiento
En el conjunto 23 25 6 4 3 10 71
de prueba
Totales 70 76 17 13 9 29 214
5.3.1. Créditos
Reglas de decisión
Regla 0
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO SIN HIJOS
ENTONCES Otorga_Creditos = SI
Regla 1
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = ALQUILA
ENTONCES Otorga_Creditos = NO
Regla 2
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = PROPIA 0 IPVU
Y Servicios = BASICOS Y TIC
ENTONCES Otorga_Creditos = SI
Regla 3
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = PROPIA 0 IPVU
Y Servicios = BASICOS, TIC Y TEL
ENTONCES Otorga_Creditos = NO
Regla 4
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = SI
Regla 5
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y UN HIJO
ENTONCES Otorga_Creditos = SI
Regla 6
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = ALQUILA
Y Ingreso = ENTRE 451 Y 550
ENTONCES Otorga_Creditos = SI
Regla 7
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = ALQUILA
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = NO
Regla 8
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = PROPIA 0 IPVU
ENTONCES Otorga_Creditos = SI
Regla 9
SI Otros_Creditos = TRES CREDITOS
ENTONCES Otorga_Creditos = NO
Regla 10
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO SIN HIJOS
ENTONCES Otorga_Creditos = SI
Regla 11
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = ALQUILA
ENTONCES Otorga_Creditos = NO
Regla 12
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = PROPIA 0 IPVU
ENTONCES Otorga_Creditos = SI
Regla 13
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = SI
Regla 14
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y UN HIJO
ENTONCES Otorga_Creditos = SI
Regla 15
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = SOLTERO
ENTONCES Otorga_Creditos = SI
Reglas de decisión
Regla 0
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO SIN HIJOS
ENTONCES Otorga_Creditos = SI
Regla 1
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = ALQUILA
ENTONCES Otorga_Creditos = NO
Regla 2
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = PROPIA 0 IPVU
Y Servicios = BASICOS Y TIC
ENTONCES Otorga_Creditos = SI
Regla 3
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = PROPIA 0 IPVU
Y Servicios = BASICOS, TIC Y TEL
ENTONCES Otorga_Creditos = NO
Regla 4
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = SI
Regla 5
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = CASADO Y UN HIJO
ENTONCES Otorga_Creditos = SI
Regla 6
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = ALQUILA
Y Ingreso = ENTRE 451 Y 550
ENTONCES Otorga_Creditos = SI
Regla 7
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = ALQUILA
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = NO
Regla 8
SI Otros_Creditos = DOS CREDITOS
Y Composición_Familiar = SOLTERO
Y Vivienda = PROPIA 0 IPVU
ENTONCES Otorga_Creditos = SI
Regla 9
SI Otros_Creditos = TRES CREDITOS
ENTONCES Otorga_Creditos = NO
Regla 10
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO SIN HIJOS
ENTONCES Otorga_Creditos = SI
Regla 11
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = ENTRE 451 Y 550
Y Vivienda = ALQUILA
ENTONCES Otorga_Creditos = NO
Regla 12
SI Otros_Creditos = UN CREDITO
Regla 13
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y DOS HIJOS
Y Ingreso = MAS DE 551
ENTONCES Otorga_Creditos = SI
Regla 14
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = CASADO Y UN HIJO
ENTONCES Otorga_Creditos = SI
Regla 15
SI Otros_Creditos = UN CREDITO
Y Composición_Familiar = SOLTERO
ENTONCES Otorga_Creditos = SI
5.3.1.3. Conclusiones
En este caso, tanto el árbol como las reglas obtenidas mediante el ID3 son idénticos para la ganancia y
para la proporción de ganancia. Si analizamos la tabla de evaluación de los resultados, vemos que el
clasificador3 obtenido es excelente para clasificar la clase “NO”, pero sólo es exitoso en un 50% de los
casos de la clase “SI”. Es decir, que un nuevo caso de clase “NO” tiene una probabilidad de 1 de ser
clasificado correctamente. Mientras que un nuevo caso de clase “SI”, tiene solamente una probabilidad
del 0,49 de ser clasificado como “SI”.
Creemos que este fenómeno puede deberse a una mala elección de los datos de entrenamiento y prueba.
Un clasificador exitoso sobre los datos de entrenamiento, será exitoso sobre los demás conjuntos de datos
en la medida en que los datos de entrenamiento sean representativos de los otros conjuntos. Supongamos
que tenemos para un dominio hipotético, los siguientes datos de entrenamiento:
3
En el caso del ID3, por clasificador nos referiremos tanto al árbol como a las reglas obtenidas, ya es indistinto utilizar uno u otro
para clasificar nuevos casos.
2 90 SI
Si aplicamos el clasificador a la siguiente tupla de prueba: {2,3,”NO”}, el resultado será erróneo. Si todos
los casos de prueba de clase “NO” tuviesen valores distintos de 1 para el atributo A, entonces la
performance de los modelos obtenidos sobre estos datos de prueba sería poco alentadora. El conjunto de
entrenamiento en ese caso no sería representativo de los conjuntos en el cual se aplicó el clasificador.
Entonces, podemos conjeturar que la performance de los clasificadores generados por el ID3 para el
dominio Créditos, puede deberse a que los datos de entrenamiento no eran representativos de los datos de
prueba. Podemos extrapolar, este problema de representatividad de los datos de aprendizaje a los
humanos: no podemos enseñarle a un niño el concepto de pájaro mostrándole sólo distintas clases de
pingüinos, ya que cuando vea cualquier otro pájaro como una golondrina, un gorrión o una paloma,
pensará que no son pájaros; para él los pájaros no vuelan, son gordos, grandes y caminan por el hielo.
5.3.2. Cardiología
NO
DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
NO
DOLOR_DE_PECHO_DE_ANGOR = TIPICO
IRRADIACION_DEL_ANGOR = NO
ANTIGÜEDAD_DEL_ANGOR = MAS DE 1 MES
NO
ANTIGÜEDAD_DEL_ANGOR = RECIENTE
ANGOR_EN_RELACION = CON ESFUERZO
SI
ANGOR_EN_RELACION = EN REPOSO
NO
IRRADIACION_DEL_ANGOR = SI
NO
Reglas de decisión
Regla 0
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y RESPUESTA_VASODILATADORA = NEGATIVO
ENTONCES DIAGNOSTICO = NO
Regla 1
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y RESPUESTA_VASODILATADORA = POSITIVO
ENTONCES DIAGNOSTICO = SI
Regla 2
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = SI
Regla 3
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
ENTONCES DIAGNOSTICO = NO
Regla 4
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = NEGATIVO
ENTONCES DIAGNOSTICO = SI
Regla 5
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = POSITIVO
Y IRRADIACION_DEL_ANGOR = NO
ENTONCES DIAGNOSTICO = NO
Regla 6
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = POSITIVO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = SI
Regla 7
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = EN REPOSO
ENTONCES DIAGNOSTICO = SI
Regla 8
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
ENTONCES DIAGNOSTICO = NO
Regla 9
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
ENTONCES DIAGNOSTICO = NO
Regla 10
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = MAS DE 1 MES
ENTONCES DIAGNOSTICO = NO
Regla 11
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = RECIENTE
Y ANGOR_EN_RELACION = CON ESFUERZO
ENTONCES DIAGNOSTICO = SI
Regla 12
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = RECIENTE
Y ANGOR_EN_RELACION = EN REPOSO
ENTONCES DIAGNOSTICO = NO
Regla 13
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = NO
RESPUESTA_VASODILATADORA = POSITIVO
IRRADIACION_DEL_ANGOR = NO
NO
IRRADIACION_DEL_ANGOR = SI
SI
ANGOR_EN_RELACION = EN REPOSO
SI
DURACION_DEL_ANGOR = MENOS DE 30 MIN
DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
NO
DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
NO
DOLOR_DE_PECHO_DE_ANGOR = TIPICO
IRRADIACION_DEL_ANGOR = NO
ANTIGÜEDAD_DEL_ANGOR = MAS DE 1 MES
NO
ANTIGÜEDAD_DEL_ANGOR = RECIENTE
ANGOR_EN_RELACION = CON ESFUERZO
SI
ANGOR_EN_RELACION = EN REPOSO
NO
IRRADIACION_DEL_ANGOR = SI
NO
Reglas de decisión
Regla 0
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y RESPUESTA_VASODILATADORA = NEGATIVO
ENTONCES DIAGNOSTICO = NO
Regla 1
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y RESPUESTA_VASODILATADORA = POSITIVO
ENTONCES DIAGNOSTICO = SI
Regla 2
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = SI
Regla 3
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
ENTONCES DIAGNOSTICO = NO
Regla 4
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = NEGATIVO
ENTONCES DIAGNOSTICO = SI
Regla 5
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = POSITIVO
Y IRRADIACION_DEL_ANGOR = NO
ENTONCES DIAGNOSTICO = NO
Regla 6
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = CON ESFUERZO
Y RESPUESTA_VASODILATADORA = POSITIVO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = SI
Regla 7
SI DURACION_DEL_ANGOR = MAS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y ANGOR_EN_RELACION = EN REPOSO
ENTONCES DIAGNOSTICO = SI
Regla 8
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = ATIPICO
ENTONCES DIAGNOSTICO = NO
Regla 9
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = AUSENTE
ENTONCES DIAGNOSTICO = NO
Regla 10
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = MAS DE 1 MES
ENTONCES DIAGNOSTICO = NO
Regla 11
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = RECIENTE
Y ANGOR_EN_RELACION = CON ESFUERZO
ENTONCES DIAGNOSTICO = SI
Regla 12
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = NO
Y ANTIGÜEDAD_DEL_ANGOR = RECIENTE
Y ANGOR_EN_RELACION = EN REPOSO
ENTONCES DIAGNOSTICO = NO
Regla 13
SI DURACION_DEL_ANGOR = MENOS DE 30 MIN
Y DOLOR_DE_PECHO_DE_ANGOR = TIPICO
Y IRRADIACION_DEL_ANGOR = SI
ENTONCES DIAGNOSTICO = NO
5.3.2.3. Conclusiones
Al igual que en el caso anterior, vemos que tanto el árbol como las reglas de decisión obtenidas son
iguales para el criterio de ganancia como para el de proporción de ganancia. En este caso, no obstante, la
performance del clasificador es notablemente mejor que en el caso anterior. El modelo generado tiene un
93% de probabilidad de clasificar correctamente un caso negativo, y un 100% de clasificar correctamente
uno positivo.
Cabe destacar que la cantidad de casos de entrenamiento es menor a la del caso anterior y no obstante la
cantidad de reglas obtenidas es 13, contra 15 obtenidas en el caso anterior. Entonces, podríamos concluir,
grosso modo, que la cantidad de datos de entrada no es proporcional al tamaño del árbol ni la cantidad de
reglas obtenidas.
5.3.3. Votaciones
Cong_honorarios_medicos = a_favor
Reduccion_corp_Synfuels = a_favor
Export_sin_impuestos = a_favor
democrata
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Presupuesto_de_educacion = a_favor
Der_demanda_Superfund = a_favor
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
republicano
Acta_sudaf_admin_export = en_contra
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Der_demanda_Superfund = en_contra
democrata
Presupuesto_de_educacion = desconocido
democrata
Presupuesto_de_educacion = en_contra
Acta_sudaf_admin_export = a_favor
Adop_resolucion_presup = a_favor
republicano
Adop_resolucion_presup = en_contra
Ayuda_a_El_Salvador = a_favor
republicano
Ayuda_a_El_Salvador = en_contra
democrata
Acta_sudaf_admin_export = desconocido
democrata
Acta_sudaf_admin_export = en_contra
democrata
Reduccion_corp_Synfuels = desconocido
republicano
Reduccion_corp_Synfuels = en_contra
Export_sin_impuestos = a_favor
Inmigracion = a_favor
republicano
Inmigracion = en_contra
Acta_sudaf_admin_export = a_favor
democrata
Acta_sudaf_admin_export = desconocido
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
democrata
Acta_sudaf_admin_export = en_contra
republicano
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Adop_resolucion_presup = a_favor
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Adop_resolucion_presup = en_contra
republicano
Cong_honorarios_medicos = desconocido
Misil_mx = a_favor
Prohib_pruebas_anti_satel = a_favor
democrata
Prohib_pruebas_anti_satel = desconocido
democrata
Prohib_pruebas_anti_satel = en_contra
republicano
Misil_mx = desconocido
republicano
Misil_mx = en_contra
democrata
Cong_honorarios_medicos = en_contra
Presupuesto_de_educacion = a_favor
democrata
Presupuesto_de_educacion = desconocido
Adop_resolucion_presup = a_favor
democrata
Adop_resolucion_presup = en_contra
republicano
Presupuesto_de_educacion = en_contra
democrata
Reglas de decisión
Regla 0
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = a_favor
ENTONCES Clase = democrata
Regla 1
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = desconocido
ENTONCES Clase = republicano
Regla 2
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = a_favor
ENTONCES Clase = republicano
Regla 3
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = republicano
Regla 4
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = desconocido
ENTONCES Clase = republicano
Regla 5
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = en_contra
Y Niños discapacitados = a_favor
ENTONCES Clase = republicano
Regla 6
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = en_contra
Y Niños discapacitados = en_contra
ENTONCES Clase = democrata
Regla 7
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = en_contra
ENTONCES Clase = democrata
Regla 8
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = desconocido
ENTONCES Clase = democrata
Regla 9
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = a_favor
ENTONCES Clase = republicano
Regla 10
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = en_contra
Y Ayuda_a_El_Salvador = a_favor
ENTONCES Clase = republicano
Regla 11
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = en_contra
Y Ayuda_a_El_Salvador = en_contra
ENTONCES Clase = democrata
Regla 12
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = desconocido
ENTONCES Clase = democrata
Regla 13
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = en_contra
ENTONCES Clase = democrata
Regla 14
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = desconocido
ENTONCES Clase = republicano
Regla 15
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = a_favor
ENTONCES Clase = republicano
Regla 16
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = democrata
Regla 17
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = desconocido
Y Particip_proy_agua = a_favor
ENTONCES Clase = republicano
Regla 18
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = desconocido
Y Particip_proy_agua = en_contra
ENTONCES Clase = democrata
Regla 19
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = en_contra
ENTONCES Clase = republicano
Regla 20
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = desconocido
ENTONCES Clase = republicano
Regla 21
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = republicano
Regla 22
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = desconocido
Y Niños discapacitados = a_favor
ENTONCES Clase = republicano
Regla 23
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = desconocido
Y Niños discapacitados = en_contra
ENTONCES Clase = democrata
Regla 24
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = en_contra
ENTONCES Clase = republicano
Regla 25
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = a_favor
ENTONCES Clase = democrata
Regla 26
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = desconocido
ENTONCES Clase = democrata
Regla 27
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = en_contra
ENTONCES Clase = republicano
Regla 28
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = desconocido
ENTONCES Clase = republicano
Regla 29
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = en_contra
ENTONCES Clase = democrata
Regla 30
SI Cong_honorarios_medicos = en_contra
Y Presupuesto_de_educacion = a_favor
ENTONCES Clase = democrata
Regla 31
SI Cong_honorarios_medicos = en_contra
Y Presupuesto_de_educacion = desconocido
Y Adop_resolucion_presup = a_favor
ENTONCES Clase = democrata
Regla 32
SI Cong_honorarios_medicos = en_contra
Y Presupuesto_de_educacion = desconocido
Y Adop_resolucion_presup = en_contra
ENTONCES Clase = republicano
Regla 33
SI Cong_honorarios_medicos = en_contra
Y Presupuesto_de_educacion = en_contra
ENTONCES Clase = democrata
Demócrata 81 2 0,9759036
Republicano 47 5 0,903846
Totales 128 7 0,948148
Cong_honorarios_medicos = a_favor
Reduccion_corp_Synfuels = a_favor
Export_sin_impuestos = a_favor
democrata
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Presupuesto_de_educacion = a_favor
Der_demanda_Superfund = a_favor
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
republicano
Acta_sudaf_admin_export = en_contra
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Der_demanda_Superfund = en_contra
democrata
Presupuesto_de_educacion = desconocido
democrata
Presupuesto_de_educacion = en_contra
Acta_sudaf_admin_export = a_favor
Adop_resolucion_presup = a_favor
republicano
Adop_resolucion_presup = en_contra
Ayuda_a_El_Salvador = a_favor
republicano
Ayuda_a_El_Salvador = en_contra
democrata
Acta_sudaf_admin_export = desconocido
democrata
Acta_sudaf_admin_export = en_contra
democrata
Reduccion_corp_Synfuels = desconocido
republicano
Reduccion_corp_Synfuels = en_contra
Export_sin_impuestos = a_favor
Inmigracion = a_favor
republicano
Inmigracion = en_contra
Acta_sudaf_admin_export = a_favor
democrata
Acta_sudaf_admin_export = desconocido
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
democrata
Acta_sudaf_admin_export = en_contra
republicano
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Adop_resolucion_presup = a_favor
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Adop_resolucion_presup = en_contra
republicano
Cong_honorarios_medicos = desconocido
Misil_mx = a_favor
Prohib_pruebas_anti_satel = a_favor
democrata
Prohib_pruebas_anti_satel = desconocido
democrata
Prohib_pruebas_anti_satel = en_contra
republicano
Misil_mx = desconocido
republicano
Misil_mx = en_contra
democrata
Cong_honorarios_medicos = en_contra
Adop_resolucion_presup = a_favor
democrata
Adop_resolucion_presup = desconocido
democrata
Adop_resolucion_presup = en_contra
Presupuesto_de_educacion = a_favor
democrata
Presupuesto_de_educacion = desconocido
republicano
Presupuesto_de_educacion = en_contra
democrata
Reglas de decisión
Regla 0
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = a_favor
ENTONCES Clase = democrata
Regla 1
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = desconocido
ENTONCES Clase = republicano
Regla 2
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = a_favor
ENTONCES Clase = republicano
Regla 3
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = republicano
Regla 4
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = desconocido
ENTONCES Clase = republicano
Regla 5
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = en_contra
Y Niños discapacitados = a_favor
ENTONCES Clase = republicano
Regla 6
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = a_favor
Y Particip_proy_agua = en_contra
Y Acta_sudaf_admin_export = en_contra
Y Niños discapacitados = en_contra
ENTONCES Clase = democrata
Regla 7
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = a_favor
Y Der_demanda_Superfund = en_contra
ENTONCES Clase = democrata
Regla 8
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = desconocido
ENTONCES Clase = democrata
Regla 9
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = a_favor
ENTONCES Clase = republicano
Regla 10
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = en_contra
Y Ayuda_a_El_Salvador = a_favor
ENTONCES Clase = republicano
Regla 11
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = a_favor
Y Adop_resolucion_presup = en_contra
Y Ayuda_a_El_Salvador = en_contra
ENTONCES Clase = democrata
Regla 12
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = desconocido
ENTONCES Clase = democrata
Regla 13
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = a_favor
Y Export_sin_impuestos = en_contra
Y Presupuesto_de_educacion = en_contra
Y Acta_sudaf_admin_export = en_contra
ENTONCES Clase = democrata
Regla 14
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = desconocido
ENTONCES Clase = republicano
Regla 15
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = a_favor
ENTONCES Clase = republicano
Regla 16
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = democrata
Regla 17
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = desconocido
Y Particip_proy_agua = a_favor
ENTONCES Clase = republicano
Regla 18
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = desconocido
Y Particip_proy_agua = en_contra
ENTONCES Clase = democrata
Regla 19
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = a_favor
Y Inmigracion = en_contra
Y Acta_sudaf_admin_export = en_contra
ENTONCES Clase = republicano
Regla 20
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = desconocido
ENTONCES Clase = republicano
Regla 21
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = a_favor
ENTONCES Clase = republicano
Regla 22
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = desconocido
Y Niños discapacitados = a_favor
ENTONCES Clase = republicano
Regla 23
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = a_favor
Y Acta_sudaf_admin_export = desconocido
Y Niños discapacitados = en_contra
ENTONCES Clase = democrata
Regla 24
SI Cong_honorarios_medicos = a_favor
Y Reduccion_corp_Synfuels = en_contra
Y Export_sin_impuestos = en_contra
Y Adop_resolucion_presup = en_contra
ENTONCES Clase = republicano
Regla 25
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = a_favor
ENTONCES Clase = democrata
Regla 26
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = desconocido
ENTONCES Clase = democrata
Regla 27
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = a_favor
Y Prohib_pruebas_anti_satel = en_contra
ENTONCES Clase = republicano
Regla 28
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = desconocido
ENTONCES Clase = republicano
Regla 29
SI Cong_honorarios_medicos = desconocido
Y Misil_mx = en_contra
ENTONCES Clase = democrata
Regla 30
SI Cong_honorarios_medicos = en_contra
Y Adop_resolucion_presup = a_favor
ENTONCES Clase = democrata
Regla 31
SI Cong_honorarios_medicos = en_contra
Y Adop_resolucion_presup = desconocido
ENTONCES Clase = democrata
Regla 32
SI Cong_honorarios_medicos = en_contra
Y Adop_resolucion_presup = en_contra
Y Presupuesto_de_educacion = a_favor
ENTONCES Clase = democrata
Regla 33
SI Cong_honorarios_medicos = en_contra
Y Adop_resolucion_presup = en_contra
Y Presupuesto_de_educacion = desconocido
ENTONCES Clase = republicano
Regla 34
SI Cong_honorarios_medicos = en_contra
Y Adop_resolucion_presup = en_contra
Y Presupuesto_de_educacion = en_contra
ENTONCES Clase = democrata
5.3.4.3. Conclusiones
En comparación con los dominios anteriores, los modelos obtenidos en este caso son de gran tamaño. Los
árboles de decisión tienen un tamaño de 57 y 58 para la ganancia y proporción de ganancia
respectivamente, y se obtuvieron 34 reglas con la ganancia y 35 con la proporción de ganancia. Los
modelos generados utilizando la ganancia y la proporción de ganancia son muy similares. Si analizamos
los árboles de decisión, vemos que esta diferencia en tamaño se origina en la rama
Cong_honorarios_medicos=a_favor, donde el atributo Presupuesto_de_educacion genera la mayor
ganancia, y el atributo Adop_resolucion_presup genera la mayor proporción de ganancia.
Si miramos los tamaños de los modelos obtenidos a la luz de los dominios analizados anteriormente,
podríamos decir que, en este caso, los modelos obtenidos son de mayor tamaño porque el sistema analizó
una mayor cantidad de datos de entrenamiento. Con lo cual, nuestra hipótesis acerca de que la cantidad de
datos de entrenamiento no afecta el tamaño de los modelos generados se vería afectada. Podríamos hilar
más fino, tomando la nueva hipótesis de que la cantidad de datos de entrenamiento influye en el tamaño
de los modelos generados, en la medida en que analizamos grandes diferencias en la cantidad de datos de
entrenamiento. En cambio, cuando estas diferencias son más pequeñas, la cantidad de datos de
entrenamiento no influye. Es decir, si construimos un modelo a partir de 400 datos de entrenamiento,
obtenemos árboles de decisión del orden de X; y si construimos un modelo a partir de 40 datos de
entrenamiento, obtenemos árboles de decisión del orden de Y, siendo Y menor que X. Sin embargo, si
construimos un árbol de decisión a partir de 450 datos de entrenamiento, probablemente el árbol tenga un
tamaño del orden de X; y si lo construimos a partir de 35 datos de entrenamiento, uno de orden Y.
En cuanto a la proporción de error sobre los datos de prueba, es de 5.19% en todos los casos. Además, la
probabilidad de clasificar un caso de prueba correctamente es alta para ambas clases.
Olor = almendra
ingerible
Olor = anis
ingerible
Olor = creosota
venenoso
Olor = especioso
venenoso
Olor = hediondo
venenoso
Olor = mohoso
venenoso
Olor = ninguno
Color_sombrero = amarillo
venenoso
Color_sombrero = blanco
Magulladuras = no
ingerible
Magulladuras = si
venenoso
Color_sombrero = canela
ingerible
Color_sombrero = gris
ingerible
Color_sombrero = marron
Sup_tronco_arriba_anillo = escamosa
ingerible
Sup_tronco_arriba_anillo = fibrosa
ingerible
Sup_tronco_arriba_anillo = sedosa
venenoso
Sup_tronco_arriba_anillo = suave
ingerible
Color_sombrero = piel
Forma_sombrero = abotonada
ingerible
Forma_sombrero = acampanada
venenoso
Forma_sombrero = chata
ingerible
Forma_sombrero = convexa
ingerible
Color_sombrero = rojo
ingerible
Color_sombrero = rosa
Color_esporas = blanca
ingerible
Color_esporas = verde
venenoso
Color_sombrero = verde
ingerible
Color_sombrero = violeta
ingerible
Olor = pescado
venenoso
Olor = punzante
Venenoso
Reglas de decisión
Regla 0
SI Olor = almendra
ENTONCES Tipo_Hongo = ingerible
Regla 1
SI Olor = anis
ENTONCES Tipo_Hongo = ingerible
Regla 2
SI Olor = creosota
ENTONCES Tipo_Hongo = venenoso
Regla 3
SI Olor = especioso
ENTONCES Tipo_Hongo = venenoso
Regla 4
SI Olor = hediondo
ENTONCES Tipo_Hongo = venenoso
Regla 5
SI Olor = mohoso
ENTONCES Tipo_Hongo = venenoso
Regla 6
SI Olor = ninguno
Y Color_sombrero = amarillo
ENTONCES Tipo_Hongo = venenoso
Regla 7
SI Olor = ninguno
Y Color_sombrero = blanco
Y Magulladuras = no
ENTONCES Tipo_Hongo = ingerible
Regla 8
SI Olor = ninguno
Y Color_sombrero = blanco
Y Magulladuras = si
ENTONCES Tipo_Hongo = venenoso
Regla 9
SI Olor = ninguno
Y Color_sombrero = canela
ENTONCES Tipo_Hongo = ingerible
Regla 10
SI Olor = ninguno
Y Color_sombrero = gris
ENTONCES Tipo_Hongo = ingerible
Regla 11
SI Olor = ninguno
Y Color_sombrero = marron
Y Sup_tronco_arriba_anillo = escamosa
ENTONCES Tipo_Hongo = ingerible
Regla 12
SI Olor = ninguno
Y Color_sombrero = marron
Y Sup_tronco_arriba_anillo = fibrosa
ENTONCES Tipo_Hongo = ingerible
Regla 13
SI Olor = ninguno
Y Color_sombrero = marron
Y Sup_tronco_arriba_anillo = sedosa
ENTONCES Tipo_Hongo = venenoso
Regla 14
SI Olor = ninguno
Y Color_sombrero = marron
Y Sup_tronco_arriba_anillo = suave
ENTONCES Tipo_Hongo = ingerible
Regla 15
SI Olor = ninguno
Y Color_sombrero = piel
Y Forma_sombrero = abotonada
ENTONCES Tipo_Hongo = ingerible
Regla 16
SI Olor = ninguno
Y Color_sombrero = piel
Y Forma_sombrero = acampanada
ENTONCES Tipo_Hongo = venenoso
Regla 17
SI Olor = ninguno
Y Color_sombrero = piel
Y Forma_sombrero = chata
ENTONCES Tipo_Hongo = ingerible
Regla 18
SI Olor = ninguno
Y Color_sombrero = piel
Y Forma_sombrero = convexa
ENTONCES Tipo_Hongo = ingerible
Regla 19
SI Olor = ninguno
Y Color_sombrero = rojo
ENTONCES Tipo_Hongo = ingerible
Regla 20
SI Olor = ninguno
Y Color_sombrero = rosa
Y Color_esporas = blanca
ENTONCES Tipo_Hongo = ingerible
Regla 21
SI Olor = ninguno
Y Color_sombrero = rosa
Y Color_esporas = verde
ENTONCES Tipo_Hongo = venenoso
Regla 22
SI Olor = ninguno
Y Color_sombrero = verde
ENTONCES Tipo_Hongo = ingerible
Regla 23
SI Olor = ninguno
Y Color_sombrero = violeta
ENTONCES Tipo_Hongo = ingerible
Regla 24
SI Olor = pescado
ENTONCES Tipo_Hongo = venenoso
Regla 25
SI Olor = punzante
ENTONCES Tipo_Hongo = venenoso
Olor = almendra
ingerible
Olor = anis
ingerible
Olor = creosota
venenoso
Olor = especioso
venenoso
Olor = hediondo
venenoso
Olor = mohoso
venenoso
Olor = ninguno
Color_velo = amarillo
venenoso
Color_velo = blanco
Tamaño_membrana = ancha
Color_esporas = blanca
ingerible
Color_esporas = marron
ingerible
Color_esporas = negro
ingerible
Color_esporas = verde
venenoso
Tamaño_membrana = fina
Magulladuras = no
Sup_tronco_arriba_anillo = fibrosa
ingerible
Sup_tronco_arriba_anillo = sedosa
venenoso
Sup_tronco_arriba_anillo = suave
ingerible
Magulladuras = si
venenoso
Color_velo = marron
ingerible
Color_velo = naranja
ingerible
Olor = pescado
venenoso
Olor = punzante
venenoso
Reglas de decisión
Regla 0
SI Olor = almendra
ENTONCES Tipo_Hongo = ingerible
Regla 1
SI Olor = anis
ENTONCES Tipo_Hongo = ingerible
Regla 2
SI Olor = creosota
ENTONCES Tipo_Hongo = venenoso
Regla 3
SI Olor = especioso
ENTONCES Tipo_Hongo = venenoso
Regla 4
SI Olor = hediondo
ENTONCES Tipo_Hongo = venenoso
Regla 5
SI Olor = mohoso
ENTONCES Tipo_Hongo = venenoso
Regla 6
SI Olor = ninguno
Y Color_velo = amarillo
ENTONCES Tipo_Hongo = venenoso
Regla 7
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = ancha
Y Color_esporas = blanca
ENTONCES Tipo_Hongo = ingerible
Regla 8
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = ancha
Y Color_esporas = marron
ENTONCES Tipo_Hongo = ingerible
Regla 9
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = ancha
Y Color_esporas = negro
ENTONCES Tipo_Hongo = ingerible
Regla 10
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = ancha
Y Color_esporas = verde
ENTONCES Tipo_Hongo = venenoso
Regla 11
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = fina
Y Magulladuras = no
Y Sup_tronco_arriba_anillo = fibrosa
ENTONCES Tipo_Hongo = ingerible
Regla 12
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = fina
Y Magulladuras = no
Y Sup_tronco_arriba_anillo = sedosa
ENTONCES Tipo_Hongo = venenoso
Regla 13
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = fina
Y Magulladuras = no
Y Sup_tronco_arriba_anillo = suave
ENTONCES Tipo_Hongo = ingerible
Regla 14
SI Olor = ninguno
Y Color_velo = blanco
Y Tamaño_membrana = fina
Y Magulladuras = si
ENTONCES Tipo_Hongo = venenoso
Regla 15
SI Olor = ninguno
Y Color_velo = marron
ENTONCES Tipo_Hongo = ingerible
Regla 16
SI Olor = ninguno
Y Color_velo = naranja
ENTONCES Tipo_Hongo = ingerible
Regla 17
SI Olor = pescado
ENTONCES Tipo_Hongo = venenoso
Regla 18
SI Olor = punzante
ENTONCES Tipo_Hongo = venenoso
5.3.4.3. Conclusiones
Los resultados obtenidos en este dominio son muy interesantes. Vemos que los árboles de decisión y, a
raíz de ello las reglas, no son iguales para el caso de la ganancia y de la proporción de ganancia. Si
analizamos los árboles, vemos que en primer término, el atributo olor es el que más información brinda
tanto utilizando la ganancia como la proporción de ganancia como medidores de información. Pero una
vez en el caso de los ejemplos que tienen olor=ninguno, la ganancia considera que el atributo
color_sombrero es el que brinda más información, mientras que la proporción de ganancia considera que
el atributo color_velo brinda más información que los demás. Analizando el archivo de log generado por
el programa (sección 4.5.2.1.8) para el caso de los ejemplos que tienen olor=ninguno, tenemos:
A pesar de esta diferencia entre los modelos obtenidos en uno y otro caso, vemos que la proporción de
error en ambos casos es baja. Con lo cual, aunque la ganancia favorezca a los atributos con mayor
cantidad de valores posibles, no podemos afirmar que esto influya en gran medida en el análisis sobre los
datos de prueba. Para el clasificador obtenido mediante la proporción de ganancia no se realizaron errores
al clasificar los casos de prueba.
5.4.1. Créditos
Reglas de decisión
Regla 12
SI Otros Creditos = TRES CREDITOS
ENTONCES clase=NO [94.2%]
Regla 2
SI Ingreso = ENTRE 451 Y 550
Y Composición Familiar = CASADO Y DOS HIJOS
Y Vivienda = ALQUILA
ENTONCES clase=NO [79.4%]
Regla 6
SI Ingreso = MAS DE 551
Y Composición Familiar = SOLTERO
Y Vivienda = ALQUILA
Y Otros Creditos = DOS CREDITOS
ENTONCES clase=NO [50.0%]
Regla 1
SI Otros Creditos = UN CREDITO
Y clase=SI [90.3%]
Regla 11
SI Otros Creditos = DOS CREDITOS
ENTONCES clase=SI [80.6%]
Regla 13
Clase=SI
Reglas de decisión
Regla 12
SI Otros Creditos = TRES CREDITOS
ENTONCES clase=NO [94.2%]
Regla 2
SI Ingreso = ENTRE 451 Y 550
Y Composición Familiar = CASADO Y DOS HIJOS
Y Vivienda = ALQUILA
ENTONCES clase=NO [79.4%]
Regla 6
SI Ingreso = MAS DE 551
Regla 1
SI Otros Creditos = UN CREDITO
ENTOCNES clase=SI [90.3%]
Regla 11
SI Otros Creditos = DOS CREDITOS
ENTONCES clase=SI [80.6%]
Regla 13
clase=SI
5.4.1.3. Conclusiones
Los árboles de decisión sin simplificar obtenidos utilizando la ganancia y la proporción de ganancia se
diferencian solamente en la rama de “Otros Créditos=UN CRÉDITO y Composición Familiar=CASADO
Y DOS HIJOS”. Al llegar a este punto, se encontró que la mayor ganancia se obtenía dividiendo a los
Resultados Magdalena Servente 139
Algoritmos TDIDT aplicados a la Minería de Datos Inteligente
datos según el atributo Ingreso, mientras que la mayor proporción de ganancia se obtuvo con el atributo
Vivienda. A pesar de estas diferencias encontradas en los árboles sin simplificar, los árboles
simplificados son idénticos y tienen un único nodo, lo cual los hace muy fáciles de comprender.
En cuanto a las reglas obtenidas, son idénticas en ambos casos. Cabe destacar que, como el árbol de
decisión simplificado es tan simple, el modelo de clasificación ofrecido en ese caso se entiende más
rápidamente que el conjunto de reglas de decisión.
Analicemos la columna ventaja de la tabla de resultados para las reglas de decisión. Recordemos que la
ventaja se expresa de la forma a(b|c) donde b es la cantidad de casos que serían clasificados erróneamente
por las reglas siguientes, de omitirse esa regla; c es la cantidad de casos que serían clasificados
correctamente; y a es el beneficio neto de omitir la regla. En el caso de la regla 12, tenemos un beneficio
neto negativo, con lo cual, a pesar de que con los métodos utilizados por el C4.5 se consideró que no era
conveniente eliminarla, de utilizar este modelo para clasificar nuevos casos, debería analizarse la
posibilidad de no tener esta regla en cuenta y trabajar con un modelo más simple.
En cuanto al valor E asociado, en el árbol original representa la cantidad de casos cubiertos por una hoja
que pertenece a una clase diferente de la indicada por la misma. Mientras que en el caso del árbol podado,
E es la cantidad de errores predichos, según la distribución binomial, en dicha hoja si un conjunto de N
nuevos casos fuesen evaluados por el árbol. Recordemos que los valores E tienen especial importancia, ya
que el estimador del éxito del modelo obtenido se calcula realizando la sumatoria de todos los valores E y
dividiéndola por la cantidad de casos de entrenamiento. En este caso, la estimación del éxito del modelo
es de un 12.4%.
5.4.2. Cardiología
Reglas de decisión
Regla 2
SI Dolor de Pecho de Angor = TIPICO
Y Duración del Angor = MAS DE 30 MIN
ENTONCES clase=SI [77.7%]
Regla 3
SI Dolor de Pecho de Angor = ATIPICO
Y Irradiación del Angor = SI
Y Duración del Angor = MAS DE 30 MIN
ENTONCES clase=SI [75.8%]
Regla 4
SI Dolor de Pecho de Angor = ATIPICO
Y Duración del Angor = MAS DE 30 MIN
Y Respuesta Vasodilatadora = POSITIVO
ENTONCES clase=SI [75.8%]
Regla 6
SI Dolor de Pecho de Angor = AUSENTE
ENTONCES clase=NO [93.9%]
Regla 1
SI Duración del Angor = MENOS DE 30 MIN
ENTONCES clase=NO [92.2%]
Regla 5
SI Dolor de Pecho de Angor = ATIPICO
Y Irradiación del Angor = NO
Y Respuesta Vasodilatadora = NEGATIVO
ENTONCES clase=NO [75.8%]
Regla 7
Clase=NO
Reglas de decisión
Regla 2
SI Dolor de Pecho de Angor = TIPICO
Y Duración del Angor = MAS DE 30 MIN
ENTONCES clase=SI [77.7%]
Regla 3
SI Dolor de Pecho de Angor = ATIPICO
Y Duración del Angor = MAS DE 30 MIN
Y Respuesta Vasodilatadora = POSITIVO
ENTONCES clase=SI [75.8%]
Regla 4
SI Dolor de Pecho de Angor = ATIPICO
Y Irradiación del Angor = SI
Y Duración del Angor = MAS DE 30 MIN
ENTONCES clase=SI [75.8%]
Regla 6
SI Dolor de Pecho de Angor = AUSENTE
ENTONCES clase=NO [93.9%]
Regla 1
SI Duración del Angor = MENOS DE 30 MIN
ENTONCES clase=NO [92.2%]
Regla 5
SI Dolor de Pecho de Angor = ATIPICO
Y Irradiación del Angor = NO
Y Respuesta Vasodilatadora = NEGATIVO
ENTONCES clase=NO [75.8%]
Regla 7
Clase= NO
5.4.2.3. Conclusiones
En este caso, tanto los árboles de decisión como las reglas que derivan de ellos, difieren entre la ganancia
y la proporción de ganancia. Esta diferencia surge del hecho de que frente a una misma situación
(Duración del Angor=MAS DE 30 MIN Y Dolor de Pecho de Angor=Atipico), la ganancia y la
proporción de ganancia son distintas. Se obtuvo mejor proporción de ganancia con el atributo Respuesta
Vasodilatadora, y mejor ganancia con el atributo Irradiación de Pecho de Angor. No obstante, al igual que
en el caso del dominio de Estudio sobre Hongos utilizando el ID3, esta diferencia no influye en las
proporciones de error de los modelos.
Podemos encontrar la misma diferencia en las reglas de decisión. Y, al igual que en el caso anterior, no
influye en gran medida en la performance de los clasificadores sobre los datos de prueba
Notemos que en este caso los árboles de decisión no han sido podados. Dada su simplicidad, esto no fue
necesario.
5.4.3. Votaciones
Reglas de decisión
Regla 7
SI cong_honorarios_medicos = en_contra
ENTONCES clase=democrata [98.4%]
Regla 3
SI reduccion_corp_Synfuels = a_favor
Y presupuesto_de_educación = en_contra
ENTONCES clase=democrata [94.3%]
Regla 9
SI cong_honorarios_medicos = desconocido
Y misil_mx = en_contra
ENTONCES clase=democrata [63.0%]
Regla 5
SI cong_honorarios_medicos = a_favor
ENTONCES clase=republicano [88.7%]
Regla 10
SI cong_honorarios_medicos = desconocido
Y misil_mx = desconocido
ENTONCES clase=republicano [50.0%]
Regla 11
clase=democrata
cong_honorarios_medicos = a_favor:
reduccion_corp_Synfuels = en_contra: republicano (97.0/3.0)
reduccion_corp_Synfuels = desconocido: republicano (4.0)
reduccion_corp_Synfuels = a_favor:
export_sin_impuestos = a_favor: democrata (2.0)
export_sin_impuestos = desconocido: republicano (1.0)
export_sin_impuestos = en_contra:
presupuesto_de_educación = a_favor: republicano (13.0/2.0)
presupuesto_de_educación = en_contra: democrata (5.0/2.0)
presupuesto_de_educación = desconocido: democrata (1.0)
cong_honorarios_medicos = en_contra:
adop_resolucion_presup = a_favor: democrata (151.0)
adop_resolucion_presup = desconocido: democrata (1.0)
adop_resolucion_presup = en_contra:
presupuesto_de_educación = a_favor: democrata (9.0)
presupuesto_de_educación = en_contra: democrata (6.0)
presupuesto_de_educación = desconocido: republicano (1.0)
cong_honorarios_medicos = desconocido:
particip_proy_agua = a_favor: democrata (4.0)
particip_proy_agua = en_contra: democrata (0.0)
particip_proy_agua = desconocido:
misil_mx = a_favor: democrata (3.0/1.0)
misil_mx = en_contra: republicano (0.0)
misil_mx = desconocido: republicano (2.0)
Reglas de decisión
Regla 1
SI reduccion_corp_Synfuels = a_favor
Y export_sin_impuestos = a_favor
ENTONCES clase=democrata [97.5%]
Regla 11
SI particip_proy_agua = a_favor
Y cong_honorarios_medicos = desconocido
ENTONCES clase=democrata [70.7%]
Regla 6
SI cong_honorarios_medicos = a_favor
Y reduccion_corp_Synfuels = en_contra
ENTONCES clase=republicano [94.8%]
Regla 2
SI cong_honorarios_medicos = a_favor
Y presupuesto_de_educación = a_favor
Y export_sin_impuestos = en_contra
ENTONCES clase=republicano [94.0%]
Regla 10
SI adop_resolucion_presup = en_contra
Y presupuesto_de_educación = desconocido
ENTONCES clase=republicano [82.0%]
Regla 13
SI cong_honorarios_medicos = desconocido
Y misil_mx = desconocido
ENTONCES clase=republicano [50.0%]
Regla 14
clase=democrata
5.4.3.3. Conclusiones
Los resultados obtenidos en este dominio con el C4.5 parecen satisfactorios a simple vista, ya que la
proporción de error no supera el 6% en ninguno de los casos. En cuanto a los árboles de decisión
simplificados son iguales para la ganancia y para la proporción de ganancia. No obstante, en los árboles
sin simplificar hay una diferencia de tamaño: el árbol obtenido con la ganancia es de tamaño menor que el
obtenido con la proporción de ganancia. Esto se debe a que en la rama
cong_honorarios_medicos=desconocido en la ganancia se optó por dividir al conjunto según el atributo
misil_mx, y en la proporción de ganancia se los dividió según particip_proy_agua y luego en uno de los
casos por misil_mx. Esta diferencia de tamaño no influye en la performance de los árboles. Es decir, nos
encontramos ante dos árboles de igual porcentaje de error, pero de distinto tamaño. Con lo cual, de tener
que optar entre ambos, eligiríamos el obtenido con la ganancia.
En cuanto a las reglas de decisión, las primeras, derivadas utilizando la ganancia, tienen una proporción
de error similar a la de los árboles y son menos que las obtenidas con la proporción de ganancia. Estas
segundas, tienen una mayor proporción de error. Nuevamente, en este caso, eligiríamos las obtenidas
utilizando la ganancia.
Reglas de decisión
Regla 5
SI Olor = hediondo
ENTONCES Tipo_hongo = venenoso [99.9%]
Regla 23
SI Olor = especioso
ENTONCES Tipo_hongo = venenoso [99.7%]
Regla 4
SI Olor = pescado
ENTONCES Tipo_hongo = venenoso [99.7%]
Regla 9
SI Color_sombrero = marron
Y Sup_tronco_arriba_anillo = sedosa
ENTONCES Tipo_hongo = venenoso [99.6%]
Regla 22
SI Olor = punzante
ENTONCES Tipo_hongo = venenoso [99.2%]
Regla 3
SI Olor = creosota
ENTONCES Tipo_hongo = venenoso [96.3%]
Regla 6
SI Olor = mohoso
ENTONCES Tipo_hongo = venenoso [96.2%]
Regla 21
SI Color_sombrero = amarillo
Y Olor = ninguno
ENTONCES Tipo_hongo = venenoso [87.1%]
Regla 14
SI Color_esporas = verde
ENTONCES Tipo_hongo = venenoso [82.0%]
Regla 2
SI Olor = anis
ENTONCES Tipo_hongo = ingerible [99.2%]
Regla 1
SI Olor = almendra
ENTONCES Tipo_hongo = ingerible [99.2%]
Regla 11
SI Olor = ninguno
ENTONCES Tipo_hongo = ingerible [99.0%]
Regla 24
Tipo_hongo = ingerible
Reglas de decisión
Regla 5
SI Olor = hediondo
ENTONCES Tipo_Hongo = venenoso [99.9%]
Regla 13
SI Tamaño_membrana = fina
Y Sup_tronco_arriba_anillo = sedosa
ENTONCES Tipo_Hongo = venenoso [99.8%]
Regla 19
SI Olor = especioso
ENTONCES Tipo_Hongo = venenoso [99.7%]
Regla 4
SI Olor = pescado
ENTONCES Tipo_Hongo = venenoso [99.7%]
Regla 18
SI Olor = punzante
ENTONCES Tipo_Hongo = venenoso [99.2%]
Regla 3
SI Olor = creosota
ENTONCES Tipo_Hongo = venenoso [96.3%]
Regla 6
SI Olor = mohoso
ENTONCES Tipo_Hongo = venenoso [96.2%]
Regla 17
SI ColorTronco_arriba_anillo = amarillo
ENTONCES Tipo_Hongo = venenoso [84.1%]
Regla 10
SI Color_esporas = verde
ENTONCES Tipo_Hongo = venenoso [82.0%]
Regla 9
SI Olor = ninguno
Y Tamaño_membrana = ancha
ENTONCES Tipo_Hongo = ingerible [99.6%]
Regla 15
SI Olor = ninguno
Y Sup_tronco_arriba_anillo = suave
ENTONCES Tipo_Hongo = ingerible [99.5%]
Regla 12
SI Olor = ninguno
Y Sup_tronco_arriba_anillo = fibrosa
ENTONCES Tipo_Hongo = ingerible [99.4%]
Regla 2
SI Olor = anis
ENTONCES Tipo_Hongo = ingerible [99.2%]
Regla 1
SI Olor = almendra
ENTONCES Tipo_Hongo = ingerible [99.2%]
Regla 20
Tipo_Hongo = ingerible
5.4.4.3. Conclusiones
El árbol de decisión obtenido utilizando la ganancia como criterio de decisión es de mayor tamaño que el
obtenido utilizando la proporción de ganancia. Esta diferencia se origina por la preferencia de la ganancia
por atributos con más cantidad de valores. Veamos que en la rama Olor=ninguno, el método que utilizó la
ganancia dividió los datos según el atributo color_sombrero que toma diez valores distintos, mientras que
el método que utilizó la proporción de ganancia, dividió los datos según el atributo Tamaño_membrana
que tiene dos valores posibles. En este caso, el hecho de que un árbol sea de mayor tamaño no favorece la
performance del mismo: el segundo árbol, más pequeño, tuvo una mejor performance en los casos de
prueba, ya que clasificó solo 7 de ellos erróneamente, mientras que el árbol generado con la ganancia
clasificó 25 erróneamente. A pesar de esta diferencia, la estimación del error sobre futuros casos es muy
buena para los dos árboles: del 0.6% para el generado utilizando la ganancia y del 0.5% para el generado
utilizando la proporción de ganancia.
Cabe destacar que en el árbol de decisión generado utilizando la proporción de ganancia, el subárbol
olor=ninguno y tamaño_membrana=fina se simplificó por una rama hija, que no contiene al atributo por
el cual se realizó la primera división. Es decir, en el árbol sin simplificar la división en el nodo en
cuestión se realizó según el atributo ColorTronco_arriba_anillo; este atributo no aparece en el árbol
simplificado.
En cuanto a las reglas de decisión, a pesar de que las primeras fueron obtenidas a partir de un árbol de
mayor tamaño, son menos que la cantidad de reglas obtenidas a partir del segundo árbol. Sin embargo, la
performance sobre los datos de prueba fue igual en ambos casos.
5.4.5. Elita
Evaluación general:
Reglas de decisión
Regla 1
SI resonancia <= 5.42
Y distancia_jupiter <= 0.18
ENTONCES Familia = THEMIS [97.0%]
Regla 2
SI distancia_jupiter > 0.18
Y distancia_jupiter <= 0.2
ENTONCES Familia = KORONIS [94.4%]
Regla 5
SI resonancia > 11.51
Y distancia_jupiter <= 0.24
ENTONCES Familia = MARIA [89.9%]
Regla 6
SI resonancia > 11.51
Y distancia_jupiter > 0.24
ENTONCES Familia = PHOCAEA [94.2%]
Regla 3
SI resonancia > 5.42
Y distancia_jupiter <= 0.2
ENTONCES Familia = EOS [94.3%]
Regla 4
SI resonancia <= 11.51
Y distancia_jupiter > 0.2
ENTONCES Familia = FLORA [97.5%]
Regla 7
Familia = FLORA
Evaluación general:
Reglas de decisión
Regla 1
SI resonancia <= 5.42
Y distancia_jupiter <= 0.18
ENTONCES Familia = THEMIS [97.0%]
Regla 2
SI distancia_jupiter > 0.18
Y distancia_jupiter <= 0.2
ENTONCES Familia = KORONIS [94.4%]
Regla 5
SI resonancia > 11.51
Y distancia_jupiter <= 0.24
ENTONCES Familia = MARIA [89.9%]
Regla 6
SI resonancia > 11.51
Y distancia_jupiter > 0.24
ENTONCES Familia = PHOCAEA [94.2%]
Regla 3
SI resonancia > 5.42
Y distancia_jupiter <= 0.2
ENTONCES Familia = EOS [94.3%]
Regla 4
SI resonancia <= 11.51
Y distancia_jupiter > 0.2
ENTONCES Familia = FLORA [97.5%]
Regla 7
Familia = FLORA
5.4.5.3. Conclusiones
En este caso, tanto los árboles como las reglas obtenidas son iguales si se utiliza la ganancia o la
proporción de ganancia como criterio de decisión. Los árboles son pequeños y poco frondosos, lo cual
facilita su comprensión. Además, no clasifican ningún ejemplo erróneamente en los datos de prueba.
Las reglas de decisión generadas, también son pocas, lo cual hace que su lectura sea más comprensible. Y
clasifican sólo un caso erróneamente sobre los datos de entrenamiento. Por lo tanto, podemos decir que
tanto los árboles como las reglas obtenidas en este dominio constituyen un buen clasificador.
Analicemos la diferencia entre trabajar con atributos discretos y con atributos continuos. Cuando
trabajamos con atributos discretos y elegimos un atributo para dividir el conjunto en la raíz, este atributo
no participa en las divisiones posteriores. Es decir, como hay una rama descendiente de ese nodo para
cada uno de los valores del atributo, todos los subconjuntos posteriores tienen un único valor para ese
atriburo, con lo cual, nunca ofrecerá una mayor ganancia o proporción de ganancia que los demás
atributos. Por lo tanto, no existirá otro nodo, descendiente del anterior, que utilice a dicho atributo como
prueba de valor. En cambio, cuando un atributo continuo se utiliza como prueba de valor en un nodo, el
C4.5 divide a los datos de acuerdo al resultado de la mejor binarización, con lo cual, la prueba de valor
realizada en el nodo es una pregunta acerca de si el valor del atributo supera o no un determinado umbral.
Puede utilizarse, no obstante, el mismo atributo para dividir los datos en un nodo hijo, variará el valor
umbral por el que se realiza la binarización. Lo mismo ocurre con los antecedentes en las reglas de
decisión. Observemos que este fenómeno se presenta tanto en los árboles como en las reglas de decisión
obtenidos en este caso, ya que la prueba de valor distancia_jupiter se repite en nodos padres e hijos, y está
presente en más de un antecedente en las reglas de decisión.
5.4.6. Hipotiroidismo
Subárbol [S1]
Árbol simplificado:
Evaluación general:
Reglas de decisión
Regla 12
SI Toma tiroxina = f
Y Cirugía tiroídea = f
Y TSH > 6
Y TT4 <= 150
Y FTI > 64
ENTONCES clase = hipotiroide compensado [98.9%]
Regla 1
SI TSH <= 6
ENTONCES clase = negativo [99.9%]
Regla 15
SI Toma tiroxina = v
Y FTI > 64
ENTONCES clase = negativo [99.5%]
Regla 9
SI Medición TSH = f
ENTONCES clase = negativo [99.5%]
Regla 14
SI TT4 > 150
ENTONCES clase = negativo [99.4%]
Regla 8
SI Cirugía tiroídea = v
ENTONCES clase = negativo [92.7%]
Regla 4
SI Cirugía tiroídea = f
Y TSH > 6
Y FTI <= 64
ENTONCES clase = hipotiroide primario [95.6%]
Regla 11
SI Medición T4U = f
Y Fuente de referencia = SVI
Y TSH > 6
ENTONCES clase = hipotiroide primario [35.2%]
Regla 16
Clase = hipotiroide compensado
Evaluación general:
Reglas de decisión
Regla 7
SI Toma tiroxina = f
Y Cirugía tiroídea = f
Y TSH > 6
Y TT4 <= 150
Y FTI > 64
ENTONCES clase = hipotiroide compensado [98.9%]
Regla 4
SI Cirugía tiroídea = f
Y TSH > 6
Y FTI <= 64
ENTONCES clase = hipotiroide primario [95.6%]
Regla 6
SI Toma tiroxina = f
Y Medición TT4 = f
Y TSH > 6
ENTONCES clase = hipotiroide primario [45.3%]
Regla 1
SI TSH <= 6
ENTONCES clase = negativo [99.9%]
Regla 10
SI Toma tiroxina = v
Y FTI > 64
ENTONCES clase = negativo [99.5%]
Regla 2
SI Medición TSH = f
Y ENTONCES clase = negativo [99.5%]
Regla 8
SI TT4 > 150
ENTONCES clase = negativo [99.4%]
Regla 5
SI Cirugía tiroídea = v
ENTONCES clase = negativo [92.7%]
Regla 11
Clase = hipotiroide compensado
5.4.6.3. Conclusiones
El árbol de decisión obtenido utilizando la ganancia sin simplificar tiene mayor tamaño que el obtenido
utilizando la proporción de ganancia. No obstante, al simplificar el primer árbol se obtiene un árbol más
pequeño que ambos. En cuanto a la proporción de error, es igual para el árbol generado utilizando la
ganancia y el generado utilizando la proporción de ganancia. Por lo tanto, podríamos afirmar que el
tamaño no parece estar relacionado con la performance.
En cuanto a las reglas de decisión, en ambos casos se obtuvieron nueve reglas. La proporción de error
observada en las reglas generadas utilizando la ganancia, es la menor de todas las observadas para este
dominio.
Mg <= 1 :
Ca <= 9 :
Al > 2 : contenedores (2.0)
Al <= 2 :
Al > 1 : lamparitas (12.0/1.0)
Al <= 1 :
Ba > 0 : lamparitas (4.0)
Ba <= 0 :
Na <= 13 : lamparitas (2.0)
Na > 13 : vajilla (3.0)
Ca > 9 :
Al <= 0 : vidrios_para_construcciones_no_float (4.0/1.0)
Al > 0 :
Si <= 72 : vidrios_para_construcciones_no_float (6.0/1.0)
Si > 72 : contenedores (4.0)
Mg > 1 :
Ca <= 7 :
Ca <= 6 : contenedores (2.0/1.0)
Árbol simplificado
Mg <= 1 :
Ca <= 9 :
Al > 2 : contenedores (2.0/1.0)
Al <= 2 :
Al > 1 : lamparitas (12.0/2.5)
Al <= 1 :
Ba > 0 : lamparitas (4.0/1.2)
Ba <= 0 :
Na <= 13 : lamparitas (2.0/1.0)
Na > 13 : vajilla (3.0/1.1)
Ca > 9 :
Al <= 0 : vidrios_para_construcciones_no_float (4.0/2.2)
Al > 0 :
Si <= 72 : vidrios_para_construcciones_no_float (6.0/2.3)
Si > 72 : contenedores (4.0/1.2)
Mg > 1 :
Ca > 7 : vidrios_float_para_construcciones (91.0/48.8)
Ca <= 7 :
Ca <= 6 : contenedores (2.0/1.8)
Ca > 6 : vidrios_para_construcciones_no_float (13.0/2.5)
Evaluación general:
Reglas de decisión
Regla 8
SI Al > 0
Y Si > 72
Y Ca > 9
ENTONCES clase = contenedores [70.7%]
Regla 5
SI Al > 2
ENTONCES clase = contenedores [63.0%]
Regla 2
SI Na > 13
Y Mg <= 1
Y Al <= 1
Y Ca <= 9
Y Ba <= 0
ENTONCES clase = vajilla [63.0%]
Regla 3
SI Al <= 2
Y Ca <= 9
Y Ba > 0
ENTONCES clase = lamparitas [84.1%]
Regla 4
SI Mg <= 1
Y Al > 1
Y Al <= 2
Y Ca <= 9
ENTONCES clase = lamparitas [79.4%]
Regla 1
SI Na <= 13
Y Mg <= 1
Y Al <= 1
Y Ca <= 9
ENTONCES clase = lamparitas [50.0%]
Regla 10
SI Mg > 1
Y Ca > 6
Y Ca <= 7
ENTONCES clase = vidrios_para_construcciones_no_float [80.9%]
Regla 7
SI Mg <= 1
Y Si <= 72
Y Ca > 9
ENTONCES clase = vidrios_para_construcciones_no_float [66.2%]
Regla 14
SI Mg > 2
Y Ca > 7
ENTONCES clase = vidrios_float_para_construcciones [48.0%]
Regla 15
Clase = vidrios_para_construcciones_no_float
Mg <= 1 :
Ca <= 9 :
Al > 2 : contenedores (2.0)
Al <= 2 :
Al > 1 : lamparitas (12.0/1.0)
Al <= 1 :
Ba > 0 : lamparitas (4.0)
Ba <= 0 :
Na <= 13 : lamparitas (2.0)
Na > 13 : vajilla (3.0)
Ca > 9 :
Al <= 0 : vidrios_para_construcciones_no_float (4.0/1.0)
Al > 0 :
Si <= 72 : vidrios_para_construcciones_no_float (6.0/1.0)
Si > 72 : contenedores (4.0)
Mg > 1 :
Ba > 0 : contenedores (2.0/1.0)
Ba <= 0 :
Mg <= 2 :
Ca <= 8 : vidrios_para_construcciones_no_float (2.0)
Ca > 8 : vidrios_float_para_construcciones (7.0/4.0)
Mg > 2 :
Ca <= 7 : vidrios_para_construcciones_no_float (13.0/1.0)
Ca > 7 :
Na > 13 : vidrios_float_para_construcciones (6.0/3.0)
Na <= 13 :
Si > 72 : vidrios_float_para_construcciones (21.0/6.0)
Si <= 72 :
Al <= 0 : vidrios_float_para_construcciones (7.0/1.0)
Al > 0 :[S1]
Subárbol [S1]
Árbol simplificado
Mg <= 1 :
Ca <= 9 :
Al > 2 : contenedores (2.0/1.0)
Al <= 2 :
Al > 1 : lamparitas (12.0/2.5)
Al <= 1 :
Ba > 0 : lamparitas (4.0/1.2)
Ba <= 0 :
Na <= 13 : lamparitas (2.0/1.0)
Na > 13 : vajilla (3.0/1.1)
Ca > 9 :
Al <= 0 : vidrios_para_construcciones_no_float (4.0/2.2)
Al > 0 :
Si <= 72 : vidrios_para_construcciones_no_float (6.0/2.3)
Si > 72 : contenedores (4.0/1.2)
Mg > 1 :
Ba > 0 : contenedores (2.0/1.8)
Ba <= 0 :
Ca <= 7 : vidrios_para_construcciones_no_float (13.0/2.5)
Ca > 7 : vidrios_float_para_construcciones (91.0/48.8)
Evaluación general:
Clase 0 0 0 0 0 0 0
vidrios_para_vehíc
ulos_no_float
Clase contenedores 0 3 0 0 1 0 0
Clase vajilla 2 1 0 0 0 0 0
Clase lamparitas 0 1 0 0 1 0 8
Reglas de decisión
Regla 8
SI Al > 0
Y Si > 72
Y Ca > 9
ENTONCES clase = contenedores [70.7%]
Regla 5
SI Al > 2
ENTONCES clase = contenedores [63.0%]
Regla 2
SI Na > 13
Y Mg <= 1
Y Al <= 1
Y Ca <= 9
Y Ba <= 0
ENTONCES clase = vajilla [63.0%]
Regla 3
SI Al <= 2
Y Ca <= 9
Y Ba > 0
ENTONCES clase = lamparitas [84.1%]
Regla 4
SI Mg <= 1
Y Al > 1
Y Al <= 2
Y Ca <= 9
ENTONCES clase = lamparitas [79.4%]
Regla 1
SI Na <= 13
Y Mg <= 1
Y Al <= 1
Y Ca <= 9
ENTONCES clase = lamparitas [50.0%]
Regla 11
SI Mg > 1
Y Ca <= 7
Y Ba <= 0
ENTONCES clase = vidrios_para_construcciones_no_float [80.9%]
Regla 7
SI Mg <= 1
Y Si <= 72
Y Ca > 9
ENTONCES clase = vidrios_para_construcciones_no_float [66.2%]
Regla 15
SI Mg > 2
Y Si > 72
Y Ca > 7
ENTONCES clase = vidrios_float_para_construcciones [61.7%]
Regla 10
SI Mg > 1
Y Ca > 8
ENTONCES clase = vidrios_float_para_construcciones [51.5%]
Regla 16
Clase = vidrios_para_construcciones_no_float
5.4.7.3. Conclusiones
En este caso los modelos obtenidos utilizando la ganancia y la proporción de ganancia son distintos,
aunque el tamaño de los árboles de decisión sea el mismo, y la diferencia en la cantidad de reglas
obtenidas sea de una regla. Para ambos árboles de decisión el porcentaje de error sobre los datos de
prueba es del 47.9%. Mientras que para los conjuntos de reglas de decisión es del 42.3%. Aunque en este
segundo caso la performance sea mejor, no podemos afirmar que un clasificador con el 42% de error sea
confiable.
5.5.1. Créditos
Analizando la figura 5.1 vemos que para los árboles obtenidos con el C4.5 se obtuvo una proporción de
error del 43%, mientras que para el resto de los casos, se obtuvo una proporción de error del 35%. No
obstante, en los árboles generados con el C4.5 la estimación del error sobre casos futuros es del 12.4%.
50.00%
40.00%
35.25% 35.25% 35.25% 35.25% 35.29% 35.29%
35.00%
30.00%
25.00%
20.00%
15.00%
10.00%
5.00%
0.00%
ID3-Árbol- ID3-Reglas- ID3-Árbol- ID3-Reglas- C4.5-Árbol- C4.5- C4.5-Árbol- C4.5-
ganancia ganancia proporción proporción ganancia Reglas- proporción Reglas-
de ganancia de ganancia ganancia de ganancia proporción
de ganancia
Figura 5.1: Porcentajes de error obtenidos en el dominio Créditos
Los árboles generados con el ID3 son similares a los generados con el C4.5 sin simplificar. La única
diferencia destacable es que la rama expresada en el ID3 como:
Se simplificó en el C4.5 a:
Notemos que de los cuatro casos clasificados en el C4.5 por esta rama, sólo uno se clasifica
erróneamente, y el árbol es más claro.
En este caso, el modelo más simple es el presentado por los árboles de decisión simplificados generados
por el C4.5: representan en un árbol de decisión que tiene únicamente una raíz y tres hojas aquello que los
árboles generados por el ID3 representan en un árbol de tamaño 26 y con una mayor proporción de error.
El modelo más simple, ayuda a destacar los atributos importantes del problema, y, por lo tanto, a
entenderlo más profundamente. No obstante, es el que mayor proporción de error tuvo en la práctica.
Entonces, antes de decidirnos por un modelo, debemos analizar la relación simplicidad - proporción de
error.
Analicemos los tamaños de los modelos obtenidos. La figura 5.2 compara los tamaños de los árboles de
decisión obtenidos para el dominio Créditos. La figura 5.3 compara la cantidad de reglas de decisión
obtenidas con cada algoritmo.
30
25
20
15
10
0
ID3-Árbol-ganancia C4.5-Árbol- ID3-Árbol- C4.5-Árbol-
ganancia proporción de proporción de
ganancia ganancia
Del análisis de estos dos gráficos anteriores vemos que los modelos generados para este dominio por el
C4.5 fueron más pequeños y, por lo tanto, más comprensibles que los generados por el ID3. Si tuviéramos
que elegir un modelo entre estos cuatro para representar al dominio, eligiríamos las reglas generadas por
el C4.5, ya que son más simples que las generadas por el ID3 y tienen una proporción de error similar.
18
16
14
12
10
8
6
4
2
0
ID3-Reglas- C4.5-Reglas- ID3-Reglas- C4.5-Reglas-
ganancia ganancia proporción de proporción de
ganancia ganancia
5.5.2. Cardiología
La figura 5.4 muestra los porcentajes de error obtenidos con cada uno de los métodos para el dominio
Cardiología. Vemos que todos los porcentajes se encuentran entre el 6.45% y el 6.50%. Con lo cual, no
hay ningún modelo que sea claramente superior que otro en este aspecto.
6.00%
5.00%
4.00%
3.00%
2.00%
1.00%
0.00%
ia
ia
ia
ia
a
ia
ci
ci
ci
nc
nc
nc
nc
nc
an
an
an
na
na
na
na
na
an
an
an
ga
ga
ga
ga
ga
-g
-g
-g
s-
de
de
de
ol
ol
as
d
la
rb
rb
gl
ón
g
Á
-Á
ió
ió
ió
Re
ci
-R
3-
rc
rc
c
.5
3-
or
ID
po
.5
po
po
C4
ID
op
C4
ro
ro
r o
pr
-p
-p
-p
s-
ol
ol
as
la
rb
rb
gl
eg
Á
-Á
Re
-R
3-
.5
3-
ID
.5
C4
ID
C4
Las figuras 5.5 y 5.6 comparan los tamaños de los modelos obtenidos. Vemos que la cantidad de reglas
obtenidas con el ID3 es el doble de la cantidad de reglas obtenidas con el C4.5. En el caso de los árboles
de decisión, el tamaño de los árboles obtenidos con el C4.5 es menor que la mitad del tamaño de los
árboles obtenidos con el ID3.
30 25 25
25
20
15 10 10
10
5
0
ID3-Árbol- C4.5-Árbol- ID3-Árbol- C4.5-Árbol-
ganancia ganancia proporción proporción
de ganancia de ganancia
30
25
20 14 14
15
10 7 7
5
0
ID3-Reglas- C4.5-Reglas- ID3-Reglas- C4.5-Reglas-
ganancia ganancia proporción proporción
de ganancia de ganancia
Analizando los árboles de decisión obtenidos para este dominio, podemos deducir que esta diferencia en
tamaños se debe a que el ID3 ajusta el árbol de decisión para cubrir todos y cada uno de los datos de
entrada, mientras que cada hoja de un árbol generado con el C4.5 cubre una distribución de casos y puede
contener errores. Veamos, por ejemplo, los árboles obtenidos utilizando la ganancia con ambos
algoritmos. En el caso de DURACION_DEL_ANGOR = MENOS DE 30 MIN, en el árbol obtenido con
el C4.5 nos encontramos con una hoja, que cubre 33 casos, 1 sólo incorrectamente. Mientras que en el
árbol obtenido con el ID3, nos encontramos con un subárbol de tamaño 10, este subárbol también cubre
33 casos, ninguno incorrectamente. Ambos árboles tienen proporciones de errores similares. Cabe
entonces preguntarnos si no es más conveniente tener hoja con un 3.03% de error en lugar de un subárbol
que cubra todos los casos, sin ningún error. Recordemos que la performance general de ambos árboles
frente a los datos de prueba fue muy similar.
5.5.3. Votaciones
La figura 5.7 presenta los porcentajes de error obtenidos para el dominio de Votaciones. Vemos que con
el ID3 el porcentaje de error ronda el 5.20%, mientras que con el C4.5, el porcentaje de error es más bajo
en el caso de ambos árboles de decisión y de las reglas utilizando la ganancia. Esto es destacable ya que si
analizamos las figuras 5.8 y 5.9, que presentan el tamaño de los modelos obtenidos, vemos que los
modelos obtenidos con el C4.5 son mucho menores que los obtenidos con el ID3. Si a esto le agregamos
el hecho de que el porcentaje de error en tres de los modelos es menor, podemos concluir que en este caso
la performance del C4.5 es mejor que la del ID3.
Podemos preguntarnos a qué se debe la diferencia en los tamaños de los modelos generados con el ID3 y
con el C4.5. Tomemos, por ejemplo, los árboles de decisión. En el caso de los árboles generados
utilizando la ganancia, los atributos elegidos por el ID3 y por el C4.5 para realizar la partición de los
datos son los mismos. Sin embargo, recordemos que cada hoja del C4.5 cubre una distribución de casos
(aún en los árboles sin simplificar), entonces el árbol resultante es más simple.
6.00%
5.19% 5.19% 5.19% 5.19% 5.20%
5.00%
3.70%
4.00%
3.00% 3.00%
3.00%
2.00%
1.00%
0.00%
ia
ia
ia
ia
a
ia
ci
ci
ci
nc
nc
nc
nc
nc
an
an
an
na
na
na
na
na
an
an
an
ga
ga
ga
ga
ga
-g
-g
-g
s-
e
de
de
de
ol
ol
s
d
la
la
rb
rb
n
ón
g
eg
Á
-Á
ió
ió
ó
e
-R
ci
-R
3-
rc
rc
rc
.5
or
ID
.5
po
po
C4
ID
op
C4
ro
ro
r o
pr
-p
-p
-p
s-
ol
ol
as
la
rb
rb
gl
eg
Á
-Á
Re
-R
3-
.5
3-
ID
.5
C4
ID
C4
Figura 5.7: Porcentajes de error obtenidas en el dominio Votaciones
40 34 35
30
20
6 7
10
0
ID3-Reglas- C4.5-Reglas- ID3-Reglas- C4.5-Reglas-
ganancia ganancia proporción proporción
de ganancia de ganancia
70 57 58
60
50
40
30
20 7 7
10
0
ID3-Árbol- C4.5-Árbol- ID3-Árbol- C4.5-Árbol-
ganancia ganancia proporción de proporción de
ganancia ganancia
A continuación, en las figuras 5.10 y 5.11 se presentan ambos árboles de decisión y se pueden apreciar las
simplificaciones realizadas por el C4.5.
Cong_honorarios_medicos = a_favor
Reduccion_corp_Synfuels = a_favor
Export_sin_impuestos = a_favor
democrata
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Presupuesto_de_educacion = a_favor
Der_demanda_Superfund = a_favor
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
republicano
Acta_sudaf_admin_export = en_contra
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Der_demanda_Superfund = en_contra
Democrata (1)
Presupuesto_de_educacion = desconocido
democrata
Presupuesto_de_educacion = en_contra
Acta_sudaf_admin_export = a_favor
Adop_resolucion_presup = a_favor
republicano
Adop_resolucion_presup = en_contra
Ayuda_a_El_Salvador = a_favor
republicano
Ayuda_a_El_Salvador = en_contra
democrata
Acta_sudaf_admin_export = desconocido
democrata
Acta_sudaf_admin_export = en_contra
Democrata (2)
Reduccion_corp_Synfuels = desconocido
republicano
Reduccion_corp_Synfuels = en_contra
Export_sin_impuestos = a_favor
Inmigracion = a_favor
republicano
Inmigracion = en_contra
Acta_sudaf_admin_export = a_favor
democrata
Acta_sudaf_admin_export = desconocido
Particip_proy_agua = a_favor
republicano
Particip_proy_agua = en_contra
democrata
Acta_sudaf_admin_export = en_contra
republicano
Export_sin_impuestos = desconocido
republicano
Export_sin_impuestos = en_contra
Adop_resolucion_presup = a_favor
Acta_sudaf_admin_export = a_favor
republicano
Acta_sudaf_admin_export = desconocido
Niños discapacitados = a_favor
republicano
Niños discapacitados = en_contra
democrata
Adop_resolucion_presup = en_contra
Republicano (3)
Cong_honorarios_medicos = desconocido
Misil_mx = a_favor
Prohib_pruebas_anti_satel = a_favor
democrata
Prohib_pruebas_anti_satel = desconocido
democrata
Prohib_pruebas_anti_satel = en_contra
Republicano (4)
Misil_mx = desconocido
republicano
Misil_mx = en_contra
democrata
Cong_honorarios_medicos = en_contra
Presupuesto_de_educacion = a_favor
democrata
Presupuesto_de_educacion = desconocido
Adop_resolucion_presup = a_favor
democrata
Adop_resolucion_presup = en_contra
republicano
Presupuesto_de_educacion = en_contra
Democrata (5)
Figura 5.10: Árbol generado por el ID3 para el dominio Votaciones utilizando la ganancia
Figura 5.11: Árbol generado por el C4.5 para el dominio Votaciones utilizando la ganancia
En el caso (1), podemos observar que el subárbol de tamaño 10 generado por el ID3, se representó en el
C4.5 con una hoja que cubre 13 casos, dos incorrectamente. En el caso (2), el C4.5 presenta una hoja que
cubre 5 casos, dos de ellos erróneamente, mientras que el ID3 presenta un subárbol de tamaño 8. En el
caso (3), el subárbol presentado por el ID3 es de tamaño 17 y la hoja presentada en el mismo caso por el
C4.5 clasifica 3 casos errróneamente de los 97 que cubre. La diferencia en el caso (4) no es tan notable,
ya que el C4.5 representa en una hoja con N=4 y E=2, lo que el ID3 presenta en un nodo de decisión con
tres hojas hijas. Finalmente, en el caso (5), el C4.5 se equivoca una sola vez en los 168 casos que cubre la
hoja, mientras que el ID3 los clasifica a todos correctamente con un subárbol de tamaño 6.
El ID3 no generaliza los resultados de una hoja, es decir, no permite que una hoja cubra casos de una
clase distinta a la expresada. Por lo tanto, cubre exhaustivamente todos los casos de entrenamiento.
Mientras que la generalización realizada por el C4.5 permite obtener árboles más pequeños a un precio
que parece ser no tan alto. Pensemos que, muchas veces es preferible tener una hoja con performance del
96.9%, como en el caso (3), que un árbol de tamaño 17. Este fenómeno que ocurre en los árboles
generados por el ID3 y, como consecuencia lógica, también en las reglas generadas por el mismo sistema,
se conoce como sobreajuste. Como su nombre lo indica, se origina en que el ID3 cubre absolutamente
todos los casos de entrenamiento correctamente, ajusta el modelo a todos los casos de entrenamiento.
Existen muchas maneras de solucionar el sobreajuste. Podríamos, por ejemplo, realizar una poda del árbol
cuando un subárbol tenga una performance mayor a una cota predefinida, es decir, cuando (E*100)/N sea
superior a una cota mínima de performance. Otra opción sería realizar esta simplificación y adjuntarle al
árbol las reglas de decisión con las excepciones.
1.00% 0.90%
0.90%
0.80%
0.70%
0.60%
0.50%
0.40% 0.30% 0.30% 0.30%
0.30%
0.20%
0.10% 0.00% 0.00% 0.00% 0.00%
0.00%
ia
ia
ia
ia
a
a
a
ia
ci
ci
ci
nc
nc
nc
nc
nc
an
an
an
na
na
na
na
na
an
an
an
ga
ga
ga
ga
ga
-g
-g
-g
s-
e
de
de
de
ol
ol
as
la
rb
rb
gl
n
n
ón
eg
-Á
Á
ió
ó
ió
e
ci
-R
-R
ci
3-
rc
rc
.5
or
or
ID
.5
3
po
po
C4
ID
op
C4
ro
ro
ro
pr
-p
-p
-p
s-
ol
ol
as
la
rb
rb
gl
eg
-Á
Á
Re
-R
3-
.5
3-
ID
.5
C4
ID
C4
En el caso de este dominio el porcentaje de error registrado con todos los clasificadores no supera el 1%,
como lo muestra la figura 5.12. El mayor porcentaje de error se obtuvo con el árbol de decisión generado
por el C4.5 utilizando la ganancia, y el menor, y más destacable, se obtuvo utilizando el ID3 con la
proporción de ganancia. En este caso, todos los datos de entrenamiento fueron clasificados correctamente.
En caso de tener que optar entre alguno de los clasificadores generados, optaríamos por los generados con
el ID3 utilizando la proporción de ganancia, ya que además de no presentar errores, el tamaño del árbol es
aceptable, como lo muestra la figura 5.13. Mientras que la cantidad de reglas generadas en el mismo caso
es superior a las generadas con el C4.5 utilizando la ganancia (figura 5.14), que tienen un porcentaje de
error bajo.
Como se destacó en las conclusiones particulares de este dominio para el ID3 (Sección 5.3.4.3) y para el
C4.5 (Sección 5.4.4.3), vimos que la ganancia favoreció a atributos con muchos valores diferentes frente
a otros atributos que generaban una mayor ganancia neta de información pero que tenían menos valores.
Este es un problema general de la ganancia y es una de las razones por las que se comenzó a utilizar la
proporción de ganancia.
50 44
45
40 32
35
30 25 25
25
20
15
10
5
0
ID3-Árbol- C4.5-Árbol- ID3-Árbol- C4.5-Árbol-
ganancia ganancia proporción de proporción de
ganancia ganancia
Figura 5.13: Tamaño de los árboles de decisión obtenidos en el dominio Estudio sobre hongos
30 26
25
19
20
15
13
15
10
5
0
ID3-Reglas- C4.5-Reglas- ID3-Reglas- C4.5-Reglas-
ganancia ganancia proporción de proporción de
ganancia ganancia
Figura 5.14: Cantidad de reglas de decisión obtenidas en el dominio Estudio sobre hongos
Si comparamos los árboles obtenidos en cada caso, veremos que en los árboles generados con el C4.5
existen muchas ramas en las que N=E=0. Es decir, ramas que no cubren ningún caso, pero que existen
porque el C4.5 toma la información de dominio y tiene en cuenta esos posibles valores al analizar la
ganancia y la proporción de ganancia en todos los casos. El ID3, en cambio, no tiene en cuenta
información de dominio, entonces, trabaja con la hipótesis de que los únicos valores válidos para los
atributos son aquellos que ocurren en los datos.
60.00%
50.00%
40.00% Créditos
Cardiología
Votaciones
30.00% Hongos
Elita
Hipotiroidismo
20.00%
Vidrios
10.00%
0.00%
ID3-árboles- ID3-reglas- ID3-árboles- ID3-reglas- C4.5- C4.5-reglas- C4.5- C4.5-reglas-
ganancia ganancia proporción proporción árboles- ganancia árboles- proporción
de ganancia de ganancia ganancia propoción de ganancia
de ganancia
Figura 5.15: Porcentaje de error obtenido para cada uno de los métodos en cada dominio de datos
En la figura 5.16 se han eliminado estos dos dominios para poder analizar más claramente el porcentaje
de error en el resto de los dominios. A partir de estos dos gráficos, podríamos decir que no hay un
método4 que genere un modelo claramente superior al resto para todos los dominios. Al contrario,
podríamos decir que el porcentaje de error en todos los casos no parece depender del método utilizado
sino del dominio analizado. Es decir, el rango de porcentajes de error dentro de cada uno de los dominios
parecería estar acotado.
4
En este caso, por método nos referimos a la combinación de un algoritmo (ID3 o C4.5) utilizando ganancia o proporción de
ganancia y generando un árbol o reglas de decisión (combinación algoritmo – criterio de decisión – tipo de resultado).
7.00%
6.00%
5.00%
Cardiología
4.00% Votaciones
Hongos
3.00% Elita
Hipotiroidismo
2.00%
1.00%
0.00%
ID3-árboles- ID3-reglas- ID3-árboles- ID3-reglas- C4.5- C4.5-reglas- C4.5- C4.5-reglas-
ganancia ganancia proporción proporción árboles- ganancia árboles- proporción
de ganancia de ganancia ganancia propoción de ganancia
de ganancia
Figura 5.16: Porcentaje de error obtenido para cada uno de los métodos en distintos dominios de datos
A pesar de lo que muestra el gráfico no podemos afirmar que la relación “menor porcentaje de error a
mayor cantidad de datos de entrenamiento” sea una regla absoluta que se cumple siempre. Para ello,
habría que hacer una cantidad de pruebas que excede los alcances de este trabajo.
60.00% C4.5-árboles-ganancia
50.00% C4.5-reglas-ganancia
30.00%
C4.5-reglas-proporción de ganancia
20.00%
ID3-árboles-ganancia
10.00%
ID3-reglas-ganancia
0.00%
0 1000 2000 3000 4000 5000 6000 ID3-árboles-proporción de ganancia
-10.00%
Cantidad de datos de entrenam iento ID3-reglas-proporción de ganancia
CAPÍTULO 6
CONCLUSIONES
En este capítulo se presentan las conclusiones del trabajo realizado. Primero se destacan los
conceptos más importantes a tener en cuenta a la hora de aplicar algún método como el ID3 y el
C4.5 (Sección 6.1.1). Luego, se analiza la búsqueda que realizan estos dos métodos en el espacio
de hipótesis (Sección 6.1.2). Se extraen conclusiones a partir de los resultados obtenidos
(Sección 6.2) y se analiza la solución propuesta (Sección 6.3). Finalmente, se plantean mejoras y
temas a tener en cuenta para continuar con el desarrollo de este tipo de algoritmos de aprendizaje
aplicados a la Minería de Datos (Sección 6.4).
• El Aprendizaje de Conceptos puede verse como una búsqueda en un gran espacio de hipótesis
predefinidas [Mitchell, 1997]. En el caso de la familia TDIDT este espacio de hipótesis, está
constituido por todos los árboles de decisión posibles para los datos que se están analizando.
• Los datos ruidosos y faltantes pueden influir en la performance del algoritmo, y depende de cada
método en particular ser robusto o no ante estas situaciones.
• Los algoritmos de aprendizaje son capaces de clasificar nuevos casos, nunca vistos para ellos, porque
tienen un sesgo inductivo implícito, es decir, realizan alguna suposición que les permite construir el
modelo. En el caso de los algoritmos de la familia TDIDT, esta suposición implícita se divide en dos:
1. Los datos sobre los que se construye el problema son representativos del dominio sobre el que se
aplicará el modelo obtenido.
2. Las hipótesis (árboles de decisión en este caso) más simples se prefieren sobre las hipótesis más
complejas, es decir, se aplica la Afeitadora de Occam.
Si el espacio de hipótesis se extendiera hasta cubrir todos los casos posibles, se eliminaría este sesgo
inductivo. Sin embargo, trabajar con todos los árboles de decisión posibles para un conjunto de datos,
no permitiría realizar la clasificación de un caso no presente en los datos de entrenamiento, ya que no
sería clasificado por ningún árbol. Es decir, un modelo totalmente insesgado, no podría clasificar
nuevos casos [Mitchell, 1997].
Recordemos que no cualquier problema de Minería de Datos es apto para ser analizado mediante los
algoritmos de la familia TDIDT. Debe cumplirse que las instancias estén constituidas por pares atributo-
valor, y que los resultados de la función de clasificación sean discretos. Además, es conveniente que los
algoritmos sean capaces de trabajar con atributos ruidosos y con atributos faltantes.
A medida que exploran el espacio de hipótesis, los algoritmos analizados mantienen una sola hipótesis
actual y no todas aquellas consistentes con los datos analizados. Esto ocasiona que estos métodos no sean
capaces de representar todos los árboles consistentes con los datos de entrada.
Por otro lado, recordemos que estos métodos no tienen vuelta atrás. Es decir, una vez que se seleccionó
un atributo como nodo del árbol, éste nunca se cambiará; los algoritmos no vuelven atrás para
reconsiderar sus elecciones. Esto ocasiona que los algoritmos sean susceptibles de caer en un máximo
local y que converjan a una solución que no es globalmente óptima [Mitchell, 1997]. El C4.5 agrega un
cierto grado de reconsideración de sus elecciones en la postpoda que realiza.
Por último, cabe destacar que el ID3 y el C4.5 utilizan todos los datos de entrenamiento en cada paso para
elegir el “mejor” atributo; esta elección se realiza estadísticamente. Esto es favorable frente a otros
métodos de aprendizaje automático que analizan los datos de entrada en forma incremental. El hecho de
tener en cuenta todos los datos disponibles en cada paso, resulta en una búsqueda mucho menos sensible a
errores en casos individuales.
Como línea futura de trabajo, se propone analizar los datos de entrada con los cuatro métodos (ID3
utilizando ganancia, ID3 utilizando proporción de ganancia, C4.5 utilizando ganancia y C4.5 utilizando
proporción de ganancia) y elegir para el nuevo dominio, el modelo que presenta la menor proporción de
error. Teniendo en cuenta que si con el primer método la proporción de error es inaceptable,
probablemente también sea inaceptable para el resto de los métodos. En cuyo caso, convendría analizar el
problema con otros métodos de aprendizaje que enfoquen la resolución del mismo desde otro ángulo.
La cantidad de datos presentada como entrada de los algoritmos debe ser la mayor posible, ya que los
casos analizados parecen mostrar que proporción de error disminuye a medida que la cantidad de datos de
entrenamiento aumenta.
Cuestión 1:
¿Qué tipos de sistemas generan como resultado árboles de decisión? La familia de los Top Down
Induction Trees (TDIDT) analiza datos previamente clasificados y genera árboles de inducción para
los mismos. Nuestro análisis se enfocó en dos algoritmos de dicha familia, el ID3 creado por Quinlan
a mediados de los años 80 [Quinlan, 1986] y el C4.5 creado por Quinlan unos años más tarde
[Quinlan, 1993d]. Estos algoritmos generan como resultado árboles de decisión que pueden aplicarse
tanto por un operador humano como por una máquina para clasificar nuevos casos.
Cuestión 2:
¿Qué condiciones deben cumplir los datos para que su análisis con el ID3 o el C4.5 sea útil y
válido? Como expresamos con anterioridad, no todos los datos son aptos para ser analizados con el
ID3 y el C4.5. En líneas generales, estos algoritmos trabajan con problemas de clasificación, es decir,
problemas donde las instancias pertenecen o no a una clase. De esta manera, el árbol de decisión
generado contiene en las hojas las distintas clases a las que pueden pertenecer los datos.
Cuestión 3:
¿Qué tan fácil es para un humano trabajar con estos algoritmos? Se planteó el desarrollo de un
ambiente integrado, en el que un usuario puede fácilmente aplicar estos dos algoritmos a bases de
datos existentes. El usuario debe lidiar con una interface gráfica mucho más amigable de por sí que la
línea de comandos, en donde, con una simple marca puede aplicar el ID3 o el C4.5 a la base de datos
que seleccionó previamente.
Cuestión 4:
¿Cuáles son los resultados del sistema? Además, de producir los árboles de decisión, el sistema
genera un conjunto de reglas de producción que pueden aplicarse para clasificar nuevos casos. La
interpretación de este conjunto de reglas debe hacerse en orden: se leen hasta que el antecedente de
alguna de las reglas satisfaga el caso en cuestión, entonces se lee el consecuente para obtener la clase.
El sistema también presenta como resultado, la evaluación de los modelos generados sobre un nuevo
conjunto de datos de prueba.
Cuestión 5:
¿Cómo podemos medir la calidad de los resultados obtenidos? Una vez generado el modelo de
decisión, el usuario puede elegir la opción de evaluarlo contra un conjunto de datos de prueba,
inéditos para el sistema. Básicamente, el sistema clasifica estos datos de prueba y compara las clases
obtenidas contra las que debería haber obtenido, generando una matriz de clasificación. De esta
manera, se obtiene una medida de la calidad del sistema de información generado, bajo el supuesto
que tanto los datos de entrenamiento como los de prueba sean representativos de la realidad a la cual
el modelo de clasificación generado se verá enfrentado una vez que entre en funcionamiento.
2. Si los atributos discretos varían en forma notable en sus valores, ¿podemos estar seguros de que un
criterio como la proporción de ganancia los está evaluando de la mejor manera? La proporción de
ganancia mide la proporción de información relevante a la clasificación, que provee la división sobre
la información producida por la división en sí. El denominador crece rápidamente a medida que la
cantidad de subconjuntos se incrementa, por lo cual, el estimador deja de ser efectivo al existir
muchos valores para un atributo. Recordemos el ejemplo del cálculo de la información de la división
sobre la clave primaria de un conjunto de datos (Sección 4.2.1.1.2)
Si deseamos reducir el número de resultados de un atributo multivaluado, debemos asociar uno o más de
sus valores en una colección de valores de atributos o grupo de valores. En los primeros trabajos sobre el
tema [Hunt et al., 1966] la única forma de agrupar valores era mediante la división binaria o binarización,
como la realizada por el C4.5.
En lugar de realizar este tipo de división, los algoritmos podrían asociar cada grupo de valores con una de
las ramas en cantidad variable. En algunos dominios, la agrupación de valores podría determinarse de
acuerdo a los conocimientos sobre el dominio. De esta manera, además de mejorar el manejo de atributos
multivaluados, estaríamos incorporando información previa al sistema. De no existir agrupaciones
determinables de acuerdo al dominio, debería seguirse otro método. Si un atributo tiene n valores, existen
2n-1-1 divisiones binarias no triviales de estos valores, entonces para un valor de n grande se hace
imposible explorar todas estas combinaciones.
En cuanto al ID3, que no maneja atributos continuos, podría incorporársele la binarización utilizada por el
C4.5, o un método similar, para que pueda trabajar con atributos de este tipo. El ID3 tal como fue
presentado, no puede aplicarse a todos los dominios, además de descartarse los dominios con clases
continuas, como en el C4.5, se descartan los dominios con cualquier atributo continuo. El agregado de
una mejora de este tipo lo liberaría de esta restricción y no es demasiado cara.
La Ley de Conservación sostiene que ningún algoritmo puede superar a otro cuando la medida de
performance es la precisión de generalización esperada, sobre la suposición de que todos los resultados
posibles son igualmente probables. El hecho de promediar la performance de un algoritmo sobre todos los
casos posibles, asumiendo que todos son igualmente probables, sería como evaluar la performance de un
auto en todos los terrenos posibles, asumiendo que todos son igualmente probables. Esta afirmación es
falsa para la práctica, ya que en un dominio en particular, es claro que no todos los casos son igualmente
probables.
Quinlan, quien ha identificado familias de dominios paralelos y secuenciales, sostiene que las redes
neuronales son más eficientes en los dominios paralelos, mientras que los algoritmos que construyen
árboles de decisión obtienen mejores resultados en los dominios secuenciales. Por lo tanto, aunque un
único algoritmo de inducción puede no ser óptimo en todas las situaciones posibles, debe analizarse el
mejor algoritmo para cada situación en particular.
El campo de la Minería de Datos es un campo en pleno desarrollo, donde la mayoría de las herramientas
utilizadas provienen de otros campos relacionados como el reconocimiento de patrones, la Estadística o la
teoría de complejidad. Dada la novedad de las investigaciones en esta área quedan todavía varios
problemas por afrontar, como ser el tamaño de los datos y el ruido en los mismos.
En los últimos años se han desarrollado muchos sistemas de Minería de Datos y se espera que este
desarrollo continúe floreciendo dada la enorme cantidad de datos que son almacenados día a día, que
requiere algún tipo de análisis, entendimiento o clasificación. La diversidad de los datos, y de las técnicas
y enfoques de la minería de datos, son un desafío para el crecimiento de este área de la tecnología.
ANEXO A
En este Anexo se describen las características generales del sistema (Sección A.1) y las
funciones de cada uno de los menúes (Sección A.2), detallando las acciones que pueden
realizarse con cada una de las opciones disponibles.
Cuando el usuario ingresa al sistema debe seleccionar la tabla sobre la cual desea trabajar y la columna de
la tabla que servirá como clasificador. Dicha tabla debe estar en formato de Paradox 4. Una vez
seleccionada la tabla de trabajo, el usuario podrá elegir el método con el cual desea realizar la minería.
Los métodos disponibles son el ID3 utilizando la ganancia como criterio de decisión, el ID3 utilizando la
proporción de ganancia como criterio de decisión, el C4.5 utilizando la ganancia como criterio de
decisión y el C4.5 utilizando la proporción de ganancia con el mismo propósito.
Una vez finalizada la minería, el sistema mostrará en pantalla el árbol de decisión generado. El usuario
tendrá la opción de guardar dicho árbol en disco. Además, el sistema genera un conjunto de reglas de
decisión. El formato de estas reglas es lo suficientemente general como para que puedan aplicarse y
utilizarse a posteriori para clasificar datos cuya clase no se conoce. Es decir, tanto el árbol como las reglas
nos sirven para comprender el modelo de clasificación presente en los datos, y las reglas nos sirven para
clasificar nuevos datos de la misma base de datos que no fueron utilizados por el sistema.
A.2. FUNCIONALIDAD
Con la opción ID3 del menú Resolución, se aplica el ID3 a la tabla elegida en la pantalla de Cambio de
Tabla (Menú Opciones/Cambio de Tabla). El ID3 puede aplicarse utilizando la ganancia o la proporción
de ganancia como criterio de decisión, el criterio de decisión se escoge según la subopción del menú que
el usuario elija.
A.2.2.1.2. C4.5
Al igual que con la opción anterior, con la opción C4.5 del menú Resolución, se aplica el C4.5 a la tabla
elegida en la sección de Cambio de Tabla (Menú Opciones/Cambio de Tabla). Nuevamente, se utilizará la
ganancia o la proporción de ganancia de acuerdo a la opción del menú desplegable en que el usuario haya
marcado.
A.2.2.1.3. Salir
Cuando el usuario elige esta opción se encuentra con la pantalla de la figura A.2 donde puede elegir una
tabla y una corrida sobre la cual desea obtener una evaluación de los resultados obtenidos. Por corrida, se
entiende, una fecha y hora determinadas en las cuales se aplicó el ID3 sobre la tabla. De esta manera,
cuando el usuario hace clic sobre el botón “Aceptar” de esta pantalla, se realiza una evaluación sobre los
datos de la tabla de prueba predefinida por el usuario y se muestran la cantidad de casos clasificados
correcta e incorrectamente para cada una de las clases de la tabla.
A.2.2.2.1. C4.5
Al hacer clic sobre esta opción de menú, el usuario podrá evaluar de manera similar a la explicada en la
sección anterior, las corridas realizadas con el C4.5.
Esta función muestra los datos de la tabla sobre la que se está trabajando.
Esta opción despliega la pantalla mostrada en la figura A.3 donde el usuario puede cambiar la tabla de
trabajo y la columna del clasificador (clase) de la misma. Si el usuario hace clic en “Aceptar” esta tabla se
utilizará para todas las corridas hasta que se cierre el sistema. Si el usuario hace clic en “Aplicar” esta
tabla quedará predeterminada, con lo cual se utilizará para todas las corridas posteriores y para las
corridas realizadas cuando se utilice el sistema por próxima vez; queda determinada como tabla por
defecto.
Esta pantalla, permite cambiar los parámetros generales de trabajo que utiliza el sistema por defecto. Los
parámetros que se pueden cambiar son el sistema de minería por defecto: ID3 o C4.5; el criterio de
decisión por defecto: ganancia o proporción de ganancia; y si se realiza o no la poda del árbol de decisión
obtenido.
ANEXO B
CONJUNTOS DE DATOS
En este Anexo se detallan los conjuntos de datos utilizados para realizar las pruebas descriptas en
el Capítulo 5. Para cada uno de los dominios, se muestran los conjuntos de datos de
entrenamiento y prueba. Los datos presentados corresponden a los siguientes dominios: Créditos
(Sección B.1), Cardiología (Sección B.2), Votaciones (Sección B.3), Estudio sobre hongos
(Sección B.4), Elita: Base de Asteroides (Sección B.5), Hipotiroidismo (Sección B.6),
Identificación de vidrios (Sección B.7).
Los datos del anexo B se encuentran en el CD que acompaña este trabajo. Están divididos en archivos
según los dominios a los que pertenecen:
• Créditos: AnexoB-Creditos.doc
• Cardiología: AnexoB-Cardiologia.doc
• Votaciones: AnexoB-Votaciones.doc
• Hipotiroidismo: AnexoB-Hipotiroidismo.doc
ANEXO C
Existe un módulo principal, el Uprincipal, desde donde se realizan los llamados a los demás módulos.
Este módulo es el formulario principal con el cual interactúa el usuario. El corazón del sistema reside en
los módulos UDMID3 y UDMC45, quienes aplican el ID3 y el C4.5 respectivamente.
La figura C.1 detalla la interacción entre los módulos del sistema. Las flechas representan los mensajes
entre ellos. Hemos divididos los módulos en cuatro grupos: módulos de pantallas principales, módulos de
opciones generales, módulos de Minería de Datos y módulos de clases de datos. Los módulos de pantallas
principales son aquellos que representan la pantalla principal y el menú de acceso rápido. Su objetivo es
llamar a los otros módulos para llevar a cabo las acciones que el usuario indica en los menúes. Los
módulos de opciones generales son aquellos módulos que realizan todas las operaciones comunes al ID3
y al C4.5, como el cambio de la tabla de datos, el cambio del criterio de decisión utilizado por defecto, o
la elección de la tabla sobre la cual se desea evaluar el rendimiento, entre otros. Por otro lado, los
módulos de Minería de Datos son aquellos que construyen los árboles y las reglas de decisión a partir de
las tablas, para ello utilizan las estructuras de datos definidas en los módulos de clases de datos.
El módulo TDIDT no pertenece a ninguno de estos grupos ya es el módulo de inicio del programa.
Las funciones principales de cada uno de estos módulos se detallan en la Sección C.2.
Utilización
TDIDT.cpp Utilización
Inicializar
Aplicar
método
UPrincipal Evaluar
Evaluar Rendimiento
Mostrar árbol
RendimientoMostrar Mostrar Mostrar Árbol
rendimiento rendimiento
Determinar Evaluar
Mostrar Tabla PreferenciasRendimiento
Cambiar Tabla
C.2.1. TDIDT.cpp
Es el módulo inicial del sistema. Inicializa las variables de entorno Windows, las librerías estándar y el
módulo de la Uprincipal, el módulo UinfoGral y el módulo DMID3.
C.2.2.1. UPrincipal
Este módulo administra el menú del sistema y transforma los clics del usuario en llamados a los
procedimientos correspondientes.
C.2.2.2. UInfoGral
Este es un menú de acceso rápido que aparece apenas se inicia el programa. El usuario puede elegir
aplicar el ID3 o el C4.5 en las opciones previamente elegidas por defecto.
C.2.3.1. UBD
Lista por pantalla todos los casos de la tabla sobre la cual se está trabajando. Es un módulo de consulta
para el usuario.
C.2.3.2. UCambioTabla
Lista todos los datos de la tabla sobre la cual se está trabajando por pantalla. Permite que el usuario
seleccione otra tabla sobre la cual trabajar, y permite determinar la clase de los datos, es decir, el campo
de tabla que cumple la función de clasificador.
Si el usuario aplica los cambios, la elección de la tabla y del campo clasificador serán guardadas como
elecciones por defecto. En cambio, si el usuario acepta los cambios, la tabla y el campo escogidos serán
utilizados hasta que se cierre el programa o hasta que el usuario modifique su elección.
C.2.3.3. frmOpcionesSist
En esta pantalla el usuario puede modificar las opciones por defecto del sistema. Estas opciones son el
método (ID3 o C4.5) aplicado con mayor preferencia, el criterio de decisión (ganancia o proporción de
ganancia) utilizado y la poda o no por defecto. Estas opciones se utilizan cuando el usuario realiza la
Minería de Datos a través del menú de acceso rápido detallado en la Sección C.2.2.2.
C.2.3.4. UElegirRendimiento
Cuando el usuario desea evaluar el rendimiento de alguno de los modelos generados con el sistema,
deberá entrar en este módulo para elegir la tabla sobre la que desea trabajar y la fecha y hora en que se
generó el modelo que está tratando de evaluar. Una vez que el usuario ha determinado todos estos datos,
se realiza la evaluación del rendimiento del modelo escogido.
C.2.3.5. URendimiento
Muestra por pantalla la matriz de evaluación del rendimiento del modelo escogido por el usuario en la
pantalla descripta en la sección C.2.3.4. La matriz de evaluación muestra, para cada clase, la cantidad de
casos que fueron evaluados correctamente y la cantidad de casos que fueron evaluados incorrectamente,
según lo descripto en el Capítulo 5.
C.2.3.6. UArbol
Este módulo muestra el árbol obtenido por pantalla. El usuario puede guardar este árbol en el archivo de
texto de su preferencia.
C.2.4.1. UDMID3
Este módulo agrupa todas las funciones necesarias para aplicar el ID3 a una tabla. Se utiliza tanto para
generar el árbol de decisión como para construir las reglas de decisión y evaluar los modelos obtenidos.
C.2.4.2. UDMC45
Al igual que el módulo anterior, este módulo agrupa todas las funciones necesarias para aplicar el C4.5 a
una tabla. Genera el árbol y las reglas de decisión, y evalúa los modelos generados.
C.2.5.1. UTipos
En este modulo se definen todos los tipos de datos propios del sistema, principalmente aquellos utilizados
por el ID3.
C.2.5.2. Types.h
En este modulo se definen todos los principales tipos de datos utilizados en el C4.5.
C.2.5.3. Defns.h
Este módulo complementa al anterior, y define aún más tipos de datos utilizados por el C4.5.
C.2.5.4. Rulex.h
En este modulo se definen los tipos de datos necesarios para aplicar el C4.5
class TParametros {
public:
AnsiString ArchivoIni;
AnsiString NombreBD;
AnsiString TablaBD;
AnsiString TablaReglas;
AnsiString ColClasificador;
AnsiString ExtensionFileReglas;
TStringList *LClasificadores;
int iFormatoReglas; /* 0: Insight2+
1: KappaPC
2: ambos
3: ninguno*/
int iSistema; /* 0: ID3
1: C4.5 */
int iCriterio; /* 0: Ganancia
1: Proporción de ganancia */
int iPoda; /* 0: Si
1: No */
int iConjuntos; /* indica si las pruebas en el C4.5 se
realizan sobre grupos de atributos discretos
0: Si
1: No */
int iMinObjs; /* Cantidad mínima de objetos que debe tener
una rama en el C4.5 */
int iCF; /* Nivel de confianza para la poda en el
C4.5 */
struct tree_node {
short int flag_hoja; /* 0 si es hoja y todos los valores son "Yes"
1 si es hoja y todos los valores son "No"
2 si es hoja y sus valores son mixtos
3 si no es hoja*/
int desc;
ANEXO D
CÓDIGO FUENTE
En este anexo se presenta el código fuente del sistema desarrollado. En la sección D.1 se
presenta el código fuente del archivo TDIDT.cpp que es el archivo inicial del proyecto y es quien
inicializa al resto de los formularios requeridos. En la sección D.2 se presenta el código fuente de
las pantallas principales. A continuación, se detallan los códigos fuentes de los módulos de
opciones generales (Sección D:3), de los módulos de minería de datos (Sección D.4) y de los
módulos de clases o estructuras de datos (Sección D.5)
D.1. TDIDT.CPP
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
USERES("TDIDT.res");
USEFORM("UPrincipal.cpp", FormPrincipal);
USEFORM("UArbol.cpp", frm_Arbol);
USEFORM("UDMID3.cpp", DMID3); /* TDataModule: DesignClass */
USEUNIT("UTipos.cpp");
USEFORM("UBD.cpp", frmBD);
USEFORM("frmOpcionesSist.cpp", frmOpciones);
USEFORM("UInfoGral.cpp", frmInfoGral);
USEFORM("URendimiento.cpp", FrmRendimiento);
USEFORM("UElegirRendimiento.cpp", frmElegirRendimiento);
USEFORM("UCambioTabla.cpp", frmCambioTabla);
USEFILE("C45\defns.h");
USEFILE("C45\TYPES.h");
USEFORM("C45\UDMC45.cpp", DMC45); /* TDataModule: DesignClass */
USEFILE("C45\Rulex.h");
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TDMID3), &DMID3);
Application->CreateForm(__classid(TFormPrincipal), &FormPrincipal);
Application->CreateForm(__classid(TfrmInfoGral), &frmInfoGral);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
return 0;
}
D.2.1. UPrincipal
D.2.1.1. UPrincipal.h
//---------------------------------------------------------------------------
#ifndef UPrincipalH
#define UPrincipalH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Menus.hpp>
#include "frmOpcionesSist.h"
#include "UDMID3.h"
#include "UArbol.h"
#include "UBD.h"
#include "UInfoGral.h"
#include "UElegirRendimiento.h"
#include "URendimiento.h"
#include "UCambioTabla.h"
#include "UDMC45.h"
//---------------------------------------------------------------------------
class TFormPrincipal : public TForm
{
__published: // IDE-managed Components
TMainMenu *MenuPpal;
TMenuItem *mnuGral;
TMenuItem *mnuSep;
TMenuItem *mnu_Salir;
TMenuItem *mnuID3;
TMenuItem *mnuC45;
TMenuItem *mnuID3G;
TMenuItem *mnuID3GR;
TMenuItem *mnuC45G;
TMenuItem *mnuC45GR;
TMenuItem *mnuAyuda;
TMenuItem *mnuHelpIndex;
TMenuItem *mnuSep3;
TMenuItem *mnuAcercaDe;
TMenuItem *mnuEvaluacion;
TMenuItem *mnuEvalID3;
TMenuItem *mnuEvalC45;
TMenuItem *mnuOpciones;
TMenuItem *mnuTabla;
TMenuItem *mnuCambiarTabla;
TMenuItem *mnuParametros;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
void __fastcall mnu_ID3GainClick(TObject *Sender);
void __fastcall mnu_ID3GainRatioClick(TObject *Sender);
void __fastcall mnu_SalirClick(TObject *Sender);
void __fastcall mnuID3GClick(TObject *Sender);
void __fastcall mnuID3GRClick(TObject *Sender);
void __fastcall FormShow(TObject *Sender);
void __fastcall mnuEvalID3Click(TObject *Sender);
void __fastcall mnuTablaClick(TObject *Sender);
void __fastcall mnuParametrosClick(TObject *Sender);
void __fastcall mnuCambiarTablaClick(TObject *Sender);
void __fastcall mnuEvalC45Click(TObject *Sender);
void __fastcall mnuC45GClick(TObject *Sender);
void __fastcall mnuC45GRClick(TObject *Sender);
private:
TParametros *Params;
public:
__fastcall TFormPrincipal(TComponent* Owner);
// void ActualizarParametros(TStringList *ListaParam);
/* Esta función actualiza los parámetros del formulario
La lista de parámetros debe tener el siguiente orden:
ExtensionArchivoReglas; AliasBD; TablaBD
ColClasificador; Lista de Clasificadores*/
//---------------------------------------------------------------------------
D.2.1.1. UPrincipal.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UPrincipal.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFormPrincipal *FormPrincipal;
Tfrm_Arbol *frm_Arbol;
TfrmOpciones *frmOpciones;
TfrmElegirRendimiento *frmElegirRendimiento;
TFrmRendimiento *FrmRendimiento;
TfrmBD *frmBD;
TfrmCambioTabla *frmCambioTabla;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// IMPLEMENTACION
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Cursor=crHourGlass;
DMID3->RecibirParametros(Params);
pRoot=DMID3->Principal(MedidorGanancia);
DMID3->PruneTree(pRoot);
DMID3->InformarValores(VecVal);
DMID3->InformarDescrip(VecDesc);
frm_Arbol->Cursor=crArrow;
FormPrincipal->Cursor=crArrow;
for (i=0;i<MAX_NIVELES;i++)
{
VecVal[i].~AnsiString();
VecDesc[i].~AnsiString();
}
}
//---------------------------------------------------------------------------
if (EvalRendimiento==0)
{
FrmRendimiento= new TFrmRendimiento(this);
FrmRendimiento->Cursor=crHourGlass;
DMID3->EvaluarReglas(frmElegirRendimiento->CodigoReglas);
FrmRendimiento->Cursor=crArrow;
}
}
//---------------------------------------------------------------------------
if (EvalRendimiento==0)
{
FrmRendimiento= new TFrmRendimiento(this);
FrmRendimiento->Cursor=crHourGlass;
DMC45->EvaluacionRendimiento();
FrmRendimiento->Cursor=crArrow;
}
}
//---------------------------------------------------------------------------
D.2.2. UInfoGral
D.2.2.1. UInfoGral.h
//---------------------------------------------------------------------------
#ifndef UInfoGralH
#define UInfoGralH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Buttons.hpp>
#include "UPrincipal.h"
//---------------------------------------------------------------------------
class TfrmInfoGral : public TForm
{
__published: // IDE-managed Components
TLabel *lblTabla;
TLabel *lblClase;
TEdit *txtClase;
TEdit *txtTabla;
TBitBtn *BtnC45;
TBitBtn *BtnID3;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall BtnID3Click(TObject *Sender);
private:
short MedidorGanancia;
public:
__fastcall TfrmInfoGral(TComponent* Owner);
void RecibirParametros(AnsiString tabla, AnsiString clase, short Ganancia);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmInfoGral *frmInfoGral;
//---------------------------------------------------------------------------
#endif
D.2.2.2. UInfoGral.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UInfoGral.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmInfoGral *frmInfoGral;
//---------------------------------------------------------------------------
__fastcall TfrmInfoGral::TfrmInfoGral(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmInfoGral::FormClose(TObject *Sender,
TCloseAction &Action)
{
Action = caFree;
}
//---------------------------------------------------------------------------
pForm=FormPrincipal;
pForm->Cursor=crHourGlass;
pForm->ID3(MedidorGanancia);
pForm->Cursor=crArrow;
}
//---------------------------------------------------------------------------
D.3.1. UBD
D.3.1.1. UBD.h
//---------------------------------------------------------------------------
#ifndef UBDH
#define UBDH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <Db.hpp>
#include <DBGrids.hpp>
#include <DBTables.hpp>
#include <Grids.hpp>
#include "UDMID3.h"
//---------------------------------------------------------------------------
class TfrmBD : public TForm
{
__published: // IDE-managed Components
TDBGrid *DBGrid;
TDataSource *DSTabla;
TTable *Tabla;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:
/*Parámetros de la base sobre la que trabajaremos*/
TParametros *Params;
public:
__fastcall TfrmBD(TComponent* Owner);
void RecibirParametros(TParametros *Parametros);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmBD *frmBD;
//---------------------------------------------------------------------------
#endif
D.3.1.2. UBD.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UBD.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmBD *frmBD;
//---------------------------------------------------------------------------
__fastcall TfrmBD::TfrmBD(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmBD::FormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree;
}
//---------------------------------------------------------------------------
Tabla->DatabaseName=Params->NombreBD;
Tabla->TableName=Params->TablaBD;
Tabla->Active=true;
}
D.3.2. UCambioTabla
D.3.2.1. UCambioTabla.h
//---------------------------------------------------------------------------
#ifndef UCambioTablaH
#define UCambioTablaH
//---------------------------------------------------------------------------
#include <Buttons.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <Db.hpp>
#include <DBGrids.hpp>
#include <DBTables.hpp>
#include <Dialogs.hpp>
#include <Grids.hpp>
#include <StdCtrls.hpp>
#include "UTipos.h"
#include "UDMID3.h"
#include "UInfoGral.h"
//---------------------------------------------------------------------------
class TfrmCambioTabla : public TForm
{
__published: // IDE-managed Components
TDBGrid *DBGrid;
TDataSource *DSTabla;
TTable *Tabla;
TEdit *txt_Tabla;
TBitBtn *Btn_fod;
TOpenDialog *fod_TablaBD;
TLabel *lbl_TablaBD;
TLabel *lbl_nbeColClasif;
TComboBox *cbo_ColClasif;
TBitBtn *btn_Aceptar;
TBitBtn *btn_Cancelar;
TBitBtn *btn_Aplicar;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall Btn_fodClick(TObject *Sender);
void __fastcall btn_CancelarClick(TObject *Sender);
void __fastcall btn_AceptarClick(TObject *Sender);
void __fastcall btn_AplicarClick(TObject *Sender);
void __fastcall txt_TablaChange(TObject *Sender);
void __fastcall cbo_ColClasifChange(TObject *Sender);
private:
/*Parámetros de la base sobre la que trabajaremos*/
TParametros *Params;
void CargarCombo();
int ActualizarCambios(); /*Devuelve:
0: si los cambios fueron exitosos
1: si hubo problemas*/
public:
__fastcall TfrmCambioTabla(TComponent* Owner);
void RecibirParametros(TParametros *Parametros);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmCambioTabla *frmCambioTabla;
//---------------------------------------------------------------------------
#endif
D.3.2.2. UCambioTabla.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UCambioTabla.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmCambioTabla *frmCambioTabla;
//---------------------------------------------------------------------------
__fastcall TfrmCambioTabla::TfrmCambioTabla(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmCambioTabla::FormClose(TObject *Sender,
TCloseAction &Action)
{
Action = caFree;
}
//---------------------------------------------------------------------------
void TfrmCambioTabla::RecibirParametros(TParametros *Parametros)
{
Params=Parametros;
//---------------------------------------------------------------------------
void __fastcall TfrmCambioTabla::Btn_fodClick(TObject *Sender)
{
bool res;
int index;
AnsiString TablaBD;
res=fod_TablaBD->Execute();
if (res)
{
TablaBD=fod_TablaBD->FileName;
index=TablaBD.LastDelimiter("\\");
TablaBD=TablaBD.SubString(index+1,TablaBD.Length()-index);
txt_Tabla->Text=TablaBD;
Tabla->Active=false;
Tabla->TableName=TablaBD;
Tabla->Active=true;
cbo_ColClasif->Clear();
CargarCombo();
}
TablaBD.~AnsiString();
}
//---------------------------------------------------------------------------
void __fastcall TfrmCambioTabla::btn_CancelarClick(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
i++;
}
fin=false;
i=0;
while (i<=cbo_ColClasif->Items->Count && !fin)
{
if (AnsiCompareText(cbo_ColClasif->Items->Strings[i].Trim(),Params-
>ColClasificador.Trim())==0)
{
cbo_ColClasif->ItemIndex=i;
fin=true;
}
i++;
}
delete(listaCpos);
}
//---------------------------------------------------------------------------
int TfrmCambioTabla::ActualizarCambios() /*Devuelve:
0: si los cambios fueron exitosos
1: si hubo problemas*/
{
AnsiString TablaAnterior;
AnsiString sqlstring;
TQuery *QValoresClasif=new TQuery(frmCambioTabla);
int res;
try
{
res=0;
if (Params->TablaBD!=txt_Tabla->Text) //actualizamos los datos de la tabla
{
Params->TablaBD=txt_Tabla->Text;
TablaAnterior=DMID3->TTennis->TableName;
DMID3->TTennis->Active=false;
DMID3->TTennis->DatabaseName=Params->NombreBD;
DMID3->TTennis->TableName=Params->TablaBD;
DMID3->TTennis->Active=true;
if (Params->ColClasificador!=cbo_ColClasif->Text)
//Cambiamos la columna del clasificador
{
if (cbo_ColClasif->Text.Trim()=="")
{
ShowMessage("El nombre de la Columna del Clasificador no puede
quedar en blanco");
cbo_ColClasif->SetFocus();
res=1;
}
else
{
Params->ColClasificador=cbo_ColClasif->Text;
//Actualizamos la lista de Clasificadores
sqlstring="SELECT DISTINCT(" + Params->ColClasificador + ") as
Clasif FROM " + Params->TablaBD;
QValoresClasif->DatabaseName=Params->NombreBD;
QValoresClasif->SQL->Clear();
QValoresClasif->SQL->Add(sqlstring);
QValoresClasif->Open();
QValoresClasif->Last();
QValoresClasif->First();
Params->LClasificadores->Clear();
while (!QValoresClasif->Eof)
{
Params->LClasificadores->Add(QValoresClasif-
>FieldByName("Clasif")->AsString);
QValoresClasif->Next();
}
res=0;
}
}
frmInfoGral->RecibirParametros(Params->TablaBD,Params->ColClasificador,
Params->iCriterio);
}
catch(...)
{
ShowMessage("Problemas con la Base de Datos, no podrá cambiar de tabla");
cbo_ColClasif->Clear();
CargarCombo();
res=1;
}
sqlstring.~AnsiString();
TablaAnterior.~AnsiString();
delete(QValoresClasif);
return(res);
}
//---------------------------------------------------------------------------
void __fastcall TfrmCambioTabla::btn_AplicarClick(TObject *Sender)
{
ActualizarCambios();
Params->GrabarParams();
btn_Aplicar->Enabled=false;
}
D.3.3. frmOpcionesSist
D.3.3.1. frmOpcionesSist.h
//---------------------------------------------------------------------------
#ifndef frmOpcionesSistH
#define frmOpcionesSistH
//---------------------------------------------------------------------------
#include <Buttons.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <ExtCtrls.hpp>
#include <StdCtrls.hpp>
#include "UPrincipal.h"
#include "UTipos.h"
//--------------------------------------------------------------------------
class TfrmOpciones : public TForm
{
__published: // IDE-managed Components
TRadioGroup *rgSistema;
TRadioGroup *rgCriterio;
TRadioGroup *rgPoda;
TBitBtn *btn_Aplicar;
TBitBtn *btn_Cancelar;
TBitBtn *btn_Aceptar;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall btn_CancelarClick(TObject *Sender);
void __fastcall btn_AceptarClick(TObject *Sender);
void __fastcall btn_AplicarClick(TObject *Sender);
private: // User declarations
TParametros *Params;
public: // User declarations
__fastcall TfrmOpciones(TComponent* Owner);
void RecibirParametros(TParametros *Parametros);
};
//---------------------------------------------------------------------------
extern PACKAGE TfrmOpciones *frmOpciones;
//---------------------------------------------------------------------------
#endif
D.3.3.2. frmOpcionesSist.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "frmOpcionesSist.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmOpciones *frmOpciones;
//---------------------------------------------------------------------------
__fastcall TfrmOpciones::TfrmOpciones(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
Close();
}
//---------------------------------------------------------------------------
cerrar=0;
Params->iSistema=rgSistema->ItemIndex;
Params->iCriterio=rgCriterio->ItemIndex;
Params->iPoda=rgPoda->ItemIndex;
Params->iFormatoReglas=1;
if (cerrar==0)
Close();
}
//---------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
D.3.4. UElegirRendimiento
D.3.4.1. UElegirRendimiento.h
//---------------------------------------------------------------------------
#ifndef UElegirRendimientoH
#define UElegirRendimientoH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <ExtCtrls.hpp>
#include <Buttons.hpp>
#include <DBTables.hpp>
#include <Dialogs.hpp>
#include "UPrincipal.h"
//---------------------------------------------------------------------------
class TfrmElegirRendimiento : public TForm
{
__published: // IDE-managed Components
TRadioGroup *rgCorrida;
TGroupBox *gbSeleccionar;
TLabel *lblTabla;
TLabel *lblFecha;
TComboBox *cbTabla;
TComboBox *cbFecha;
TBitBtn *btn_Cancelar;
TBitBtn *btn_Aceptar;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall btn_CancelarClick(TObject *Sender);
void __fastcall rgCorridaClick(TObject *Sender);
void __fastcall btn_AceptarClick(TObject *Sender);
void __fastcall cbTablaChange(TObject *Sender);
private:
AnsiString Base;
int Tipo;
void CargarCboFechora();
public:
int CodigoReglas;
__fastcall TfrmElegirRendimiento(TComponent* Owner);
D.3.4.2. UElegirRendmiento.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UElegirRendimiento.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmElegirRendimiento *frmElegirRendimiento;
//---------------------------------------------------------------------------
__fastcall TfrmElegirRendimiento::TfrmElegirRendimiento(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TfrmElegirRendimiento::FormClose(TObject *Sender,
TCloseAction &Action)
{
Action = caFree;
}
//---------------------------------------------------------------------------
void __fastcall TfrmElegirRendimiento::btn_CancelarClick(TObject *Sender)
{
FormPrincipal->EvalRendimiento=1;
Close();
}
//---------------------------------------------------------------------------
void __fastcall TfrmElegirRendimiento::rgCorridaClick(TObject *Sender)
{
if (rgCorrida->ItemIndex==1)
gbSeleccionar->Enabled=true;
else
gbSeleccionar->Enabled=false;
}
//---------------------------------------------------------------------------
try {
Base=NombreBD;
Tipo=iTipo;
QTabla->DatabaseName=NombreBD;
QTabla->SQL->Clear();
QTabla->SQL->Add(sqlstring);
QTabla->Open();
QTabla->First();
while (!QTabla->Eof)
{
cbTabla->Items->Add(QTabla->FieldByName("Nombre_Tabla")->AsString);
QTabla->Next();
}
cbTabla->ItemIndex=0;
CargarCboFechora();
sqlstring.~AnsiString();
delete(QTabla);
}
catch(...)
{ShowMessage("No se pudieron obtener los datos de la tabla de evaluación");
sqlstring.~AnsiString();
delete(QTabla);}
}
//---------------------------------------------------------------------------
void __fastcall TfrmElegirRendimiento::btn_AceptarClick(TObject *Sender)
{
TQuery *QTabla=new TQuery(frmElegirRendimiento);
AnsiString sqlstring;
try {
QTabla->DatabaseName=Base;
if (rgCorrida->ItemIndex==0)
{
/* sqlstring="SELECT Max(Codigo)as Codigo FROM UpdatesHeader.db WHERE
Nombre_Tabla='" +
cbTabla->Text +"' AND Metodo='";
*/ sqlstring="SELECT Max(Codigo) as Codigo FROM UpdatesHeader.db WHERE
Metodo='";
if (Tipo==0)
AppendStr(sqlstring,"ID3'");
else
AppendStr(sqlstring,"C45'");
}
else
{
sqlstring="SELECT Codigo FROM UpdatesHeader.db WHERE Nombre_Tabla='" +
cbTabla->Text +"' AND Metodo='";
if (Tipo==0)
AppendStr(sqlstring,"ID3'");
else
AppendStr(sqlstring,"C45'");
AppendStr(sqlstring,"AND Fecha='");
AppendStr(sqlstring,cbFecha->Text.SubString(0,8));
AppendStr(sqlstring,"' AND Hora='");
AppendStr(sqlstring,Trim(cbFecha->Text.SubString(9,cbFecha-
>Text.Length()-8)));
AppendStr(sqlstring,"'");
}
QTabla->SQL->Clear();
QTabla->SQL->Add(sqlstring);
QTabla->Open();
CodigoReglas=QTabla->FieldByName("Codigo")->AsInteger;
QTabla->First();
sqlstring.~AnsiString();
delete(QTabla);
}
catch(...)
{ShowMessage("Problemas con la Base de Datos, no se pudo recuperar la tabla a
procesar.");
sqlstring.~AnsiString();
delete(QTabla);}
}
//---------------------------------------------------------------------------
void TfrmElegirRendimiento::CargarCboFechora()
{TQuery *QTabla=new TQuery(frmElegirRendimiento);
AnsiString sqlstring;
if (cbTabla->Text!="")
{
try {
QTabla->DatabaseName=Base;
QTabla->SQL->Clear();
QTabla->SQL->Add(sqlstring);
QTabla->Open();
QTabla->First();
cbFecha->Clear();
while (!QTabla->Eof)
{
sqlstring=QTabla->FieldByName("Fecha")->AsString + " " + QTabla-
>FieldByName("Hora")->AsString;
cbFecha->Items->Add(sqlstring);
QTabla->Next();
}
cbFecha->ItemIndex=0;
}
catch(...)
{ShowMessage("No se pudieron obtener los datos de la tabla de evaluación");}
}
sqlstring.~AnsiString();
delete(QTabla);
}
//---------------------------------------------------------------------------
D.3.5. URendimiento
D.3.5.1. URendimiento.h
//---------------------------------------------------------------------------
#ifndef URendimientoH
#define URendimientoH
#include <Classes.hpp>
#include <Controls.hpp>
#include <Grids.hpp>
//---------------------------------------------------------------------------
class TFrmRendimiento : public TForm
{
__published: // IDE-managed Components
TStringGrid *GridResultados;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private: // User declarations
public: // User declarations
__fastcall TFrmRendimiento(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TFrmRendimiento *FrmRendimiento;
//---------------------------------------------------------------------------
#endif
D.3.5.2. URendmiento.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "URendimiento.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TFrmRendimiento *FrmRendimiento;
//---------------------------------------------------------------------------
__fastcall TFrmRendimiento::TFrmRendimiento(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TFrmRendimiento::FormClose(TObject *Sender,
TCloseAction &Action)
{
Action = caFree;
}
//---------------------------------------------------------------------------
D.3.6. Uarbol
D.3.6.1. UArbol.h
//---------------------------------------------------------------------------
#ifndef UArbolH
#define UArbolH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <ComCtrls.hpp>
#include <Controls.hpp>
#include <Dialogs.hpp>
#include "UPrincipal.h"
#include "UTipos.h"
//--------------------------------------------------------------------------
class Tfrm_Arbol : public TForm
{
__published: // IDE-managed Components
TTreeView *ArbolRes;
TOpenDialog *fod_Archivo;
void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
private:
/*String que tendrá los valores posibles para el clasificador*/
TStringList *LClasificadores;
TVecValores VecValores;
TVecValores VecDescriptores;
};
//---------------------------------------------------------------------------
extern PACKAGE Tfrm_Arbol *frm_Arbol;
//---------------------------------------------------------------------------
#endif
D.3.6.2. UArbol.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UArbol.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
Tfrm_Arbol *frm_Arbol;
TTreeNode *pNivel[MAX_CANT_DESCRIPTORES]; //para la visualización
//---------------------------------------------------------------------------
__fastcall Tfrm_Arbol::Tfrm_Arbol(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
int Tfrm_Arbol::ObtenerNuevoValor() /*numeración de valores del VecValores para
la impresión del árbol*/
{
static int i=0;
i++;
return(i);
}
//---------------------------------------------------------------------------
//-------------------- IMPRESIÓN DEL ÁRBOL EN PANTALLA ----------------------
//---------------------------------------------------------------------------
/******************************************************************************
******************************************************************************/
void Tfrm_Arbol::PrintTree(tree_ptr Tree)
{
int nivel;
TTreeNode *pNivel[MAX_CANT_DESCRIPTORES]; //para la visualización
nivel=0;//nivel de la raíz
pNivel[nivel]= new TTreeNode(ArbolRes->Items);
PrintSubtree(Tree, nivel);
// frm_Arbol->ArbolRes->Width=Width-60;
ArbolRes->Left=Left+30;
ArbolRes->Top=Top+50;
// frm_Arbol->ArbolRes->Height=Height-100;
ArbolRes->FullExpand();
ArbolRes->Visible=true;
}
if (nivel!=0)
{
val=ObtenerNuevoValor();
nodo=VecDescriptores[val];
AppendStr(nodo, " = ");
AppendStr(nodo, VecValores[val]);
pNivel[nivel]=ArbolRes->Items->AddChild(pNivel[nivel-1],nodo);
}
if (Tree->flag_hoja!=3) //es hoja
ArbolRes->Items->AddChild(pNivel[nivel], LClasificadores->Strings[Tree-
>flag_hoja]);
else //no es hoja
{
i=0;
nivel++;
while ((Tree->pHijos[i]!=NULL)&&(i<MAX_HIJOS))
{
PrintSubtree(Tree->pHijos[i],nivel);
i++;
}
}//end else
nodo.~AnsiString();
return;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void Tfrm_Arbol::RecibirParametros(TStringList *LClasif,TVecValores VecVal,
TVecValores VecDescrip)
{
int i;
LClasificadores=LClasif;
for (i=0;i<MAX_NIVELES;i++)
{
VecValores[i]=VecVal[i];
VecDescriptores[i]=VecDescrip[i];
}
}
D.4.1. UDMID3
D.4.1.1. UDMID3.h
//---------------------------------------------------------------------------
#ifndef UDMID3H
#define UDMID3H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Db.hpp>
#include <DBTables.hpp>
#include <fstream.h>
#include <math.hpp>
#include <Dialogs.hpp>
#include "UTipos.h"
#include "URendimiento.h"
//---------------------------------------------------------------------------
class TDMID3 : public TDataModule
{
__published: // IDE-managed Components
TTable *TTennis;
TDataSource *DSTTennis;
TQuery *QGral;
TQuery *QUpdates;
void __fastcall DMID3Destroy(TObject *Sender);
private:
TParametros *Params;
short int EvaluarResultados(AnsiString NombreTabla);
public:
__fastcall TDMID3(TComponent* Owner);
//----------------------------------------------------------------------------
//--------------------DECLARACION DE FUNCIONES--------------------------------
//----------------------------------------------------------------------------
tree_ptr Principal ( short int MedidorGanancia); /*Módulo principal*/
void RecibirParametros(TParametros *Parametros);
D.4.1.2. UDMID3.cpp
//---------------------------------------------------------------------------
#pragma hdrstop
#include "UDMID3.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TDMID3 *DMID3;
tree_ptr pRoot;
//---------------------------------------------------------------------------
__fastcall TDMID3::TDMID3(TComponent* Owner)
: TDataModule(Owner)
{
}
//---------------------------------------------------------------------------
//==============================VARIABLES======================================
/*String que tendrá los valores posibles para el clasificador*/
TVecValores VecValores;
TVecValores VecDescriptores;
UsoGain=MedidorGanancia;
error=BuscarPK(ValidDs);
if (error!=1)
{
DateSeparator='-';
TimeSeparator='_';
strcat(outFile, DateTimeToStr(Now()).c_str());
strcat(outFile, Params->TablaBD.c_str());
strcat(outFile, ".log");
FVitacora.open(outFile, ios::out);
pRoot=BuildTree(ObtenerNuevoNivel(), pValidDs);
FVitacora.close();
return(pRoot);
}
else
{
pRoot=NULL;
return(pRoot);
}
}
Params=Parametros;
TTennis->DatabaseName=Params->NombreBD;
TTennis->TableName=Params->TablaBD;
TTennis->Active=true;
sqlstring.~AnsiString();
}
/******************************************************************************
Verifica la uniformidad de un conjunto de filas, por uniformidad nos referimos
al hecho que todos los valores del clasificador para dicho conjunto sean iguales
Devuelve:
0 si todos los valores son "Yes"
1 si todos los valores son "No"
3 si los valores no son uniformes
4 si hubo un error
******************************************************************************/
short int TDMID3::EsUniforme(int nivelArbol)
{//begin EsUniforme
/******************************************************************************
Obtiene la cantidad de descriptores presentes en la tabla
que será la cantidad de atributos menos 2: el clasificador y el atributo nivel
******************************************************************************/
int TDMID3::ObtenerCantDescr()
{
int cant_descr;
TTennis->Open();
cant_descr=TTennis->FieldCount;
cant_descr-=2;
return(cant_descr);
}
/******************************************************************************
/*Busca claves primarias en la tabla, para no trabajar sobre esos campos por
no ofrecer ninguna ganancia de información
Devuelve: 0 si no hubo problemas
1 si hubo algún error
/******************************************************************************/
int TDMID3::BuscarPK(short int ValidDs[])
{
AnsiString sqlstring;
int i, cantCampos, cantReg;
cantCampos=TTennis->FieldCount;
cantReg=TTennis->RecordCount;
try
{
for (i=0; i<cantCampos-1; i++)
{
sqlstring="SELECT COUNT(DISTINCT ";
AppendStr(sqlstring,TTennis->Fields->Fields[i]->FieldName);
AppendStr(sqlstring," ) as Cant FROM ");
AppendStr(sqlstring, Params->TablaBD);
QGral->SQL->Clear();
QGral->SQL->Add(sqlstring);
sqlstring.~AnsiString();
QGral->Open();
if (QGral->FieldByName("Cant")->AsInteger==cantReg)
ValidDs[i]=0; //no es un desc válido por ser PK
QGral->Close();
}
return(0);
}
catch(...)
{
sqlstring.~AnsiString();
ShowMessage("No se pudo abrir el Query");
return(1);
}
}
//---------------------------------------------------------------------------
/******************************************************************************
Función recursiva que arma el árbol de decisión según el
algoritmo ID3
******************************************************************************/
TTablaCorrel Tabla;
TStringList *valores = new TStringList();
TStringList *MaxValores = new TStringList();
cant_clas=Params->LClasificadores->Count;
/* Inicializar Nodo*/
pNewNode=(DECISION_TREE)malloc(sizeof(struct tree_node));
if (pNewNode==0)
{
ShowMessage("No hay suficiente espacio en memoria para la operación");
}
else
{
for (i=0; i<MAX_HIJOS; i++)
pNewNode->pHijos[i]=0;
pNewNode->desc=-1;
FVitacora<<"******************************************************";
FVitacora<<endl;
FVitacora<<"Nivel=";
FVitacora<<nivel<<endl;
/* EsUniforme?*/
unif=EsUniforme(nivel);
if (unif!=4) /*no hubo un error*/
if (unif!=3) /*es hoja*/
{
/* Si es hoja, tabularla*/
pNewNode->flag_hoja=unif; /*unif valdrá 0 si todos los valores
FVitacora<<desc_max_ganancia;
FVitacora<<" ";
FVitacora<<max_ganancia<<endl;
if (exito==0)
exito=Particionar(desc_max_ganancia, nivel,
pNewNode,MaxValores);
delete MaxValores;
return(pNewNode);
}
else /*se acabaron los descriptores, entonces habrá errores en
el resultado*/
{
pNewNode->flag_hoja=2;
delete valores;
delete MaxValores;
return pNewNode;
}
} // fin else no es hoja
}//fin else
}
//---------------------------------------------------------------------------
/******************************************************************************
Esta función proporciona un identificador único para cada uno de los niveles
del árbol
******************************************************************************/
int TDMID3::ObtenerNuevoNivel() /*numeración de niveles para el árbol*/
{
static int i=-1;
i++;
return(i);
}
//------------------------------------------------------------------------------
// CÁLCULO DE LA ENTROPÍA
//------------------------------------------------------------------------------
/******************************************************************************
Esta función devuelve la información brindada por un término
cant: cantidad de ocurrencias de ese elemento
total: total de elementos en el conjunto
InfoTermino=-(cant/total)log2(cant/total)
******************************************************************************/
if ((cant!=0)&&(total!=0))
{
temp1=float(cant)*0.1;
temp2=float(total)*0.1;
temp=temp1/temp2;
if (temp==0)
temp=0;
else
temp*=Log2(temp);
temp=-temp;
return(temp);
}
else
return(0);
}
/******************************************************************************
Esta función devuelve la información de un conjunto antes de realizar la partición
cant_clasificadores: cantidad de valores posibles para los clasificadores, es
decir, la cantidad de filas de la tabla de correlación
cant_descriptores: cantidad de descriptores válidos para el nivel del árbol que
se está analizando, es decir, la cantidad de columnas
de la tabla de correlación
Ianterior(T)=Sum(i=1, i<k)(-(cant/Total)log2(cant/total))
******************************************************************************/
float TDMID3::Ianterior(int cant_clasificadores, int cant_descriptores, TTablaCorrel
tabla)
{
float auxI=0;
float temp1, temp2;
int i;
return(auxI);
}
/******************************************************************************
Esta función devuelve la información de un conjunto después de realizar
la partición según el atributo por el que se armó la tabla
cant_clasificadores: cantidad de valores posibles para los clasificadores, es
decir, la cantidad de filas de la tabla de correlación
cant_descriptores: cantidad de descriptores válidos para el nivel del árbol que
se está analizando, es decir, la cantidad de columnas
de la tabla de correlación
Iposterior(T)=Sum(i=1, i<k)(-(cant/Total)log2(cant/total))
******************************************************************************/
Iaux=0;
for (i=0; i<cant_descriptores; i++)
{
Itemp=0;
for (j=0; j<cant_clasificadores; j++)
{
Itemp+=InfoTermino(tabla[j][i], tabla[cant_clasificadores][i]);
}
if (tabla[cant_clasificadores][cant_descriptores]!=0)
{
Iaux+=(float(tabla[cant_clasificadores][i])/float(tabla[cant_clasificadores][cant_descri
ptores]))*Itemp;
}
}
return(Iaux);
}
/******************************************************************************
Ganancia debida a la partición del conjunto según el atributo en
cuestión
Gain=Ianterior-Iposterior;
******************************************************************************/
float TDMID3::Gain(float Ianterior, float Iposterior)
{
return(Ianterior-Iposterior);
}
/******************************************************************************
Información debida a la partición realizada
Sum(i=1, k)(|cant elementos según la partición i||cant total de elementos|*
*InfoTermino);
******************************************************************************/
SIaux=0;
for (i=0; i<cant_descriptores; i++)
{
if (tabla[cant_clasificadores][cant_descriptores]!=0)
{
SIaux+=InfoTermino(tabla[cant_clasificadores][i],tabla[cant_clasificadores][cant_descrip
tores])*(float(tabla[cant_clasificadores][i])/float(tabla[cant_clasificadores][cant_desc
riptores]));
}
}
return(SIaux);
}
/******************************************************************************
Proporción de ganancia debida a la partición del conjunto según el atributo en
cuestión
Gain=Gain/SplitInfo;
******************************************************************************/
float TDMID3::GainRatio(float Ganancia, float SplitInformation)
{
if (SplitInformation!=0)
return(Ganancia/SplitInformation);
else
return(0);
}
/******************************************************************************
Utiliza todas las funciones anteriores para obtener el Gain Ratio de
la tabla recibida como parámetro
******************************************************************************/
float TDMID3::GananciaEntropia(int cant_clasificadores, int cant_descriptores,
TTablaCorrel tabla)
{
float anterior, posterior, gano;
float splitI, auxGE;
FVitacora<<"Gain=";
FVitacora<<gano<<endl;
FVitacora<<"Gain Ratio=";
FVitacora<<auxGE<<endl;
FVitacora<<"--------------------------------------";
FVitacora<<endl;
if (UsoGain==0)
return(gano);
else
return(auxGE);
}
//---------------------------------------------------------------------------
/******************************************************************************
Arma la tabla de correlación
Nivel: es el nivel del árbol que se está analizando
desc: es el descriptor según el que se quiere armar la tabla
Si la función devuelve un 0 significa que no hubo problemas
Hubo problemas en todo otro caso
******************************************************************************/
int TDMID3::ArmarTabla(TTablaCorrel& Tabla, int Nivel, int desc, TStringList *valores)
{
int c, d, maxd;
int cant_clas;
int CantTotal;
AnsiString sqlstring1("");
AnsiString sqlstring2("");
valores->Clear();
cant_clas=Params->LClasificadores->Count;
/*Obtenemos el nombre del atributo "desc" de la tabla*/
TTennis->GetFieldNames(lista);
//======================================================================
//=================== MANEJO DEL QUERY PARA ============================
//===OBTENER TODOS LOS VALORES POSIBLES DE UN ATRIBUTO =================
//======================================================================
try //Intentamos abrir el Query
{
/*Obtenemos todos los valores posibles para el atributo descriptor*/
sqlstring1="SELECT DISTINCT(";
AppendStr(sqlstring1,lista->Strings[desc]);
AppendStr(sqlstring1,") as descriptor FROM ");
AppendStr(sqlstring1, Params->TablaBD);
AppendStr(sqlstring1," WHERE Nivel=");
AppendStr(sqlstring1,IntToStr(Nivel));
QGral->SQL->Clear();
QGral->SQL->Add(sqlstring1);
QGral->Open();
QGral->First();
sqlstring1.~AnsiString();
FVitacora<<"Descriptor=";
FVitacora<<lista->Strings[desc].c_str()<<endl;
d=0;
while (!(QGral->Eof)) /*Obtenemos todos los valores
posbiles para el descriptor en la lista valores*/
{
valores->Add(QGral->FieldByName("Descriptor")->AsString);
d++;
QGral->Next();
}//fin del while
maxd=d-1;
QGral->Close();
}
catch(...) //De la apertura del Query
{
ShowMessage("No se pudo abrir el Query para obtener todos los valores de un
descriptor");
delete lista;
sqlstring1.~AnsiString();
sqlstring2.~AnsiString();
return(1);
}
//======================================================================
//=================== MANEJO DEL QUERY =============================
//===========CARGAMOS LOS VALORES INDIVIDUALES DE Tabla[c][d]===========
//==========================(NO LOS TOTALES)============================
//======================================================================
try //Intentamos abrir el QGral
{
d=0;
while (d<=maxd)
{
FVitacora<<" ";
FVitacora<<valores->Strings[d].c_str();
d++;
}
c=0;
while (c<cant_clas)
{
d=0;
FVitacora<<endl;
FVitacora<<Trim(Params->LClasificadores->Strings[c]).c_str();
FVitacora<<" ";
while (d<=maxd)
{
sqlstring2="SELECT (COUNT(*)) as Cant FROM ";
AppendStr(sqlstring2, Params->TablaBD);
AppendStr(sqlstring2," WHERE Nivel=");
AppendStr(sqlstring2,IntToStr(Nivel));
AppendStr(sqlstring2," AND ");
AppendStr(sqlstring2,lista->Strings[desc]);
AppendStr(sqlstring2," = '");
AppendStr(sqlstring2,valores->Strings[d]);
AppendStr(sqlstring2,"' AND ");
AppendStr(sqlstring2, Params->ColClasificador);
AppendStr(sqlstring2, " = '");
AppendStr(sqlstring2,Trim(Params->LClasificadores->Strings[c]));
// AppendStr(sqlstring2,"Yes");
AppendStr(sqlstring2,"'");
QGral->SQL->Clear();
QGral->SQL->Add(sqlstring2);
QGral->Open();
sqlstring2.~AnsiString();
Tabla[c][d]=QGral->FieldByName("Cant")->AsInteger;
FVitacora<<Tabla[c][d];
FVitacora<<" ";
QGral->Close();
d++;
}//fin while (d<=maxd)
c++;
}//fin while (c<=cant_clas)
}
catch(...)
{
ShowMessage("No se pudo abrir el Query para obtener la cantidad de registros con
un valor determinado de descriptor");
delete lista;
sqlstring2.~AnsiString();
return(1);
}//fin del catch
FVitacora<<Tabla[cant_clas][d];
FVitacora<<" ";
}
Tabla[cant_clas][maxd+1]=CantTotal;
FVitacora<<Tabla[cant_clas][maxd+1];
FVitacora<<endl;
delete lista;
return(0);
}
//---------------------------------------------------------------------------
/******************************************************************************
Particiona el conjunto de datos según el descriptor indicado, en tantos conjuntos
como valores pueda tomar el descriptor.
desc: es el nro de descriptor con el que trabajamos
pNode: es el nodo del árbol desde el cual se está haciendo la partición
valores: es la lista de valores posibles que puede tomar el descriptor
******************************************************************************/
int TDMID3::Particionar(int desc, int nivel, tree_ptr pNode, TStringList *valores)
{
AnsiString sqlstring;
AnsiString particion;
TStringList *lista = new TStringList();
int i, maxValores;
int nuevoNivel;
short int *pDesc=pNode->D;
VecValores[nuevoNivel]=valores->Strings[i];
TTennis->Close();
TTennis->Open();
pNode->pHijos[i]=BuildTree(nuevoNivel, pDesc);
}
sqlstring.~AnsiString();
particion.~AnsiString();
delete lista;
return(0);
}
catch(...)
{
ShowMessage("No se pudo realizar la actualización");
sqlstring.~AnsiString();
particion.~AnsiString();
delete lista;
return(1);
}
}
//---------------------------------------------------------------------------
/******************************************************************************
/*realiza el "pruning" del árbol obtenido
Devuelve 0 si todos los valores son "Yes"
1 si todos los valores son "No"
2 si los valores son mixtos
-1 si los valores son distintos
******************************************************************************/
short int TDMID3::PruneTree(tree_ptr Tree)
{
int i;
short int hijo;
short int iguales; /* 0 si todos los valores son "Yes"
1 si todos los valores son "No"
2 si todos los valores son mixtos
-1 si no son iguales*/
//---------------------------------------------------------------------------
/******************************************************************************
**Arma las reglas de decisión generadas a partir del árbol de decisión "Tree"***
******************************************************************************/
void TDMID3::ArmarReglas(tree_ptr Tree)
{
TVecInt VecReglas;
char outFile[50]="Reglas\\";
int nroRegla;
int i;
nroRegla=0;
char* tabla="";
char* delimitador=".";
AnsiString sqlstring;
int cod;
FReglas.open(outFile, ios::out);
//Cerramos el archivo
FReglas.close();
sqlstring.~AnsiString();
}
TRule rule="";
if (nroRegla!=0)
{
/*Agregamos el el vector de reglas el subindice del par
"descriptor - valor" utilizado para armar a regla, es decir,
utilizado para recorrer la rama del arbol */
j=0;
while (VecReglas[j]!=-1)
{j++;}
VecReglas[j]=ObtenerSubReglas();
}
if (Tree->flag_hoja!=3) //es hoja
{
//Agregamos el nro de regla "RULE N°"
FReglas << endl;
FReglas << endl;
FReglas << header;
FReglas << ObtenerNroRegla() << endl;
//Armamos la regla
FReglas << si;
//descriptor
j=0;
strcat(rule, VecDescriptores[VecReglas[j]].c_str());
sqlstring=VecDescriptores[VecReglas[j]];
AppendStr(sqlstring,"=");
strcat(rule, es);
//valor
strcat(rule, VecValores[VecReglas[j]].c_str());
AppendStr(sqlstring,"'");
AppendStr(sqlstring,VecValores[VecReglas[j]]);
AppendStr(sqlstring,"'");
FReglas << rule << endl;
j++;
while (VecReglas[j]!=-1)
{
strcpy(rule, and);
AppendStr(sqlstring," and ");
//descriptor
strcat(rule, VecDescriptores[VecReglas[j]].c_str());
strcat(rule, es);
AppendStr(sqlstring,VecDescriptores[VecReglas[j]]);
AppendStr(sqlstring," = ");
//valor
strcat(rule, VecValores[VecReglas[j]].c_str());
AppendStr(sqlstring,"'");
AppendStr(sqlstring,VecValores[VecReglas[j]]);
AppendStr(sqlstring,"'");
j++;
FReglas << rule << endl;
}
QUpdates->SQL->Clear();
QUpdates->SQL->Add(sqlstring2);
QUpdates->ExecSQL();
}
else //no es hoja
{
i=0;
nroRegla++;
while ((Tree->pHijos[i]!=NULL)&&(i<MAX_HIJOS))
{
ObtenerReglas(Tree->pHijos[i], VecReglas, nroRegla,codigo);
/*Eliminamos el valor de la ultima rama recorrida porque
estamos cambiando de rama*/
j=0;
while (VecReglas[j]!=-1)
{j++;}
VecReglas[j-1]=-1;
i++;
}
}//end else
tabla.~AnsiString();
sqlstring.~AnsiString();
sqlstring2.~AnsiString();
return;
}
/******************************************************************************
**Numeración única para las reglas de decisión generadas**********************
******************************************************************************/
short int TDMID3::ObtenerNroRegla()
{
static short int nroRegla=-1;
nroRegla++;
return(nroRegla);
}
short int TDMID3::ObtenerSubReglas()
{
static short int subRegla=0;
subRegla++;
return(subRegla);
}
short int TDMID3::ObtenerNroUpdate()
{
static short int nroRegla=-1;
nroRegla++;
return(nroRegla);
}
//---------------------------------------------------------------------------
/******************************************************************************
**Inicializa el árbol, creando un ptr a la raíz del mismo**********************
******************************************************************************/
tree_ptr TDMID3::InicializarTree()
{
tree_ptr pNewNode;
pNewNode=(DECISION_TREE)malloc(sizeof(struct tree_node));
if (pNewNode==0)
{
ShowMessage("No hay suficiente espacio en memoria para la operación");
return(NULL);
}
else
{
pNewNode->pHijos[0]=0;
return(pNewNode);
}
}
/******************************************************************************
i=0;
while ((Tree->pHijos[i]!=NULL)&&(i<5))
{
EliminarTree(Tree->pHijos[i]);
free(Tree->pHijos[i]);
i++;
}
return;
}
/******************************************************************************
****************Evaluamos los resultados obtenidos*****************************
*******************************************************************************/
void TDMID3::AplicarReglas(AnsiString Tabla, int codigoReglas)
{AnsiString sqlstring;
TQuery *QReglas=new TQuery(DMID3);
TQuery *QAplicar=new TQuery(DMID3);
QReglas->DatabaseName=Params->NombreBD;
QAplicar->DatabaseName=Params->NombreBD;
sqlstring="SELECT * FROM Updates.db where codigo=";
AppendStr(sqlstring, codigoReglas);
QReglas->SQL->Clear();
QReglas->SQL->Add(sqlstring);
QReglas->Open();
QReglas->Last();
QReglas->First();
while (!QReglas->Eof)
{
sqlstring=QReglas->FieldByName("Modificacion")->AsString;
sqlstring.Insert(Tabla,0);
sqlstring.Insert("UPDATE ",0);
QAplicar->SQL->Clear();
QAplicar->SQL->Add(sqlstring);
QAplicar->ExecSQL();
QReglas->Next();
}
delete(QReglas);
delete(QAplicar);
sqlstring.~AnsiString();
}
try
{
pForm=FrmRendimiento;
//Inicializamos los nombres de las columnas
pForm->GridResultados->Cells[0][0]="Clases";
pForm->GridResultados->Cells[1][0]="Correctos";
pForm->GridResultados->Cells[2][0]="Errores";
pForm->GridResultados->Cells[3][0]="Probabilidad de Aciertos";
QClases->DatabaseName=Params->NombreBD;
QRes->DatabaseName=Params->NombreBD;
QClases->First();
i=0;
while(!QClases->Eof)
{
pForm->GridResultados->Cells[0][i+1]=QClases->FieldByName("Clase")-
>AsString;
//Correctos
sqlstring=QClases->FieldByName("Clase")->AsString;
sqlstring.Insert(" WHERE ClaseReal='",0);
sqlstring.Insert(NombreTabla,0);
sqlstring.Insert("SELECT Count(*) as Correctos FROM ",0);
AppendStr(sqlstring,"' AND ClaseReal=");
AppendStr(sqlstring,Params->ColClasificador);
QRes->SQL->Clear();
QRes->SQL->Add(sqlstring);
QRes->Open();
QRes->First();
TablaRes[i][0]=(double)QRes->FieldByName("Correctos")->AsInteger;
pForm->GridResultados->Cells[1][i+1]=TablaRes[i][0];
//Errores
sqlstring=QClases->FieldByName("Clase")->AsString;
sqlstring.Insert(" WHERE ClaseReal='",0);
sqlstring.Insert(NombreTabla,0);
sqlstring.Insert("SELECT Count(*) as Erroneos FROM ",0);
AppendStr(sqlstring,"' AND ClaseReal<>");
AppendStr(sqlstring,Params->ColClasificador);
QRes->SQL->Clear();
QRes->SQL->Add(sqlstring);
QRes->Open();
QRes->First();
TablaRes[i][1]=QRes->FieldByName("Erroneos")->AsInteger;
pForm->GridResultados->Cells[2][i+1]=TablaRes[i][1];
QClases->Next();
pForm->GridResultados->RowCount++;
i++;
}
QClases->First();
i=0;
correctos=0;
erroneos=0;
while(!QClases->Eof)
{
/*Calculamos la probabilidad con que un ejemplo clasificado
como de clase i pertenece a dicha clase*/
TablaRes[i][2]=TablaRes[i][0]/(TablaRes[i][0]+TablaRes[i][1]);
pForm->GridResultados->Cells[3][i+1]=TablaRes[i][2];
correctos+=TablaRes[i][0];
erroneos+=TablaRes[i][1];
QClases->Next();
i++;
}
//Resultados generales
TablaRes[i][0]=correctos;
TablaRes[i][1]=erroneos;
TablaRes[i][2]=TablaRes[i][0]/(TablaRes[i][0]+TablaRes[i][1]);
pForm->GridResultados->Cells[0][i+1]="Totales";
pForm->GridResultados->Cells[1][i+1]=TablaRes[i][0];
pForm->GridResultados->Cells[2][i+1]=TablaRes[i][1];
pForm->GridResultados->Cells[3][i+1]=TablaRes[i][2];
QClases->Close();
QRes->Close();
delete(QClases);
delete(QRes);
sqlstring.~AnsiString();
return(0);
}
catch(...)
{
delete(QClases);
delete(QRes);
sqlstring.~AnsiString();
return(-1);
}
}
{boolean CrearTabla;
AnsiString consulta;
TStringList *listaCampos = new TStringList();
TQuery *QIni=new TQuery(DMID3);
TTable *TEval=new TTable(DMID3);
int i;
short int errorNo;
try
{
TEval->Active=false;
TEval->DatabaseName=Params->NombreBD;
TEval->TableName="Eval.db";
TEval->TableType=ttDefault;
TEval->FieldDefs->Clear();
TFieldDef *NewField=TEval->FieldDefs->AddFieldDef();
NewField->DataType=ftString;
NewField->Name="ClaseReal";
i=0;
while ((TTennis->FieldDefs->Items[i]->Name!=Params->ColClasificador) &&
(i<TTennis->FieldDefs->Count-1))
i++;
if (i<TTennis->FieldDefs->Count-1)
NewField->Size=TTennis->FieldDefs->Items[i]->Size;
else
NewField->Size=255;
CrearTabla=false;
if(TEval->Exists)
{if (Application->MessageBox((AnsiString("Sobreescribimos la tabla") +
TEval->TableName + AnsiString("?")).c_str(),
"Tabla Existente", MB_YESNO) == IDYES)
CrearTabla=true;}
else
CrearTabla=true;
if (CrearTabla)
TEval->CreateTable();
TEval->Active=true;
errorNo=0;
}
catch(...)
{errorNo=-2;}
errorNo=0;
}
catch(...)
{errorNo=-3;}
}
TEval->Close();
TEval->DeleteTable();
delete(listaCampos);
delete(TEval);
delete(QIni);
consulta.~AnsiString();
return(errorNo);
}
}
//---------------------------------------------------------------------------
D.4.2. UDMC45
D.4.2.1. UDMC45.h
//---------------------------------------------------------------------------
#ifndef UDMC45H
#define UDMC45H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Db.hpp>
#include <DBTables.hpp>
#include <fstream.h>
#include <stdio.h>
#include <string.h>
#include "UTipos.h"
#include "defns.h"
#include "types.h"
#include "Rulex.h"
//---------------------------------------------------------------------------
class TDMC45 : public TDataModule
{
__published: // IDE-managed Components
TDataSource *DSTTennis;
TTable *TTennis;
void __fastcall DMC45Destroy(TObject *Sender);
private:
TParametros *Params;
/* Datos externos descriptos en extern.i */
short MaxAtt, MaxClass, MaxDiscrVal;
DiscrValue MaxAttVal[MAX_CANT_DESCRIPTORES];
TStringList *ClassName,
*AttName;
TTablaValores AttValName;
TVecValores SpecialStatus;
String FileName;
ItemNo MaxItem;
Description *Item;
short VERBOSITY,
TRIALS;
Boolean GAINRATIO,
SUBSET,
UNSEENS;
ItemNo MINOBJS;
float CF;
Tree *Pruned;
Boolean AllKnown;
ItemCount
*Weight, /* Weight[i] = fracción actual del item i */
**Freq, /* Freq[x][c] = nro de items de clase c con resultado x */
*ValFreq, /* ValFreq[x] = nro.de items con resultado x */
*ClassFreq; /* ClassFreq[c] = nro.de items de clase c */
float
*Gain, /* Gain[a] = info gain al dividir según el atrib a */
*Info, /* Info[a] = info potencial de div sobre el atrib a */
*Bar, /* Bar[a] = mejor valor límite para el atrib a */
*UnknownRate; /* UnknownRate[a] = proporción desconocida actual
para el atrib a */
float
*SplitGain, /* SplitGain[i] = gain con el valor del atrib del item i
como límite */
ItemCount
*Slice1, /* Slice1[c] = valores de Freq[x][c] en subconj.c */
*Slice2; /* Slice2[c] = valores de Freq[y][c] */
//----------------------------------------------------------------------
void GetNames();
/* Lee los nombres de las clases, atributos y valores legales de */
/* atributos. */
void GetData(); //Lee los datos de la tabla
Description GetDescription();
int VerificarPK(AnsiString Atrib);
/* Verifica si el atributo Atrib es clave primaria de la tabla
Devuelve:
0 si es PK
1 en caso contrario
-1 si hubo error*/
int PosicionValor(AnsiString Valor,int nro_atrib, int pri, int ult);
/* Busca la posición del Valor para el atributo especificado por el
nro_atrib en el Tabla de Valores de atributos y la devuelve */
int PosicionClase(AnsiString Valor);
/* Busca la posición del Valor en el vector de la clase */
void OneTree();
/* Construye y poda un árbol a partir de todos los datos */
void Evaluate(bool CMInfo,short Saved);
/* Imprime los errores para cada una de las pruebas */
void InitialiseTreeData();
/* Reserva espacio para las tablas de construcción */
void InitialiseWeights();
/* Inicializa el peso de cada item */
Tree FormTree(ItemNo Fp, ItemNo Lp);
/* Construye un árbol de decisión para los casos Fp hasta Lp */
ItemNo Group(DiscrValue V, ItemNo Fp, ItemNo Lp,Tree TestNode);
/* Agrupa los ítems correspondiente a la rama V de una prueba y
y devuelve el índice del último ítem */
void Intercambio(ItemNo a, ItemNo b);
/* Intercambia los items en a y b */
ItemCount CountItems(ItemNo Fp, ItemNo Lp);
/* Devuelve el peso total de los items desde Fp hasta Lp */
/*************************************************************************/
/* */
/* Calculo de la información, ganancia de información e */
/* impresión de distribuciones */
/* */
/*************************************************************************/
float Worth(float ThisInfo, float ThisGain, float MinGain);
/* Determina el valor de una división particular según el criterio elegido */
void ResetFreq(DiscrValue MaxVal);
/* Resetea las tablas de frecuencia Freq[][] y ValFreq[] */
float ComputeGain(float BaseInfo,float UnknFrac, DiscrValue MaxVal,ItemCount
TotalItems);
/* Dadas las tablas Freq[][] y ValFreq[], computa la ganancia de información */
float TotalInfo(ItemCount V[], DiscrValue MinVal, DiscrValue MaxVal);
/* Computa la información total en V[ MinVal..MaxVal ] */
void PrintDistribution(Attribute Att,DiscrValue MaxVal,bool ShowNames);
/* Imprime la tabla de distribución para un atributo determinado */
/*************************************************************************/
/* */
/* Rutinas de manejo de los árboles de decisión */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* Evaluación del armado de subconjuntos sobre los atrib discretos */
/* */
/*************************************************************************/
void EvalSubset(Attribute Att, ItemNo Fp, ItemNo Lp,ItemCount Items);
/* Evaluación del armado de subconjuntos sobre un atrib discreto. */
void Combine(DiscrValue x,DiscrValue y,DiscrValue Last);
/* Combina los valores de la distribución de los atributos discretos x e y */
void Uncombine(DiscrValue x,DiscrValue y);
/* Devuelve a x e y sus valores originales */
void PrintSubset(Attribute Att,Conjunto Ss);
/* Imprime los valores del atributo Att que estan en el subconjunto Ss */
void SubsetTest(Tree Node,Attribute Att);
/* Construye una prueba de nodo sobre un subconjunto de valores */
void EvalDiscreteAtt(Attribute Att,ItemNo Fp,ItemNo Lp,ItemCount Items);
/* Setea Info[] and Gain[] para la partición discreta de items Fp hasta Lp */
void ComputeFrequencies(Attribute Att,ItemNo Fp,ItemNo Lp);
/* Computa las tablas de frecuencia Freq[][] y ValFreq[] para Att */
float DiscrKnownBaseInfo(DiscrValue KnownItems,ItemCount MaxVal);
/* Devuelve la información base de los items con valores conocidos
de un atributo discreto, utilizando la tabla de frecuencias Freq[][] */
void DiscreteTest(Tree Node,Attribute Att);
/* Construye y devuelve un nodo para una prueba sobre un atributo discreto */
/*************************************************************************/
/* */
/* Evaluación de una prueba sobre un atributo continuo */
/* */
/*************************************************************************/
void EvalContinuousAtt(Attribute Att,ItemNo Fp,ItemNo Lp);
void ContinTest(Tree Node,Attribute Att);
/* Transforma una hoja en una prueba sobre un atributo continuo */
float GreatestValueBelow(Attribute Att,float t);
/* Devuelve el mayor valor del atributo Att por debajo del límite t */
/*************************************************************************/
/* */
/* Poda un árbol de decisión y predice su proporción de error */
/* */
/*************************************************************************/
Conjunto *PossibleValues;//=Nil;
bool Changed;
bool Prune(Tree T);
/*Poda el árbol T y devuelve true si el árbol fue modificado */
float EstimateErrors(Tree T, ItemNo Fp,ItemNo Lp,short Sh,bool UpdateTree);
/*Estima los errores en un determinado subárbol */
void CheckPossibleValues(Tree T);
/* Elimina pruebas de subconjuntos innecesarias sobre valores faltantes */
/*************************************************************************/
/* */
/* Determina la clase de un caso a partir del árbol de decisión */
/* */
/*************************************************************************/
float *ClassSum; /* ClassSum[c]= peso total de la clase c */
ClassNo Category(Description CaseDesc,Tree DecisionTree);
/* Categoriza la descripción de un caso utilizando el árbol de decisión */
void Classify(Description CaseDesc,Tree T,float Weight);
/* Clasifica un caso utilizando el subárbol dado, ajustando el valor
de ClassSum para cada clase */
Tree *Raw;
/*************************************************************************/
/* */
/* REGLAS DE DECISIÓN */
/* */
/*************************************************************************/
/* Variables usadas en la generación de reglas */
bool SIGTEST, /* uso de la prueba de significado en
la poda de reglas */
SIMANNEAL; /* uso de simulated annealing */
float SIGTHRESH,
REDUNDANCY; /* factor que estima la cantidad de
de redundancia e irrelevancia
en los atributos */
PR *Rule; /* reglas actuales */
RuleNo NRules, /* nro de reglas actuales */
*RuleIndex; /* índice de reglas */
short RuleSpace; /* espacio reservado para las reglas */
ClassNo DefaultClass; /* clase por defecto */
RuleSet *PRSet; /* conjuntos de reglas */
float AttTestBits, /* bits para codificar el atrib
testeado */
*BranchBits; /* idem para el valor del atrib */
fstream FReglas; //guarda los datos generados durante la generación de las
reglas con el C4.5
float *LogItemNo;
double *LogFact;
ItemNo *TargetClassFreq, /* [Boolean] */
*Errors, /* [Condición] */
*Total; /* [Condición] */
float *Pessimistic, /* [Condición] */
*Actual, /* [Condición] */
*CondSigLevel; /* [Condición] */
bool **CondSatisfiedBy, /* [Condición][NroItem] */
*Deleted; /* [Condición] */
DiscrValue *SingleValue; /* [Atributo] */
Condition *Stack;
short MaxDisjuncts,
MaxDepth;
ItemNo *Covered, /* Covered[i] = nro.de reglas que cubren el item i */
*FalsePos, /* FalsePos[c] = nro.de falsos positivos de las
reglas seleccionadas para la calse c */
ClassNo FocusClass;
Test *TestVec;
short NTests;
float Confidence; /* factor de certeza de la regla aplicada
(determinado por BestRuleIndex) */
public:
__fastcall TDMC45(TComponent* Owner);
void RecibirParametros(TParametros *Parametros);
void C45(); //dispara el proceso del C4.5 con los parámetros recibidos
void GenerarReglasC45(); //dispara el proceso de generación de las reglas de
decisión
};
//---------------------------------------------------------------------------
extern PACKAGE TDMC45 *DMC45;
//---------------------------------------------------------------------------
#endif
D.4.2.2. UDMC45.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "UDMC45.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TDMC45 *DMC45;
fstream FVitacora; //guarda los datos generados durante la ejecución del C4.5
//---------------------------------------------------------------------------
__fastcall TDMC45::TDMC45(TComponent* Owner)
: TDataModule(Owner)
{
MaxDiscrVal = 2;
FileName = "DF";
VERBOSITY = 0;
TRIALS = 10;
GAINRATIO = true;
SUBSET = false;
UNSEENS = false;
MINOBJS = 2;
AllKnown = true;
Val[0]=0;
Val[1]=0.001;
Val[2]=0.005;
Val[3]=0.01;
Val[4]=0.05;
Val[5]=0.10;
Val[6]=0.20;
Val[7]=0.40;
Val[8]=1.00;
//Val[] = { 0, 0.001, 0.005, 0.01, 0.05, 0.10, 0.20, 0.40, 1.00};
Dev[0]=4.0;
Dev[1]=3.09;
Dev[2]=2.58;
Dev[3]=2.33;
Dev[4]=1.65;
Dev[5]=1.28;
Dev[6]=0.84;
Dev[7]=0.25;
Dev[8]=0.00;
//Dev[] = {4.0, 3.09, 2.58, 2.33, 1.65, 1.28, 0.84, 0.25, 0.00};
//*ClassSum=0;
}
//------------------------------------------------------------------------------
void TDMC45::RecibirParametros(TParametros *Parametros)
{
AnsiString sqlstring;
TQuery *QGral=new TQuery(DMC45);
Params=Parametros;
TTennis->DatabaseName=Params->NombreBD;
TTennis->TableName=Params->TablaBD;
TTennis->Active=true;
sqlstring.~AnsiString();
delete(QGral);
}
//---------------------------------------------------------------------------
void __fastcall TDMC45::DMC45Destroy(TObject *Sender)
{int i,j;
delete ClassName;
delete AttName;
for (i=0;i<MAX_NIVELES;i++)
{
for (j=0;j<MAX_NIVELES;j++)
{AttValName[i][j].~AnsiString();}
SpecialStatus[i].~AnsiString();
}
}
/*************************************************************************/
/* */
/* Principal c4.5 */
/* ------------------ */
/* */
/* Basado en el código propuesto por Quinlan en: */
/* Quinlan, "C4.5: Programs for Machine Learning" (1993) */
/* Morgan Kaufman Publishers, San Mateo, California */
/*************************************************************************/
void TDMC45::C45()
{
extern int optind;
short Best;
char outFile[50]="Log\\C45";
DateSeparator='-';
TimeSeparator='_';
strcat(outFile, DateTimeToStr(Now()).c_str());
strcat(outFile, Params->TablaBD.c_str());
strcat(outFile, ".log");
FVitacora.open(outFile, ios::out);
FileName = Params->TablaBD;
VERBOSITY = 5; //grado de explicación en el Log
if (Params->iCriterio==0)
GAINRATIO = false;
if (Params->iConjuntos==0)
SUBSET = true;
MINOBJS = Params->iMinObjs;
CF =Params->iCF;
Check(CF, Epsilon, 100);
CF /= 100;
/* Inicialización */
GetNames();
GetData();
FVitacora<<(MaxItem+1);
FVitacora<<" casos (";
FVitacora<<MaxAtt;
FVitacora<<" atributos) leídos de ";
FVitacora<<Params->TablaBD.c_str();
TRIALS = 1;
OneTree();
Best = 0;
if ( TRIALS == 1 )
{
FVitacora<<endl;
FVitacora<<"El árbol ha sido guardado con éxito.";
FVitacora<<endl;
}
SaveTree(Pruned[Best], ".tree");
if ( UNSEENS )
{
GetData();
FVitacora<<endl;
FVitacora<<"Evaluación sobre los datos de prueba (";
FVitacora<<(MaxItem+1);
FVitacora<<" ítems):";
FVitacora<<endl;
Evaluate(true, Best);
}
exit(0);
FVitacora.close();
}
/*************************************************************************/
/* */
/* Lee los nombres de las clases, atributos y valores legales de */
/* atributos. */
/* Cuando termina, los nombres se almacenan en: */
/* ClassName - nombres de las clases */
/* AttName - nombres de los atributos */
/* AttValName - nombres de los valores de los atributos */
/* con: */
/* MaxAttVal - cantidad de valores para cada atributo */
/* */
/* Las otras variables globales que se setean son: */
/* MaxAtt - número máximo de atributos */
/* MaxClass - número máximo de clases */
/* MaxDiscrVal - número máximo de valores discretos para */
/* cualquier atributo */
/* */
/*************************************************************************/
void TDMC45::GetNames()
{int i,j;
TQuery *QGral=new TQuery(DMC45);
AnsiString sqlstring;
try
{
/* Obtenemos los nombres de las clases */
ClassName = new TStringList();
ClassName->AddStrings(Params->LClasificadores);
MaxClass = ClassName->Count;
QGral->DatabaseName=Params->NombreBD;
sqlstring=") as Campo from ";
AppendStr(sqlstring, Params->TablaBD);
MaxDiscrVal=-1;
for (i=0;i<MaxAtt;i++)
{
if (TTennis->Fields->Fields[i]->FieldName!=Params->ColClasificador)
{
if (VerificarPK(TTennis->Fields->Fields[i]->FieldName)==0 ||
TTennis->Fields->Fields[i]->FieldName=="Nivel" )
{SpecialStatus[i]="IGNORAR";}
else
{
AttName->Add(TTennis->Fields->Fields[i]->FieldName);
}
}
}
}
}
catch(...)
{
Application->MessageBox("Error en la carga de los datos del
archivo","Error",MB_OK|MB_ICONWARNING);
}
delete(QGral);
sqlstring.~AnsiString();
}
//---------------------------------------------------------------------------
/*************************************************************************/
/* */
/* Lee los casos de la tabla. */
/* */
/* Al terminar,los casos se almacenan en el vector Item con la forma */
/* de Descriptions (i.e. vectores de valores de atributos), */
/* y se le asigna a MaxItem el número de items de datos. */
/* */
/*************************************************************************/
void TDMC45::GetData()
{
ItemNo i=-1, ItemSpace;
Description GetDescription();
TQuery *QData=new TQuery(DMC45);
int Dv;
float Cv;
Variant Atributo;
Description Dvec;
Attribute Att;
AnsiString AtDiscreto;
try{
ItemSpace=0;
QData->DatabaseName=Params->NombreBD;
QData->SQL->Clear();
QData->SQL->Add("SELECT * FROM " + Params->TablaBD);
QData->Open();
QData->First();
do
{
++i;
MaxItem = i;
if ( i >= ItemSpace )
{
if ( ItemSpace )
{
ItemSpace += Inc;
Item = (Description *)
realloc(Item, ItemSpace*sizeof(Description));
}
else
{
Item = (Description *)
malloc((ItemSpace=Inc)*sizeof(Description));
}
}
for(Att=0;Att<MaxAtt;Att++)
{
Atributo=QData->FieldValues[TTennis->Fields->Fields[Att]->FieldName];
if ( SpecialStatus[Att] == "IGNORAR" )
{
/* Salteamos este valor */
DVal(Dvec, Att) = 0;
}
else
if ( MaxAttVal[Att] || SpecialStatus[Att] == "DISCRETE" )
{
/* Valor discreto */
AtDiscreto=AnsiString(Atributo);
if ( AtDiscreto.AnsiCompareIC("?")==0)
{
Dv = 0;
}
else
{
Dv = PosicionValor(AtDiscreto,Att, 0, MaxAttVal[Att]);
}
DVal(Dvec, Att) = Dv;
}
else
{
/* Valor Continuo */
if ( Atributo.VType==varEmpty || Atributo.VType==varNull)
{
Cv = Unknown;
}
else
{
Cv = double(Atributo);
}
CVal(Dvec, Att) = Cv;
}
}
Class(Dvec) = PosicionClase(QData->FieldByName(Params->ColClasificador)-
>AsString);
Item[i] = Dvec;
QData->Next();
}
catch(...)
{
Application->MessageBox("Error en la carga de los datos del
archivo","Error",MB_OK|MB_ICONWARNING);
}
delete (QData);
AtDiscreto.~AnsiString();
}
//-----------------------------------------------------------------------------
int TDMC45::PosicionValor(AnsiString Valor,int nro_atrib, int pri, int ult)
/* Busca la posición del Valor para el atributo especificado por el
nro_atrib en el Tabla de Valores de atributos y la devuelve */
{int i;
i=pri;
while (AttValName[nro_atrib][i].AnsiCompareIC(Valor)!=0 && i<=ult)
i++;
return(i<=ult ? i : pri-1);
}
int TDMC45::PosicionClase(AnsiString Valor)
/* Busca la posición del Valor en el vector de la clase */
{int i;
i=0;
while (ClassName->Strings[i].Trim()!=Valor.Trim() && i<=MaxClass)
i++;
return(i<=MaxClass ? i : -1);
}
//-----------------------------------------------------------------------------
int TDMC45::VerificarPK(AnsiString Atrib)
/* Verifica si el atributo Atrib es clave primaria de la tabla
Devuelve:
o si es PK
1 en caso contrario
-1 si hubo error */
{
AnsiString sqlstring;
int cantReg,res;
TQuery *QGral=new TQuery(DMC45);
cantReg=TTennis->RecordCount;
try
{
sqlstring="SELECT COUNT(DISTINCT ";
AppendStr(sqlstring,Atrib);
AppendStr(sqlstring," ) as Cant FROM ");
AppendStr(sqlstring, Params->TablaBD);
QGral->DatabaseName=Params->NombreBD;
QGral->SQL->Clear();
QGral->SQL->Add(sqlstring);
QGral->Open();
QGral->First();
if (QGral->FieldByName("Cant")->AsInteger==cantReg)
{res=0;} //es PK
else
{res=1;}
}
catch(...)
{
res=-1;
}
delete (QGral);
sqlstring.~AnsiString();
return(res);
}
/*************************************************************************/
/* Construye y poda un árbol a partir de todos los datos */
/* */
/*************************************************************************/
void TDMC45::OneTree()
{
ItemNo *TargetClassFreq;
Tree *Raw;
Tree *Pruned;
InitialiseTreeData();
InitialiseWeights();
AllKnown = true;
Raw[0] = FormTree(0, MaxItem);
FVitacora<<endl;
PrintTree(Raw[0]);
SaveTree(Raw[0], ".unpruned");
Pruned[0] = CopyTree(Raw[0]);
if ( Prune(Pruned[0]) )
{
FVitacora<<endl;
FVitacora<<"Simplificamos el ";
PrintTree(Pruned[0]);
}
}
/*************************************************************************/
/* */
/* Imprime los errores para cada una de las pruebas */
/* */
/*************************************************************************/
void TDMC45::Evaluate(bool CMInfo,short Saved)
{
char outFile[50]="Eval";
ClassNo RealClass, PrunedClass;
short t;
ItemNo *ConfusionMat, i, RawErrors, PrunedErrors;
fstream FEvalRendimiento; //guarda los datos de la evaluación de rendimiento
//para el C4.5
if ( CMInfo )
{
ConfusionMat = (ItemNo *) calloc((MaxClass+1)*(MaxClass+1), sizeof(ItemNo));
}
DateSeparator='-';
TimeSeparator='_';
strcat(outFile, DateTimeToStr(Now()).c_str());
strcat(outFile, Params->TablaBD.c_str());
strcat(outFile, ".log");
FEvalRendimiento.open(outFile, ios::out);
FVitacora<<endl;
if ( TRIALS > 1 )
{
FVitacora<<"Prueba Antes de Podar Después de Podar";
FVitacora<<endl;
FVitacora<<"------ --------------- ------------------";
FVitacora<<endl;
}
else
{
FVitacora<<" Antes de Podar Después de Podar";
FVitacora<<endl;
FVitacora<<" --------------- ------------------";
FVitacora<<endl;
}
FVitacora<<" Tamaño Errores Tamaño Errores Estimación";
FVitacora<<endl;
FVitacora<<endl;
ForEach(t, 0, TRIALS-1)
{
RawErrors = PrunedErrors = 0;
ForEach(i, 0, MaxItem)
{
RealClass = Class(Item[i]);
if ( TRIALS > 1 )
{
FVitacora<<t;
}
FVitacora<<" ";
FVitacora<<TreeSize(Raw[t]);
FVitacora<<" ";
FVitacora<<RawErrors;
FVitacora<<"(";
FVitacora<<(100.0*RawErrors / (MaxItem+1.0));
FVitacora<<") ";
FVitacora<<TreeSize(Pruned[t]);
FVitacora<<" ";
FVitacora<<PrunedErrors;
FVitacora<<"(";
FVitacora<<(100.0*PrunedErrors / (MaxItem+1.0));
FVitacora<<") (";
FVitacora<<(100 * Pruned[t]->Errors / Pruned[t]->Items);
FVitacora<<")";
if (t==Saved)
{FVitacora<<" <<";}
FVitacora<<endl;
}
if ( CMInfo )
{
PrintConfusionMatrix(ConfusionMat);
free(ConfusionMat);
}
FEvalRendimiento.close();
}
/*************************************************************************/
/* */
/* Reserva espacio para las tablas de construcción */
/* */
/*************************************************************************/
void TDMC45::InitialiseTreeData()
{
DiscrValue v;
Attribute a;
MultiVal = true;
if ( ! SUBSET )
{
for ( a = 0 ; MultiVal && a <= MaxAtt ; a++ )
{
if ( SpecialStatus[a] != "IGNORAR" )
{
MultiVal = MaxAttVal[a] >= 0.3 * (MaxItem + 1);
}
}
}
}
/*************************************************************************/
/* */
/* Inicializa el peso de cada item */
/* */
/*************************************************************************/
void TDMC45::InitialiseWeights()
{
ItemNo i;
ForEach(i, 0, MaxItem)
{
Weight[i] = 1.0;
}
}
/*************************************************************************/
/* */
/* Construye un árbol de decisión para los casos Fp hasta Lp: */
/* */
/* - si todos los casos son de la misma clase, el árbol es una hoja, */
/* y devuelve una hoja con dicha clase */
/* */
/* - para cada atributo, calcular la información potencial provista */
/* por una prueba sobre el atributo (basada en las probabilidades */
/* de que cada caso tenga un valor particular para el atributo), */
/* y la ganancia en información que resultaría de una prueba */
/* sobre el atributo (basada en las probabilidades de cada caso */
/* con un valor particular para el atributo pertenezca a una */
/* determinada clase). */
/* */
/* - sobre esta base, y dependiendo del criterio de selección actual, */
/* encontrar el mejor atributo para crear una rama. */
/* Nota: no se permitirán divisiones sobre un atributo a menos */
/* que dos o más subconjuntos tengan al menos MINOBJS items. */
/* */
/* - intentar crear una rama y analizar si es mejor que crear una hoja */
/* */
/*************************************************************************/
Tree TDMC45::FormTree(ItemNo Fp, ItemNo Lp)
{
ItemNo i, Kp, Ep;
ItemCount Cases, NoBestClass, KnownCases;
float Factor, BestVal, Val, AvGain=0;
Attribute Att, BestAtt, Possible=0;
ClassNo c, BestClass;
Tree Node;
DiscrValue v;
bool PrevAllKnown;
ForEach(c, 0, MaxClass)
{
ClassFreq[c] = 0;
}
BestClass = 0;
ForEach(c, 0, MaxClass)
{
if ( ClassFreq[c] > ClassFreq[BestClass] )
{
BestClass = c;
}
}
NoBestClass = ClassFreq[BestClass];
Verbosity(1)
{
FVitacora<<(Lp-Fp+1);
FVitacora<<" ítems, peso total ";
FVitacora<<Cases;
FVitacora<<endl;
}
ForEach(Att, 0, MaxAtt-1)
{
Gain[Att] = -Epsilon;
if ( MaxAttVal[Att] )
{
/* atributo discreto */
BestVal = -Epsilon;
BestAtt = None;
AvGain = ( Possible ? AvGain / Possible : 1E6 );
Verbosity(2)
{
if ( AvGain < 1E6 )
{
FVitacora<<" ganancia promedio ";
FVitacora<<AvGain;
FVitacora<<endl;
}
}
ForEach(Att, 0, MaxAtt-1)
{
if ( Gain[Att] > -Epsilon )
{
Val = Worth(Info[Att], Gain[Att], AvGain);
if ( Val > BestVal )
{
BestAtt = Att;
BestVal = Val;
}
}
}
if ( BestAtt != None )
{
Verbosity(1)
{
FVitacora<<" mejor atributo ";
FVitacora<<AttName->Strings[BestAtt].c_str();
if ( ! MaxAttVal[BestAtt] )
{
FVitacora<<" corte ";
FVitacora<<Bar[BestAtt];
}
FVitacora<<" inf ";
FVitacora<<Info[BestAtt];
FVitacora<<" ganancia ";
FVitacora<<Gain[BestAtt];
FVitacora<<" val ";
FVitacora<<BestVal;
FVitacora<<endl;
}
if ( MaxAttVal[BestAtt] )
{
/* Atributo discreto */
ContinTest(Node, BestAtt);
}
PrevAllKnown = AllKnown;
Verbosity(1)
{
if ( UnknownRate[BestAtt] > 0 )
{
FVitacora<<" proporciones desconocidas para ";
FVitacora<<AttName->Strings[BestAtt].c_str();
FVitacora<<" = ";
FVitacora<<UnknownRate[BestAtt];
FVitacora<<endl;
}
}
++Tested[BestAtt];
Ep = Kp - 1;
Node->Errors = 0;
ForEach(v, 1, Node->Forks)
{
Ep = Group(v, Kp, Lp, Node);
if ( Kp <= Ep )
{
Factor = CountItems(Kp, Ep) / KnownCases;
--Tested[BestAtt];
AllKnown = PrevAllKnown;
}
}
return Node;
}
/*************************************************************************/
/* */
/* Agrupa los ítems correspondiente a la rama V de una prueba y */
/* y devuelve el índice del último ítem */
/* */
/* Nota: si V está vacía, agrupa valores desconocidos */
/* */
/*************************************************************************/
ItemNo TDMC45::Group(DiscrValue V, ItemNo Fp, ItemNo Lp,Tree TestNode)
{
ItemNo i;
Attribute Att;
float Thresh;
Conjunto SS;
Att = TestNode->Tested;
if ( V )
{
/* Agrupar items según el valor del atributo Att, según el
tipo de rama */
switch ( TestNode->NodeType )
{
case BrDiscr:
for(i=Fp;i<Lp;i++)
{
if ( DVal(Item[i], Att) == V ) Intercambio(Fp++, i);
}
break;
case ThreshContin:
Thresh = TestNode->Cut;
for(i=Fp;i<Lp;i++)
{
if ( (CVal(Item[i], Att) <= Thresh) == (V == 1) ) Intercambio(Fp++,
i);
}
break;
case BrSubset:
SS = TestNode->Subset[V];
for(i=Fp;i<Lp;i++)
{
if ( In(DVal(Item[i], Att), SS) ) Intercambio(Fp++, i);
}
break;
}
}
else
{
/* Agrupar valores desconocidos */
switch ( TestNode->NodeType )
{
case BrDiscr:
case BrSubset:
for(i=Fp;i<Lp;i++)
{
if ( ! DVal(Item[i], Att) ) Intercambio(Fp++, i);
}
break;
case ThreshContin:
for(i=Fp;i<Lp;i++)
{
return Fp - 1;
}
/*************************************************************************/
/* */
/* Intercambia los items en a y b */
/* */
/*************************************************************************/
void TDMC45::Intercambio(ItemNo a, ItemNo b)
{
register Description Hold;
register ItemCount HoldW;
Hold = Item[a];
Item[a] = Item[b];
Item[b] = Hold;
HoldW = Weight[a];
Weight[a] = Weight[b];
Weight[b] = HoldW;
}
/*************************************************************************/
/* */
/* Devuelve el peso total de los items desde Fp hasta Lp */
/* */
/*************************************************************************/
if ( AllKnown ) return Lp - Fp + 1;
return Sum;
}
//------------------------------------------------------------------------------
/*************************************************************************/
/* */
/* Determina el valor de una división particular según el criterio */
/* elegido */
/* */
/* Parámetros: */
/* SplitInfo: información potencial de la división */
/* SplitGain: ganancia en información de la división */
/* MinGain: ganancia a partir de la cual el */
/* Gain Ratio puede usarse */
/* */
/* Si se está utilizando el criterio de Gain,se devuelve la ganancia de */
/* información de la división, pero si se está utilizando el criterio */
/* de GainRatio, se devuelve la proporción de la ganancia de */
/* información con respecto a su información potencial. */
/* */
/*************************************************************************/
float TDMC45::Worth(float ThisInfo, float ThisGain, float MinGain)
{
if ( GAINRATIO )
{
if ( ThisGain >= MinGain - Epsilon && ThisInfo > Epsilon )
{
return ThisGain / ThisInfo;
}
else
{
return -Epsilon;
}
}
else
{
return ( ThisInfo > 0 && ThisGain > -Epsilon ? ThisGain : -Epsilon );
}
}
/*************************************************************************/
/* */
/* Resetea las tablas de frecuencia Freq[][] y ValFreq[] */
/* */
/*************************************************************************/
void TDMC45::ResetFreq(DiscrValue MaxVal)
{
DiscrValue v;
ClassNo c;
for (v=0;v<MaxVal;v++)
{
for (c=0;c<MaxClass;c++)
{
Freq[v][c] = 0;
}
ValFreq[v] = 0;
}
}
/*************************************************************************/
/* */
/* Dadas las tablas Freq[][] y ValFreq[], computa la ganancia */
/* de información. */
/* */
/* Parámetros: */
/* BaseInfo: información promedio para todos los */
/* items con valores conocidos del */
/* atributo que se está testeando */
/* UnknownRate: fracción de los items con valor del */
/* atributo desconocido */
/* MaxVal: cantidad de divisiones */
/* TotalItems: nro de items con valores conocidos para */
/* el atributo que se está testeando */
/* */
/* donde Freq[x][y] contiene el nro de casos con valor x para un */
/* atributo particular que pertenecen a la clase y, */
/* y ValFreq[x] contiene el nro de casos con valor x para un */
/* atributo particular */
/* */
/*************************************************************************/
float TDMC45::ComputeGain(float BaseInfo,float UnknFrac, DiscrValue MaxVal,ItemCount
TotalItems)
{
DiscrValue v;
float ThisInfo=0.0, ThisGain;
short ReasonableSubsets=0;
Verbosity(5)
{
FVitacora<<"CalculoEstaGanancia: items ";
FVitacora<<(TotalItems + ValFreq[0]);
FVitacora<<" info ";
FVitacora<<ThisInfo;
FVitacora<<" desc ";
FVitacora<<UnknFrac;
FVitacora<<" resultado ";
FVitacora<<ThisGain<<endl;
}
return ThisGain;
}
/*************************************************************************/
/* */
/* Computa la información total en V[ MinVal..MaxVal ] */
/* */
/*************************************************************************/
float TDMC45::TotalInfo(ItemCount V[], DiscrValue MinVal, DiscrValue MaxVal)
{
DiscrValue v;
float Sum=0.0;
ItemCount N, TotalItems=0;
for(v=MinVal;v<MaxVal;v++)
{
N = V[v];
Sum += N * Log(N);
TotalItems += N;
}
/*************************************************************************/
/* */
/* Imprime la tabla de distribución para un atributo determinado */
/* */
/*************************************************************************/
void TDMC45::PrintDistribution(Attribute Att,DiscrValue MaxVal,bool ShowNames)
{
DiscrValue v;
ClassNo c;
String Val;
FVitacora<<endl;
FVitacora<<" ";
for(c=0;c<MaxClass;c++)
{
FVitacora<<ClassName->Strings[c].c_str();
}
FVitacora<<endl;
for(v=0;v<MaxVal;v++)
{
if ( ShowNames )
{
if ( !v)
{
Val="desconocidos";
}
else
{
if (MaxAttVal[Att])
{
Val=AttValName[Att][v];
}
else
{
if(v == 1)
{
Val="menores";
}
else
{
Val="mayores";
}
}
}
FVitacora<<Val.c_str();
}
else
{
FVitacora<<v;
}
for(c=0;c<MaxClass;c++)
{
FVitacora<<Freq[v][c];
}
FVitacora<<endl;
}
}
/*************************************************************************/
/* */
/* Construye una hoja en un determinado nodo */
/* */
/*************************************************************************/
Tree TDMC45::Leaf(ItemCount *ClassFreq, ClassNo NodeClass,ItemCount Cases,ItemCount
Errors)
{
Tree Node;
Node->NodeType = 0;
Node->Leaf = NodeClass;
Node->Items = Cases;
Node->Errors = Errors;
return Node;
}
/*************************************************************************/
/* */
/* Inserta ramas en un nodo */
/* */
/*************************************************************************/
void TDMC45::Sprout(Tree Node, DiscrValue Branches)
{
Node->Forks = Branches;
/*************************************************************************/
/* */
/* Muestra todo el árbol de decisión T */
/* */
/*************************************************************************/
Tree TDMC45::PrintTree(Tree T)
{
short s;
Subtree=0;
FVitacora<<"Árbol de decisión";
FVitacora<<endl;
Show(T, 0);
FVitacora<<endl;
ForEach(s, 1, Subtree)
{
FVitacora<<endl;
FVitacora<<endl;
FVitacora<<"Subárbol ";
FVitacora<<s;
Show(Subdef[s], 0);
FVitacora<<endl;
}
FVitacora<<endl;
}
/*************************************************************************/
/* */
/* Muestra el árbol T con offset Sh */
/* */
/*************************************************************************/
void TDMC45::Show(Tree T,short Sh)
{
DiscrValue v, MaxV;
if ( T->NodeType )
{
/* Verificamos si se necesita un nuevo subárbol */
if ( (T->Items!=0) && (Sh) && (Sh * TabSize + MaxLine(T) > Width) )
{
if ( Subtree < 99 )
{
Subdef[++Subtree] = T;
FVitacora<<Subtree;
}
else
{
FVitacora<<"[S??]";
}
}
else
{
MaxV = T->Forks;
ForEach(v, 1, MaxV)
{
if ( T->Branch[v]->NodeType )
{
ShowBranch(Sh, T, v);
}
}
}
}
else
{
FVitacora<<ClassName->Strings[T->Leaf].c_str();
FVitacora<<"(";
FVitacora<<T->Items;
if ( T->Errors > 0 ) FVitacora<<T->Errors;
FVitacora>>")";
}
}
/*************************************************************************/
/* */
/* Imprime un nodo T con offset Sh, valor de rama v */
/* */
/*************************************************************************/
void TDMC45::ShowBranch(short Sh,Tree T,DiscrValue v)
{
DiscrValue Pv, Last;
Attribute Att;
bool FirstValue;
short TextWidth, Skip, Values=0, i;
Att = T->Tested;
switch ( T->NodeType )
{
case BrDiscr:
Indent(Sh, Tab);
FVitacora<<AttName->Strings[Att].c_str();
FVitacora<<" = ";
FVitacora<<AttValName[Att][v].c_str();
break;
case ThreshContin:
Indent(Sh, Tab);
{
FVitacora<<AttName->Strings[Att].c_str();
FVitacora<<" ";
if (v==1)
{FVitacora<<"<=";}
else
{FVitacora<<">";}
FVitacora<<" ";
FVitacora<<T->Cut;
}
if ( T->Lower != T->Upper )
{
FVitacora<<"[";
FVitacora<<T->Lower;
FVitacora<<",";
FVitacora<<T->Upper;
}
FVitacora<<":";
break;
case BrSubset:
/* Contar los valores de la rama */
ForEach(Pv, 1, MaxAttVal[Att])
{
if ( In(Pv, T->Subset[v]) )
{
Last = Pv;
Values++;
}
}
if ( ! Values ) return;
Indent(Sh, Tab);
if ( Values == 1 )
{
FVitacora<<AttName->Strings[Att].c_str();
FVitacora<<" = ";
FVitacora<<AttValName[Att][Last].c_str();
FVitacora<<":";
break;
}
FVitacora<<AttName->Strings[Att].c_str();
FVitacora<<" en {";
FirstValue = true;
Skip = TextWidth = AttName->Strings[Att].Length() + 5;
ForEach(Pv, 1, MaxAttVal[Att])
{
if ( In(Pv, T->Subset[v]) )
{
if ( ! FirstValue &&
TextWidth + AttValName[Att][Pv].Length() + 11 > Width )
{
Indent(Sh, Tab);
ForEach(i, 1, Skip) putchar(' ');
TextWidth = Skip;
FirstValue = true;
}
FVitacora<<AttValName[Att][Pv].c_str();
if (Pv==Last)
{FVitacora<<"}";}
else
{FVitacora<<",";}
TextWidth += AttValName[Att][Pv].Length() + 1;
FirstValue = false;
}
}
putchar(':');
}
Show(T->Branch[v], Sh+1);
}
/*************************************************************************/
/* */
/* Encuentra el tamaño máx de una línea nodo para el subárbol St. */
/* El formato de la línea es */
/* <atributo> <> X.xx:[ <clase (<Items>)], o */
/* <atributo> = <DVal>:[ <clase> (<Items>)] */
/* */
/*************************************************************************/
short TDMC45::MaxLine(Tree St)
{
Attribute a;
DiscrValue v, MaxV, Next;
short Ll, MaxLl=0;
a = St->Tested;
MaxV = St->Forks;
ForEach(v, 1, MaxV)
{
Ll = ( St->NodeType == 2 ? 4 : AttValName[a][v].Length() ) + 1;
Next = v;
if ( ! St->Branch[Next]->NodeType )
{
Ll += ClassName->Strings[St->Branch[Next]->Leaf].Length() + 6;
}
MaxLl = Max(MaxLl, Ll);
}
/*************************************************************************/
/* */
/* Indenta Sh columnas */
/* */
/*************************************************************************/
void TDMC45::Indent(short Sh,char *Mark)
{
FVitacora<<endl;
while ( Sh-- ) FVitacora<<Mark;
}
/*************************************************************************/
/* */
/* Guardamos el árbol T completo en el archivo */
/* de extensión Extension */
/* */
/*************************************************************************/
void TDMC45::SaveTree(Tree T,AnsiString Extension)
{
static char *LastExt="";
if ( strcmp(LastExt, Extension.c_str()) )
{
LastExt = Extension.c_str();
if ( TRf ) fclose(TRf);
strcpy(Fn, FileName.c_str());
strcat(Fn, Extension.c_str());
if ( ! ( TRf = fopen(Fn, "w") ) )
{
FVitacora<<"Error al abrir el archivo de salida del árbol";
FVitacora<<endl;
}
}
putc('\n', TRf);
OutTree(T);
SaveDiscreteNames();
}
/*************************************************************************/
/* */
/* Guardamos el árbol T como caracteres */
/* */
/*************************************************************************/
void TDMC45::OutTree(Tree T)
{
DiscrValue v;
int Bytes;
if ( T->NodeType )
{
StreamOut((char *) &T->Tested, sizeof(Attribute));
StreamOut((char *) &T->Forks, sizeof(short));
switch ( T->NodeType )
{
case BrDiscr:
break;
case ThreshContin:
StreamOut((char *) &T->Cut, sizeof(float));
StreamOut((char *) &T->Lower, sizeof(float));
StreamOut((char *) &T->Upper, sizeof(float));
break;
case BrSubset:
Bytes = (MaxAttVal[T->Tested]>>3) + 1;
ForEach(v, 1, T->Forks)
{
StreamOut((char *) T->Subset[v], Bytes);
}
break;
}
ForEach(v, 1, T->Forks)
{
OutTree(T->Branch[v]);
}
}
}
/*************************************************************************/
/* */
/* Obtiene el árbol de decisión con extensión Extension */
/* */
/*************************************************************************/
Tree TDMC45::GetTree(AnsiString Extension)
{
Tree Hold;
static char *LastExt="";
if ( strcmp(LastExt, Extension.c_str()) )
{
LastExt = Extension.c_str();
if ( TRf ) fclose(TRf);
strcpy(Fn, FileName.c_str());
strcat(Fn, Extension.c_str());
if ( ! ( TRf = fopen(Fn, "r") ) )
{
FVitacora<<"Error al leer del archivo del árbol";
FVitacora<<endl;
}
}
Hold = InTree();
RecoverDiscreteNames();
return Hold;
}
/*************************************************************************/
/* */
/* Recuperar el árbol a partir de los caracteres guardados */
/* */
/*************************************************************************/
Tree TDMC45::InTree()
{
Tree T;
DiscrValue v;
int Bytes;
T = (Tree) malloc(sizeof(tree_record));
if ( T->NodeType )
{
StreamIn((char *) &T->Tested, sizeof(Attribute));
StreamIn((char *) &T->Forks, sizeof(short));
switch ( T->NodeType )
{
case BrDiscr:
break;
case ThreshContin:
StreamIn((char *) &T->Cut, sizeof(float));
StreamIn((char *) &T->Lower, sizeof(float));
StreamIn((char *) &T->Upper, sizeof(float));
break;
case BrSubset:
T->Subset = (Conjunto *) calloc(T->Forks + 1, sizeof(Conjunto));
Bytes = (MaxAttVal[T->Tested]>>3) + 1;
ForEach(v, 1, T->Forks)
{
T->Subset[v] = (Conjunto) malloc(Bytes);
StreamIn((char *) T->Subset[v], Bytes);
}
}
}
}
return T;
}
/*************************************************************************/
/* */
/* Stream caracteres de/hacia el archivo TRf desde/hacia una dir */
/* */
/*************************************************************************/
void TDMC45::StreamOut(AnsiString s,int n)
{char *ss;
ss=s.c_str();
while ( n-- ) putc(*ss++, TRf);
}
/*************************************************************************/
/* */
/* Libera el espacio ocupado por Node */
/* */
/*************************************************************************/
void TDMC45::ReleaseTree(Tree Node)
{
DiscrValue v;
if ( Node->NodeType )
{
ForEach(v, 1, Node->Forks)
{
ReleaseTree(Node->Branch[v]);
}
free(Node->Branch);
if ( Node->NodeType == BrSubset )
{
free(Node->Subset);
}
free(Node->ClassDist);
free(Node);
}
/*************************************************************************/
/* */
/* Cuenta los nodos de un árbol */
/* */
/*************************************************************************/
int TDMC45::TreeSize(Tree Node)
{
int Sum=0;
DiscrValue v;
if ( Node->NodeType )
{
ForEach(v, 1, Node->Forks)
{
Sum += TreeSize(Node->Branch[v]);
}
}
return Sum + 1;
}
/*************************************************************************/
/* */
/* Devuelve una copia de tree T */
/* */
/*************************************************************************/
Tree TDMC45::CopyTree(Tree T)
{
DiscrValue v;
Tree New;
if ( T->NodeType )
{
New->Branch = (Tree *) calloc(T->Forks + 1, sizeof(Tree));
ForEach(v, 1, T->Forks)
{
New->Branch[v] = CopyTree(T->Branch[v]);
}
}
return New;
}
/*************************************************************************/
/* */
/* Guarda los valores de los atributos leídos con "discrete N" */
/* */
/*************************************************************************/
void TDMC45::SaveDiscreteNames()
{
Attribute Att;
DiscrValue v;
int Length;
ForEach(Att, 0, MaxAtt)
{
if ( SpecialStatus[Att] != "DISCRETE" ) continue;
ForEach(v, 1, MaxAttVal[Att])
{
Length = AttValName[Att][v].Length() + 1;
/*************************************************************************/
/* */
/* Recupera los valores de los atributos leídos con "discrete N" */
/* */
/*************************************************************************/
void TDMC45::RecoverDiscreteNames()
{
Attribute Att;
DiscrValue v;
int Length;
ForEach(Att, 0, MaxAtt)
{
if ( SpecialStatus[Att] != "DISCRETE" ) continue;
StreamIn(AnsiString(MaxAttVal[Att]), sizeof(int));
ForEach(v, 1, MaxAttVal[Att])
{
StreamIn(AnsiString(Length), sizeof(int));
/*************************************************************************/
/* */
/* Evaluación del armado de subconjuntos sobre un atrib discreto. A */
/* partir de los subconjuntos elegidos Subset[Att][], se asigna a */
/* Subsets[Att] el nro de subconjuntos, y se calculan Info[] y Gain[] */
/* de una prueba sobre el atributo of a test on the attribute. */
/* */
/*************************************************************************/
void TDMC45::EvalSubset(Attribute Att, ItemNo Fp, ItemNo Lp,ItemCount Items)
{
DiscrValue V1, V2, BestV1, BestV2, Barred;
ItemCount KnownItems;
ClassNo c;
float BaseInfo, MinGain, ThisGain, ThisInfo,
Val, BestVal, BestGain, BestInfo,
PrevVal, PrevGain, PrevInfo;
short Blocks=0, MissingValues=0, ReasonableSubsets, Bytes, b;
bool MergedSubsets = false;
int SaveMINOBJS;
SaveMINOBJS = MINOBJS;
MINOBJS = 1;
Gain[Att] = -Epsilon;
Info[Att] = 0;
return;
}
Verbosity(2)
{
FVitacora<<AttName->Strings[Att].c_str();
FVitacora<<"inf ";
FVitacora<<PrevInfo;
FVitacora<<" ganancia ";
FVitacora<<PrevGain;
FVitacora<<" val=";
FVitacora<<PrevVal;
FVitacora<<endl;
}
Bytes = (MaxAttVal[Att]>>3) + 1;
ClearBits(Bytes, Subset[Att][0]);
ForEach(V1, 1, MaxAttVal[Att])
{
if ( ValFreq[V1] > 0.5 )
{
if ( ++Blocks < V1 )
{
ValFreq[Blocks] = ValFreq[V1];
ForEach(c, 0, MaxClass)
{
Freq[Blocks][c] = Freq[V1][c];
}
}
ClearBits(Bytes, Subset[Att][Blocks]);
SetBit(V1, Subset[Att][Blocks]);
}
else
{
SetBit(V1, Subset[Att][0]);
MissingValues++;
}
}
/* Unimos los subconjuntos de una única clase con otrs de la misma clase */
/* Nota: ValFreq[V] > 0 para todo V */
ForEach(V1, 1, Blocks-1)
{
for ( c = 0 ; Freq[V1][c] < 0.1 ; c++ )
;
ForEach(b, 0, Bytes-1)
{
Subset[Att][V1][b] |= Subset[Att][V2][b];
Subset[Att][V2][b] = Subset[Att][Blocks][b];
}
Blocks--;
MergedSubsets = true;
}
}
}
if ( MergedSubsets )
{
PrevGain = ComputeGain(BaseInfo, UnknownRate[Att], Blocks, KnownItems);
PrevInfo = TotalInfo(ValFreq, 0, Blocks) / Items;
PrevVal = Worth(PrevInfo, PrevGain, Epsilon);
Verbosity(2)
{
FVitacora<<"Luego de unir subconjuntos de una única clase:";
FVitacora<<"inf ";
FVitacora<<PrevInfo;
FVitacora<<" ganancia ";
FVitacora<<PrevGain;
FVitacora<<" val=";
FVitacora<<PrevVal;
FVitacora<<endl;
}
}
MinGain = PrevGain / 2;
ReasonableSubsets = 0;
Barred = 1;
ForEach(V1, 1, Blocks)
{
if ( ValFreq[V1] >= SaveMINOBJS ) ReasonableSubsets++;
ForEach(V1, 1, Blocks-1)
{
ForEach(V2, V1+1, Blocks)
{
if ( V1 == Barred || V2 == Barred ) continue;
Verbosity(4)
{
FVitacora<<"combinación ";
FVitacora<<V1;
FVitacora<<" ";
FVitacora<<V2;
FVitacora<<" info ";
FVitacora<<ThisInfo;
FVitacora<<" ganancia ";
FVitacora<<ThisGain;
FVitacora<<" val ";
FVitacora<<Val;
PrintDistribution(Att, Blocks-1, false);
}
Uncombine(V1, V2);
}
}
if ( GAINRATIO &&
PrevGain = BestGain;
PrevInfo = BestInfo;
PrevVal = BestVal;
ForEach(b, 0, Bytes-1)
{
Subset[Att][BestV1][b] |= Subset[Att][BestV2][b];
Subset[Att][BestV2][b] = Subset[Att][Blocks][b];
}
Blocks--;
Verbosity(2)
{
FVitacora<<" formamos el subconjunto ";
PrintSubset(Att, Subset[Att][BestV1]);
FVitacora<<": ";
FVitacora<<Blocks;
FVitacora<<" subconjuntos, inf ";
FVitacora<<BestInfo;
FVitacora<<" ganancia ";
FVitacora<<BestGain;
FVitacora<<" val ";
FVitacora<<BestVal;
Verbosity(3)
{
FVitacora<<" combinación ";
FVitacora<<BestV1;
FVitacora<<" ";
FVitacora<<BestV2;
PrintDistribution(Att, Blocks, false);
}
}
}
MINOBJS = SaveMINOBJS;
if ( PrevVal <= 0 )
{
Gain[Att] = -Epsilon;
Info[Att] = 0;
}
else
{
Gain[Att] = ComputeGain(BaseInfo, UnknownRate[Att], Blocks, KnownItems);
Info[Att] = PrevInfo;
if ( MissingValues )
{
Blocks++;
CopyBits(Bytes, Subset[Att][0], Subset[Att][Blocks]);
}
Subsets[Att] = Blocks;
/*************************************************************************/
/* */
/* Combina los valores de la distribución de los atributos discretos */
/* x e y, guardando los nuevos valores en Freq[x][] y ValFreq[x][]. */
/* Los valores anteriores se guardan en Slice1 y Slice2 */
/* */
/*************************************************************************/
void TDMC45::Combine(DiscrValue x,DiscrValue y,DiscrValue Last)
{
ClassNo c;
ForEach(c, 0, MaxClass)
{
Slice1[c] = Freq[x][c];
Slice2[c] = Freq[y][c];
Freq[x][c] += Freq[y][c];
Freq[y][c] = Freq[Last][c];
}
Slice1[MaxClass+1] = ValFreq[x];
Slice2[MaxClass+1] = ValFreq[y];
ValFreq[x] += ValFreq[y];
ValFreq[y] = ValFreq[Last];
}
/*************************************************************************/
/* */
/* Devuelve a x e y los valores de distribución originales a partir */
/* de Slice1 y Slice2 */
/* */
/*************************************************************************/
void TDMC45::Uncombine(DiscrValue x,DiscrValue y)
{
ClassNo c;
ForEach(c, 0, MaxClass)
{
Freq[x][c] = Slice1[c];
Freq[y][c] = Slice2[c];
}
ValFreq[x] = Slice1[MaxClass+1];
ValFreq[y] = Slice2[MaxClass+1];
}
/*************************************************************************/
/* */
/* Imprime los valores del atributo Att que estan en el subconjunto Ss */
/* */
/*************************************************************************/
void TDMC45::PrintSubset(Attribute Att,Conjunto Ss)
{
DiscrValue V1;
bool First=true;
ForEach(V1, 1, MaxAttVal[Att])
{
if ( In(V1, Ss) )
{
if ( First )
{
First = false;
}
else
{
FVitacora<<", ";
}
FVitacora<<AttValName[Att][V1].c_str();
}
}
}
/*************************************************************************/
/* */
/* Construye una prueba de nodo sobre un subconjunto de valores */
/* */
/*************************************************************************/
void TDMC45::SubsetTest(Tree Node,Attribute Att)
{
short S, Bytes;
Sprout(Node, Subsets[Att]);
Node->NodeType = BrSubset;
Node->Tested = Att;
Node->Errors = 0;
Bytes = (MaxAttVal[Att]>>3) + 1;
Node->Subset = (Conjunto *) calloc(Subsets[Att] + 1, sizeof(Conjunto));
ForEach(S, 1, Node->Forks)
{
Node->Subset[S] = (Conjunto) malloc(Bytes);
CopyBits(Bytes, Subset[Att][S], Node->Subset[S]);
}
}
/*************************************************************************/
/* */
/* Setea Info[] and Gain[] para la partición discreta de items */
/* Fp hasta Lp */
/* */
/*************************************************************************/
void TDMC45::EvalDiscreteAtt(Attribute Att,ItemNo Fp,ItemNo Lp,ItemCount Items)
{
ItemCount KnownItems;
Gain[Att] = -Epsilon;
Info[Att] = 0.0;
return;
}
Verbosity(2)
{
FVitacora<<AttName->Strings[Att].c_str();
Verbosity(3) PrintDistribution(Att, MaxAttVal[Att], true);
FVitacora<<Info[Att];
FVitacora<<" ganancia ";
FVitacora<<Gain[Att];
FVitacora<<endl;
}
/*************************************************************************/
/* */
ResetFreq(MaxAttVal[Att]);
ForEach(v, 0, MaxAttVal[Att])
{
ForEach(c, 0, MaxClass)
{
ValFreq[v] += Freq[v][c];
}
}
/*************************************************************************/
/* */
/* Devuelve la información base de los itemscon valores conocidos */
/* de un atributo discreto, utilizando la tabla de frecuencias Freq[][] */
/* */
/*************************************************************************/
float TDMC45::DiscrKnownBaseInfo(DiscrValue KnownItems,ItemCount MaxVal)
{
ClassNo c;
ItemCount ClassCount;
double Sum=0;
DiscrValue v;
ForEach(c, 0, MaxClass)
{
ClassCount = 0;
ForEach(v, 1, MaxVal)
{
ClassCount += Freq[v][c];
}
Sum += ClassCount * Log(ClassCount);
}
/*************************************************************************/
/* */
/* Construye y devuelve un nodo para una prueba sobre un */
/* atributo discreto */
/* */
/*************************************************************************/
void TDMC45::DiscreteTest(Tree Node,Attribute Att)
{
ItemCount CountItems();
Sprout(Node, MaxAttVal[Att]);
Node->NodeType = BrDiscr;
Node->Tested = Att;
Node->Errors = 0;
}
/*************************************************************************/
/* */
/* Evaluación de una prueba sobre un atributo continuo */
/* --------------------------------------------------- */
/* */
/*************************************************************************/
/*************************************************************************/
/* */
/* Trabajamos ocmo si los atributos continuos tuviesen los siguientes */
/* valores posibles: */
/* 0 (desconocido), 1 (menor que el corte), 2(mayor que el corte) */
/* Esta rutina busca el mejor corte para los items desde Fp hasta Lp */
/* y setea Info[], Gain[] and Bar[] */
/* */
/*************************************************************************/
void TDMC45::EvalContinuousAtt(Attribute Att,ItemNo Fp,ItemNo Lp)
{
ItemNo i, BestI, Xp, Tries=0;
ItemCount Items, KnownItems, LowItems, MinSplit;
ClassNo c;
float AvGain=0, Val, BestVal, BaseInfo, ThreshCost;
Verbosity(2)
{
FVitacora<<"Atrib ";
FVitacora<<AttName->Strings[Att].c_str();
}
Verbosity(3) FVitacora<<endl;
ResetFreq(2);
ValFreq[0] = 0;
ForEach(c, 0, MaxClass)
{
ValFreq[0] += Freq[0][c];
}
Gain[Att] = -Epsilon;
Info[Att] = 0.0;
return;
}
LowItems = 0;
ForEach(i, Xp, Lp - 1)
{
c = Class(Item[i]);
LowItems += Weight[i];
Freq[1][c] += Weight[i];
Freq[2][c] -= Weight[i];
Verbosity(3)
{ FVitacora<<" Corte en ";
FVitacora<<(( CVal(Item[i],Att) + CVal(Item[i+1],Att) ) / 2);
FVitacora<<" (ganancia ";
FVitacora<<SplitGain[i];
FVitacora<<" val ";
FVitacora<<Worth(SplitInfo[i], SplitGain[i], Epsilon);
FVitacora<<"):";
PrintDistribution(Att, 2, true);
}
}
}
BestVal = 0;
BestI = None;
ForEach(i, Xp, Lp - 1)
{
if ( (Val = SplitGain[i] - ThreshCost) > BestVal )
{
BestI = i;
BestVal = Val;
}
}
if ( BestI == None )
{
Gain[Att] = -Epsilon;
Info[Att] = 0.0;
Verbosity(2)
{ FVitacora<<" no hay ganancia ";
FVitacora<<endl;
}
}
else
{
Bar[Att] = (CVal(Item[BestI],Att) + CVal(Item[BestI+1],Att)) / 2;
Gain[Att] = BestVal;
Info[Att] = SplitInfo[BestI];
Verbosity(2)
{
FVitacora<<" corte=";
FVitacora<<Bar[Att];
FVitacora<<" inf ";
FVitacora<<Info[Att];
FVitacora<<" ganancia ";
FVitacora<<Gain[Att];
FVitacora<<endl;
}
}
}
/*************************************************************************/
/* */
/* Transforma una hoja en una prueba sobre un atributo continuo */
/* */
/*************************************************************************/
void TDMC45::ContinTest(Tree Node,Attribute Att)
{
float Thresh;
Sprout(Node, 2);
Node->NodeType = ThreshContin;
Node->Tested = Att;
Node->Cut =
Node->Lower =
Node->Upper = Thresh;
Node->Errors = 0;
}
/*************************************************************************/
/* */
/* Devuelve el mayor valor del atributo Att por debajo del límite t */
/* */
/*************************************************************************/
float TDMC45::GreatestValueBelow(Attribute Att,float t)
{
ItemNo i;
float v, Best;
bool NotYet=true;
ForEach(i, 0, MaxItem)
{
v = CVal(Item[i], Att);
if ( v != Unknown && v <= t && ( NotYet || v > Best ) )
{
Best = v;
NotYet = false;
}
}
return Best;
}
/*************************************************************************/
/* */
if ( Fp < Lp )
{
Thresh = CVal(Item[Lp], Att);
Middle = Fp;
Lower = Middle - 1;
// (*Exchange)(Middle, Lp);
Intercambio(Middle,Lp);
/*************************************************************************/
/* */
/* Poda el árbol T y devuelve true si el árbol fue modificado */
/* */
/*************************************************************************/
bool TDMC45::Prune(Tree T)
{
ItemNo i;
Attribute a;
InitialiseWeights();
AllKnown = true;
Verbosity(1) FVitacora<<endl;
Changed = false;
if ( SUBSET )
{
if ( ! PossibleValues )
{
PossibleValues = (Conjunto *) calloc(MaxAtt+1, sizeof(Conjunto));
}
ForEach(a, 0, MaxAtt)
{
if ( MaxAttVal[a] )
{
PossibleValues[a] = (Conjunto) malloc((MaxAttVal[a]>>3) + 1);
ClearBits((MaxAttVal[a]>>3) + 1, PossibleValues[a]);
ForEach(i, 1, MaxAttVal[a])
{
SetBit(i, PossibleValues[a]);
}
}
}
CheckPossibleValues(T);
}
return Changed;
}
/*************************************************************************/
/* */
/* Estima los errores en un determinado subárbol */
/* */
/*************************************************************************/
float TDMC45::EstimateErrors(Tree T, ItemNo Fp,ItemNo Lp,short Sh,bool UpdateTree)
{
ItemNo i, Kp, Ep;
ItemCount Cases, KnownCases, *LocalClassDist, TreeErrors, LeafErrors,
ExtraLeafErrors, BranchErrors,Factor, MaxFactor;
DiscrValue v, MaxBr;
ClassNo c, BestClass;
bool PrevAllKnown;
BestClass = T->Leaf;
ForEach(c, 0, MaxClass)
{
if ( LocalClassDist[c] > LocalClassDist[BestClass] )
{
BestClass = c;
}
}
LeafErrors = Cases - LocalClassDist[BestClass];
ExtraLeafErrors = AddErrs(Cases, LeafErrors);
if ( UpdateTree )
{
T->Items = Cases;
T->Leaf = BestClass;
memcpy(T->ClassDist, LocalClassDist, (MaxClass + 1) * sizeof(ItemCount));
}
if ( ! T->NodeType ) /* hoja */
{
TreeErrors = LeafErrors + ExtraLeafErrors;
if ( UpdateTree )
{
T->Errors = TreeErrors;
LocalVerbosity(1)
{
Intab(Sh);
FVitacora<<ClassName->Strings[T->Leaf].c_str();
FVitacora<<" ( ";
FVitacora<<T->Items;
FVitacora<<":";
FVitacora<<LeafErrors;
FVitacora<<"/";
FVitacora<<T->Errors;
FVitacora<<endl;
}
}
free(LocalClassDist);
return TreeErrors;
}
PrevAllKnown = AllKnown;
if ( Kp != Fp ) AllKnown = false;
TreeErrors = MaxFactor = 0;
ForEach(v, 1, T->Forks)
{
Ep = Group(v, Kp, Lp, T);
if ( Kp <= Ep )
{
Factor = CountItems(Kp, Ep) / KnownCases;
AllKnown = PrevAllKnown;
if ( ! UpdateTree )
{
free(LocalClassDist);
return TreeErrors;
}
LocalVerbosity(1)
{
Intab(Sh);
FVitacora<<AttName->Strings[T->Tested].c_str();
FVitacora<<": [";
FVitacora<<((int) ((TreeErrors * 100) / (T->Items + 0.001)));
FVitacora<<"N=";
FVitacora<<T->Items;
FVitacora<<" árbol=";
FVitacora<<TreeErrors;
FVitacora<<" hoja=";
FVitacora<<LeafErrors;
FVitacora<<"+";
FVitacora<<ExtraLeafErrors;
FVitacora<<" br[";
FVitacora<<MaxBr;
FVitacora<<"]=";
FVitacora<<BranchErrors;
FVitacora<<endl;
}
T->NodeType = 0;
T->Errors = LeafErrors + ExtraLeafErrors;
Changed = true;
}
else
if ( BranchErrors <= TreeErrors + 0.1 )
{
LocalVerbosity(1)
{
Intab(Sh);
FVitacora<<"Reemplazado por la rama ";
FVitacora<<MaxBr;
FVitacora<<endl;
}
AllKnown = PrevAllKnown;
EstimateErrors(T->Branch[MaxBr], Fp, Lp, Sh, true);
memcpy((char *) T, (char *) T->Branch[MaxBr], sizeof(tree_record));
Changed = true;
}
else
{
T->Errors = TreeErrors;
}
AllKnown = PrevAllKnown;
free(LocalClassDist);
return T->Errors;
}
/*************************************************************************/
/* */
/* Elimina pruebas de subconjuntos innecesarias sobre */
/* valores faltantes */
/* */
/*************************************************************************/
void TDMC45::CheckPossibleValues(Tree T)
{
Conjunto HoldValues;
int v, Bytes, b;
Attribute A;
char Any=0;
if ( T->NodeType == BrSubset )
{
A = T->Tested;
Bytes = (MaxAttVal[A]>>3) + 1;
HoldValues = (Conjunto) malloc(Bytes);
ForEach(b, 0, Bytes-1)
{
T->Subset[T->Forks][b] &= PossibleValues[A][b];
Any |= T->Subset[T->Forks][b];
}
if ( ! Any )
{
T->Forks--;
}
ForEach(v, 1, T->Forks)
{
CopyBits(Bytes, T->Subset[v], PossibleValues[A]);
CheckPossibleValues(T->Branch[v]);
}
free(HoldValues);
}
else
if ( T->NodeType )
{
ForEach(v, 1, T->Forks)
{
CheckPossibleValues(T->Branch[v]);
}
}
}
/*************************************************************************/
/* */
/* Computa los errores adicionales si la proporción de error llega al */
/* límite superior del nivel de confianza. El coeficiente es el */
/* cuadrado del nro de desviaciones estándar correspondientes al */
/* nivel de confianza seleccionado. */
/* (Basado en el Documenta Geigy Scientific Tables (Sixth Edition), */
/* p185 (with modifications).) */
/* */
/*************************************************************************/
float TDMC45::AddErrs(ItemCount N, ItemCount e)
{
static float Coeff=0;
float Val0, Pr;
if ( ! Coeff )
{
/* Computa y retiene el valor del coeficiente, interpolando los
valores de Val y Dev */
int i;
i = 0;
while ( CF > Val[i] ) i++;
Coeff = Dev[i-1] +
if ( e < 1E-6 )
{
return N * (1 - exp(log(CF) / N));
}
else
if ( e < 0.9999 )
{
Val0 = N * (1 - exp(log(CF) / N));
return Val0 + e * (AddErrs(N, 1.0) - Val0);
}
else
if ( e + 0.5 >= N )
{
return 0.67 * (N - e);
}
else
{
Pr = (e + 0.5 + Coeff/2
+ sqrt(Coeff * ((e + 0.5) * (1 - (e + 0.5)/N) + Coeff/4)) )
/ (N + Coeff);
return (N * Pr - e);
}
}
/*************************************************************************/
/* */
/* Categoriza la descripción de un caso utilizando el árbol de decisión */
/* */
/*************************************************************************/
ClassNo TDMC45::Category(Description CaseDesc,Tree DecisionTree)
{
ClassNo c, BestClass;
if ( ! ClassSum )
{
ClassSum = (float *) malloc((MaxClass+1) * sizeof(float));
}
ForEach(c, 0, MaxClass)
{
ClassSum[c] = 0;
}
BestClass = 0;
ForEach(c, 0, MaxClass)
{
Verbosity(5) printf("clase %s peso %.2f\n", ClassName[c], ClassSum[c]);
return BestClass;
}
/*************************************************************************/
/* */
/* Clasifica un caso utilizando el subárbol dado, ajustando el valor */
/* de ClassSum para cada clase */
/* */
/*************************************************************************/
void TDMC45::Classify(Description CaseDesc,Tree T,float Weight)
{
DiscrValue v, dv;
float Cv;
Attribute a;
ClassNo c;
switch ( T->NodeType )
{
case 0: /* hoja */
if ( T->Items > 0 )
{
/* Actualizamos a partir de TODAS las clases */
ForEach(c, 0, MaxClass)
{
if ( T->ClassDist[c] )
{
ClassSum[c] += Weight * T->ClassDist[c] / T->Items;
}
}
}
else
{
ClassSum[T->Leaf] += Weight;
}
return;
a = T->Tested;
v = DVal(CaseDesc, a);
return;
a = T->Tested;
Cv = CVal(CaseDesc, a);
if ( Cv == Unknown )
{
ForEach(v, 1, 2)
{
Classify(CaseDesc, T->Branch[v],
(Weight * T->Branch[v]->Items) / T->Items);
}
}
else
{
v = ( Cv <= T->Cut ? 1 : 2 );
Classify(CaseDesc, T->Branch[v], Weight);
}
return;
a = T->Tested;
dv = DVal(CaseDesc, a);
if ( dv )
{
ForEach(v, 1, T->Forks)
{
if ( In(dv, T->Subset[v]) )
{
Classify(CaseDesc, T->Branch[v], Weight);
return;
}
}
}
ForEach(v, 1, T->Forks)
{
Classify(CaseDesc, T->Branch[v],
(Weight * T->Branch[v]->Items) / T->Items);
}
return;
}
}
/*************************************************************************/
/* */
/* Imprime matrices de confusión */
/* */
/*************************************************************************/
void TDMC45::PrintConfusionMatrix(ItemNo *ConfusionMat)
{
short Row, Col;
FVitacora<<endl;
FVitacora<<endl;
FVitacora<<" ";
ForEach(Col, 0, MaxClass)
{
FVitacora<<" (";
FVitacora<<('a' + Col);
FVitacora<<")";
}
ForEach(Row, 0, MaxClass)
{
FVitacora<<" ";
ForEach(Col, 0, MaxClass)
{
if ( ConfusionMat[Row*(MaxClass+1) + Col] )
{
FVitacora<<ConfusionMat[Row*(MaxClass+1) + Col];
}
else
{
FVitacora<<" ";
}
}
FVitacora<<" (";
FVitacora<<('a'+Row);
FVitacora<<"): clase";
FVitacora<<ClassName->Strings[Row].c_str();
FVitacora<<endl;
}
FVitacora<<endl;
}
/**************************************************************************************/
//Inicializamos el archivo
strcat(outFile, DateTimeToStr(Now()).c_str());
strcat(outFile, Params->TablaBD.c_str());
strcat(outFile, ".log");
FReglas.open(outFile, ios::out);
FReglas<<"C4.5 Generador de reglas ";
FReglas<<DateTimeToStr(Now()).c_str();
FReglas<<endl;
FReglas<<"---------------------------";
FReglas<<endl;
FReglas<<endl;
/* Inicialización */
GetNames();
GetData();
FReglas<<(MaxItem+1);
FReglas<<" casos (";
FReglas<<MaxAtt;
FReglas<<" atributos) leídos de ";
FReglas<<Params->TablaBD.c_str();
GenerateLogs();
/* Construcción de reglas */
GenerateRules();
/* Evaluaciones */
FReglas<<endl;
FReglas<<endl;
FReglas<<"Evaluación sobre los datos de entrenamiento (";
FReglas<<MaxItem+1;
FReglas<<" ítems):";
FReglas<<endl;
EvaluateRulesets(true);
SaveRules();
if ( UNSEENS )
{
GetData();
FReglas<<endl;
FReglas<<endl;
FReglas<<"Evaluación sobre los datos de prueba (";
FReglas<<MaxItem+1;
FReglas<<" ítems):";
FReglas<<endl;
EvaluateRulesets(false);
}
exit(0);
}
/*************************************************************************/
/* */
/* Creación del vector LogItemNo para contener los logaritmos de */
/* enteros, y el vector LogFact para contener los logaritmos de los */
/* factoriales (todos en base 2) */
/* */
/*************************************************************************/
void TDMC45::GenerateLogs()
{
ItemNo i;
LogItemNo[0] = -1E38;
LogItemNo[1] = 0;
LogFact[0] = LogFact[1] = 0;
ForEach(i, 2, MaxItem+99)
{
LogItemNo[i] = log((float) i) / Log2;
LogFact[i] = LogFact[i-1] + LogItemNo[i];
}
}
/*************************************************************************/
/* */
/* Para cada árbol, creamos un conj de reglas y lo procesamos. Luego */
/* componemos un conjunto de reglas a partir de estos conjuntos. */
/* Si hay un sólo árbol, entoncesno se compone un nuevo conjunto. */
/* */
/* Los conj de reglas se almacenan desde PRSet[0] hasta PRSet[TRIALS], */
/* donde PRSet[TRIALS] contiene el conjunto compuesto. */
/* */
/* Al terminar, el conjunto actual es el compuesto (si es que uno se */
/* creó), sino es el conjunto de un árbol. */
/* */
/*************************************************************************/
void TDMC45::GenerateRules()
{
Tree DecisionTree, GetTree();
short t=0, RuleSetSpace=0, r;
FindTestCodes();
FormRules(DecisionTree);
ConstructRuleset();
FReglas<<endl;
FReglas<<"Reglas finales del árbol: ";
FReglas<<t;
FReglas<<endl;
PrintIndexedRules();
if ( t + 1 >= RuleSetSpace )
{
RuleSetSpace += 10;
if ( RuleSetSpace > 10 )
{
PRSet = (RuleSet *) realloc(PRSet, RuleSetSpace * sizeof(RuleSet));
}
else
{
PRSet = (RuleSet *) malloc(RuleSetSpace * sizeof(RuleSet));
}
PRSet[t].SNRules = NRules;
PRSet[t].SRule = Rule;
PRSet[t].SRuleIndex = RuleIndex;
PRSet[t].SDefaultClass = DefaultClass;
++t;
}
if ( ! t )
{
FReglas<<endl;
FReglas<<"ERROR: no se encuentra ningun árbol de decisión";
FReglas<<endl;
exit(1);
}
TRIALS = t;
if ( TRIALS > 1 )
{
CompositeRuleset();
}
}
/*************************************************************************/
/* */
/* Determina las longitudes de los codigos para los atributos */
/* y las ramas */
/* */
/*************************************************************************/
void TDMC45::FindTestCodes()
{
Attribute Att;
DiscrValue v, V;
ItemNo i, *ValFreq;
int PossibleCuts;
float Sum, SumBranches=0, p;
void SwapUnweighted();
ForEach(Att, 0, MaxAtt)
{
if ( (V = MaxAttVal[Att]) )
{
ValFreq = (ItemNo *) calloc(V+1, sizeof(ItemNo));
ForEach(i, 0, MaxItem)
{
ValFreq[DVal(Item[i],Att)]++;
}
Sum = 0;
ForEach(v, 1, V)
{
if ( ValFreq[v] )
{
Sum += (ValFreq[v] / (MaxItem+1.0)) *
(LogItemNo[MaxItem+1] - LogItemNo[ValFreq[v]]);
}
}
free(ValFreq);
BranchBits[Att] = Sum;
}
else
{
Quicksort(0, MaxItem, Att);
PossibleCuts = 1;
ForEach(i, 1, MaxItem)
{
if ( CVal(Item[i],Att) > CVal(Item[i-1],Att) )
{
PossibleCuts++;
}
}
SumBranches += BranchBits[Att];
}
AttTestBits = 0;
ForEach(Att, 0, MaxAtt)
{
if ( (p = BranchBits[Att] / SumBranches) > 0 )
{
AttTestBits -= p * log(p) / log(2.0);
}
}
}
/*************************************************************************/
/* */
/* Intercambio de los items en a y b. */
/* */
/*************************************************************************/
void TDMC45::SwapUnweighted(ItemNo a,ItemNo b)
{
Description Hold;
Hold = Item[a];
Item[a] = Item[b];
Item[b] = Hold;
}
/*************************************************************************/
/* */
/* Creación de un conjunto compuesto de reglas de todas las pruebas */
/* */
/*************************************************************************/
void TDMC45::CompositeRuleset()
{
RuleNo r;
short t, ri;
InitialiseRules();
ForEach(t, 0, TRIALS-1)
{
ForEach(ri, 1, PRSet[t].SNRules)
{
r = PRSet[t].SRuleIndex[ri];
NewRule(PRSet[t].SRule[r].Lhs, PRSet[t].SRule[r].Size,
PRSet[t].SRule[r].Rhs, PRSet[t].SRule[r].Error);
}
}
/* Seleccionamos un subconjunto */
ConstructRuleset();
FReglas<<endl;
PRSet[TRIALS].SNRules = NRules;
PRSet[TRIALS].SRule = Rule;
PRSet[TRIALS].SRuleIndex = RuleIndex;
PRSet[TRIALS].SDefaultClass = DefaultClass;
}
/*************************************************************************/
/* */
/* Creación de un conjunto de reglas del árbol de decisión t */
/* */
/*************************************************************************/
void TDMC45::FormRules(Tree t)
{
short i;
MaxDepth = 0;
MaxDisjuncts = 0;
TreeParameters(t, 0);
ForEach(i, 0, MaxDepth+1)
{
CondSatisfiedBy[i] = (bool *) calloc(MaxItem+1, sizeof(char));
Stack[i] = (Condition) malloc(sizeof(struct CondRec));
}
InitialiseRules();
Scan(t, 0);
/* Liberamos almacenamiento */
ForEach(i, 0, MaxDepth+1)
{
free(CondSatisfiedBy[i]);
free(Stack[i]);
}
free(Deleted);
free(CondSatisfiedBy);
free(Stack);
free(Actual);
free(Total);
free(Errors);
free(Pessimistic);
free(CondSigLevel);
free(TargetClassFreq);
}
/*************************************************************************/
/* */
/* Calcula la profundidad máx y el nro de hojas en el arbol t con */
/* profundidad inicial d */
/* */
/*************************************************************************/
void TDMC45::TreeParameters(Tree t,short d)
{
DiscrValue v;
if ( t->NodeType )
{
ForEach(v, 1, t->Forks)
{
TreeParameters(t->Branch[v], d+1);
}
}
else
{
/* Es una hoja */
/*************************************************************************/
/* */
/* Extrae los disjuntos del arbol t en la profundidad d y los procesa */
/* */
/*************************************************************************/
void TDMC45::Scan(Tree t,short d)
{
DiscrValue v;
short i;
Condition *Term;
Test x, FindTest();
if ( t->NodeType )
{
d++;
Stack[d]->CondTest = FindTest();
ForEach(v, 1, t->Forks)
{
Stack[d]->TestValue = v;
Scan(t->Branch[v], d);
}
}
else
if ( t->Items >= 1 )
{
/* Hoja del árbol de decisión - construimos el conjunto de
condiciones asociadas con esta hoja y las podamos */
Term = (Condition *) calloc(d+1, sizeof(Condition));
ForEach(i, 1, d)
{
Term[i] = (Condition) malloc(sizeof(struct CondRec));
Term[i]->CondTest = Stack[i]->CondTest;
Term[i]->TestValue = Stack[i]->TestValue;
}
PruneRule(Term, d, t->Leaf);
free(Term);
}
}
/*************************************************************************/
/* */
/* Construcción de un subconjunto ordenado (indexado por el RuleIndex) */
/* a partir del actual conjunto de reglas */
/* */
/*************************************************************************/
void TDMC45::ConstructRuleset()
{
RuleNo r, OldNRules = NRules;
ForEach(r, 1, NRules)
{
Match[r] = (Boolean *) calloc(MaxItem+1, sizeof(Boolean));
}
InitialiseTables();
FindRuleCodes();
CodeWeight = 0.5;
ForEach(FocusClass, 0, MaxClass)
{
CoverClass();
}
MakeIndex();
FindDefault();
free(Value);
free(RuleIn);
free(ClassRules);
free(Subset);
free(Covered);
free(FalsePos);
free(NoRule);
ForEach(r, 1, OldNRules)
{
free(Match[r]);
}
free(Match);
}
/*************************************************************************/
/* */
/* Inicialización de tablas */
/* */
/*************************************************************************/
void TDMC45::InitialiseTables()
{
ItemNo i;
RuleNo r;
ClassNo c;
float Strength();
ForEach(r, 1, NRules)
{
RuleIn[r] = false;
Rule[r].Used = Rule[r].Incorrect = 0;
}
ForEach(c, 0, MaxClass)
{
ClassFreq[c] = 0;
}
ForEach(i, 0, MaxItem)
{
ClassFreq[Class(Item[i])]++;
ForEach(r, 1, NRules)
{
Match[r][i] = Fuerza(Rule[r], Item[i]) > 0.1;
if ( Match[r][i] )
{
Rule[r].Used++;
if ( Class(Item[i]) != Rule[r].Rhs ) Rule[r].Incorrect++;
}
}
}
}
/*************************************************************************/
/* */
/* Seleccionamos un subconjunto de las reglas para la FocusClass */
/* */
/*************************************************************************/
void TDMC45::CoverClass()
{
RuleNo r, RuleCount=0;
ItemNo i;
Verbosity(1)
{
FReglas<<endl;
FReglas<<"Clase ";
FReglas<<ClassName->Strings[FocusClass].c_str();
FReglas<<endl;
FReglas<<"-----";
FReglas<<"Acción Cambio Valor";
}
ForEach(i, 0, MaxItem)
{
Covered[i] = 0;
}
ForEach(r, 1, NRules)
{
if ( Rule[r].Rhs == FocusClass )
{
RuleCount++;
ClassRules[RuleCount] = r;
}
}
if ( ! RuleCount )
{
return;
}
SubsetValue = 1E10;
if ( RuleCount <= 10 )
{
AllCombinations(RuleCount);
}
else
if ( SIMANNEAL )
{
SimAnneal(RuleCount);
}
else
{
SpotSearch(RuleCount);
}
/*************************************************************************/
/* */
/* Probamos todas las combinaciones de reglas para encontrar la mejor */
/* */
/*************************************************************************/
void TDMC45::AllCombinations(RuleNo NR)
{
RuleNo r;
if ( ! NR )
{
CalculateValue();
}
else
{
r = ClassRules[NR];
AllCombinations(NR-1);
AddRule(r);
AllCombinations(NR-1);
DeleteRule(r);
Verbosity(1) FReglas<<endl;
}
}
/*************************************************************************/
/* */
/* Búsqueda de un buen subconjunto mediante simulated annealing */
/* */
/*************************************************************************/
void TDMC45::SimAnneal(RuleNo RuleCount)
{
RuleNo r, OutCount;
short ri, Tries;
float Temp, Delta;
Boolean Changed;
Verbosity(2)
{
OutCount = 0;
ForEach(ri, 1, RuleCount)
{
r = ClassRules[ri];
if ( ! RuleIn[r] )
{
if ( ! (OutCount++ % 3) ) printf("\n\t\t");
FReglas<< r;
FReglas<<" ";
FReglas<<Right[r];
FReglas<<" ";
FReglas<<Wrong[r];
FReglas<<" ";
FReglas<<Value[r];
}
}
FReglas<<endl;
FReglas<<endl;
}
Changed = false;
ri = RuleCount * Random + 1;
r = ClassRules[ri];
Changed = true;
}
}
if ( ! Changed ) break;
}
/*************************************************************************/
/* */
/* Busca un buen subconjunto mediante una búsqueda golosa reiterativa */
/* */
/*************************************************************************/
void TDMC45::SpotSearch(RuleNo RuleCount)
{
RuleNo r;
short ri, Trial;
float ProbIn;
ForEach(Trial, 0, 10)
{
Verbosity(1)
{
FReglas<<endl;
FReglas<<" Prueba ";
FReglas<<Trial;
}
/* Agregamos reglas al azar al subconj inicial */
{
r = ClassRules[ri];
RuleIn[r] = Random < ProbIn;
}
HillClimb(RuleCount);
}
}
/*************************************************************************/
/* */
/* Mejora un subconjunto de reglas agregando o eliminando reglas */
/* */
/*************************************************************************/
void TDMC45::HillClimb(RuleNo RuleCount)
{
RuleNo r, Bestr;
short ri, OutCount;
ItemNo i;
float Delta, BestDelta;
ItemNo aux;
ForEach(i, 0, MaxItem)
{
Covered[i] = 0;
}
ForEach(ri, 1, RuleCount)
{
r = ClassRules[ri];
if ( RuleIn[r] )
{
ForEach(i, 0, MaxItem)
{
if ( Match[r][i] )
{
Covered[i]++;
}
}
}
}
while ( true )
{
CalculateValue();
Verbosity(2)
{
OutCount = 0;
ForEach(ri, 1, RuleCount)
{
r = ClassRules[ri];
if ( ! RuleIn[r] )
{
if ( ! (OutCount++ % 3) ) printf("\n\t\t");
FReglas<<r;
FReglas<<"<";
aux=Right[r]|Wrong[r];
FReglas<<aux;
FReglas<<"=";
FReglas<<Value[r];
}
}
FReglas<<endl;
FReglas<<endl;
}
Bestr = BestDelta = 0;
ForEach(ri, 1, RuleCount)
{
r = ClassRules[ri];
Delta = ( RuleIn[r] ? -Value[r] : Value[r] );
if ( RuleIn[Bestr] )
{
DeleteRule(Bestr);
}
else
{
AddRule(Bestr);
}
}
}
/*************************************************************************/
/* */
/* Calculamos los aciertos y errores para las reglas de la FocusClass */
/* y determinamos el valor de dichas reglas. Si es el mejor hasta */
/* ahora, lo guardamos. */
/* */
/*************************************************************************/
void TDMC45::CalculateValue()
/* -------------- */
{
RuleNo r, Selected=0, InCount;
ItemNo i, Times, FPos=0, FNeg=0, SumCover=0,aux;
float BaseBits, RuleBits=0, NewBits;
ClassNo ThisClass;
Boolean *RuleMatch;
ForEach(i, 0, MaxItem)
{
ThisClass = Class(Item[i]);
if ( Covered[i] )
{
SumCover++;
if( ThisClass != FocusClass ) FPos++;
}
else
if ( ThisClass == FocusClass )
{
FNeg++;
}
}
ForEach(r, 1, NRules)
{
if ( Rule[r].Rhs == FocusClass )
{
Right[r] = Wrong[r] = 0;
if ( RuleIn[r] )
{
RuleBits += Rule[r].Bits;
Selected++;
}
RuleMatch = Match[r];
ForEach(i, 0, MaxItem)
{
if ( RuleMatch[i] &&
( ! (Times = Covered[i]) || Times == 1 && RuleIn[r] ) )
{
if ( Class(Item[i]) == FocusClass )
{
Right[r]++;
}
else
{
Wrong[r]++;
}
}
}
}
}
RuleBits -= LogFact[Selected];
Verbosity(1)
{
FReglas<<" ";
InCount = -1;
}
ForEach(r, 1, NRules)
{
if ( Rule[r].Rhs == FocusClass )
{
if ( RuleIn[r] )
{
NewBits = ExceptionBits(SumCover-Right[r]-Wrong[r],
FPos-Wrong[r], FNeg+Right[r]) +
CodeWeight *
(RuleBits - Rule[r].Bits + LogItemNo[Selected]);
Value[r] = NewBits - BaseBits;
}
else
{
NewBits = ExceptionBits(SumCover+Right[r]+Wrong[r],
FPos+Wrong[r], FNeg-Right[r]) +
CodeWeight *
(RuleBits + Rule[r].Bits - LogItemNo[Selected+1]);
Value[r] = BaseBits - NewBits;
}
Verbosity(1)
{
if ( RuleIn[r] )
{
if ( ++InCount && ! (InCount % 3) ) printf("\n\t\t");
FReglas<<r;
FReglas<<"[";
aux=Right[r]|Wrong[r];
FReglas<<aux;
FReglas<<"=";
FReglas<<Value[r];
}
}
}
}
Verbosity(1)
{
FReglas<<endl;
FReglas<<" ";
FReglas<<Selected;
FReglas<<" reglas, ";
FReglas<<SumCover;
FReglas<<" cant de veces en que la regla se usó: ";
FReglas<<"F+=";
FReglas<<FPos;
FReglas<<" F-=";
FReglas<<FNeg;
FReglas<<", ";
FReglas<<BaseBits;
FReglas<<"bits (reglas=";
FReglas<<RuleBits;
FReglas<<endl;
}
{
SubsetValue = BaseBits;
memcpy(Subset, RuleIn, NRules+1);
}
}
/*************************************************************************/
/* */
/* Agregamos la regla r al conj de relgas incluidas e incrementamos la */
/* cantidad de reglas q cubren los items q disparan la regla */
/* */
/*************************************************************************/
void TDMC45::AddRule(RuleNo r)
{
ItemNo i;
RuleIn[r] = true;
ForEach(i, 0, MaxItem)
{
if ( Match[r][i] )
{
Covered[i]++;
}
}
Verbosity(1)
{
FReglas<<r;
FReglas<<"+ ";
FReglas<<Value[r];
}
}
/*************************************************************************/
/* */
/* Eliminamos la regla r del conj de reglas incluídas y decrementamos */
/* la cant de reglas que cubren cada uno de los items cubiertos por */
/* la regla */
/* */
/*************************************************************************/
void TDMC45::DeleteRule(RuleNo r)
{
ItemNo i;
RuleIn[r] = false;
ForEach(i, 0, MaxItem)
{
if ( Match[r][i] )
{
Covered[i]--;
}
}
Verbosity(1)
{
FReglas<<r;
FReglas<<"- ";
FReglas<<-Value[r];
}
}
/*************************************************************************/
/* */
/* Crea un índice de reglas incluídas en RuleIndex. Selecciona primero */
/* aquellas clases cuyas reglas tienen el menor nro de falsos positivos */
/* Dentro de cada clase, pone las relgas con más aciertos primero */
/* */
/*************************************************************************/
void TDMC45::MakeIndex()
{
ClassNo c, BestC, Pass;
ForEach(i, 0, MaxItem)
{
Covered[i] = 0;
}
ForEach(Pass, 0, MaxClass)
{
ForEach(c, 0, MaxClass)
{
if ( Included[c] ) continue;
FalsePos[c] = 0;
ForEach(i, 0, MaxItem)
{
if ( Covered[i] || Class(Item[i]) == c ) continue;
ForEach(r, 1, NRules)
{
if ( Rule[r].Rhs == c && RuleIn[r] && Match[r][i] )
{
FalsePos[c]++;
break;
}
}
}
}
BestC = -1;
ForEach(c, 0, MaxClass)
{
if ( ! Included[c] &&
( BestC < 0 || FalsePos[c] < FalsePos[BestC] ) )
{
BestC = c;
}
}
Included[BestC] = true;
Verbosity(1)
{
FReglas<<FalsePos[BestC];
FReglas<<" ";
FReglas<<ClassName->Strings[BestC].c_str();
FReglas<<endl;
}
do
{
BestR = 0;
ForEach(r, 1, NRules)
{
if ( RuleIn[r] && Rule[r].Rhs == BestC &&
( ! BestR || Rule[r].Error < Rule[BestR].Error ) )
{
BestR = r;
}
}
if ( BestR )
{
RuleIndex[++NewNRules] = BestR;
RuleIn[BestR] = false;
ForEach(i, 0, MaxItem)
{
Covered[i] |= Match[BestR][i];
}
}
} while ( BestR );
}
NRules = NewNRules;
free(Included);
}
/*************************************************************************/
/* */
/* Determina la clase por defecto. Los empates se resuelven a favor */
/* de la clase más frecuente */
/* */
/*************************************************************************/
void TDMC45::FindDefault()
{
ClassNo c;
ItemNo i;
ForEach(c, 0, MaxClass)
{
NoRule[c] = 0;
}
ForEach(i, 0, MaxItem)
{
if ( ! Covered[i] )
{
NoRule[Class(Item[i])]++;
}
}
Verbosity(1)
{
FReglas<<endl;
FReglas<<"Items: No cubiertos Clase";
FReglas<<endl;
ForEach(c, 0, MaxClass)
{
FReglas<<ClassFreq[c];
FReglas<<" ";
FReglas<<NoRule[c];
FReglas<<" ";
FReglas<< ClassName->Strings[c].c_str();
FReglas<<endl;
}
FReglas<<endl;
}
DefaultClass = 0;
ForEach(c, 1, MaxClass)
{
if ( NoRule[c] > NoRule[DefaultClass] ||
NoRule[c] == NoRule[DefaultClass] &&
ClassFreq[c] > ClassFreq[DefaultClass] )
{
DefaultClass = c;
}
}
}
/*************************************************************************/
/* */
/* Dados una regla y un caso, determina la fuerza con la que podemos */
/* determinar que el caso pertenece a la clase especificada en la regla */
/* Si el caso no satisface todas las condiciones de la regla, entonces */
/* la fuerza vale 0. */
/* */
/*************************************************************************/
float TDMC45::Fuerza(PR ThisRule,Description Case)
{
short d;
ForEach(d, 1, ThisRule.Size)
{
if ( ! Satisfies(Case, ThisRule.Lhs[d]) )
{
return 0.0;
}
}
return ( 1 - ThisRule.Error );
}
/*************************************************************************/
/* */
/* Determina el nro de bits necesarios para codificar las excepciones */
/* */
/*************************************************************************/
float TDMC45::Biased(int N, int E, float ExpE)
{
float Rate;
Rate = ExpE / N;
return -E * Log(Rate) - (N-E) * Log(1-Rate);
}
/*************************************************************************/
/* */
/* Determina la longitud de codificación para todas las reglas */
/* */
/*************************************************************************/
void TDMC45::FindRuleCodes()
{
RuleNo r;
short d, NCond;
float Bits;
ForEach(r, 1, NRules)
{
NCond = Rule[r].Size;
Bits = 0;
ForEach(d, 1, NCond)
{
Bits += CondBits(Rule[r].Lhs[d]);
}
/*************************************************************************/
/* */
/* Determina el nro de bits requerido para codificar una condición */
/* */
/*************************************************************************/
float TDMC45::CondBits(Condition C)
{
Test t;
Attribute a;
t = C->CondTest;
a = t->Tested;
switch ( t->NodeType )
{
case BrDiscr: /* prueba de un atrib discreto */
case ThreshContin: /* prueba de un atrib continuo */
/*************************************************************************/
/* */
/* Guarda el conjunto de reglas en el archivo de reglas según el orden */
/* del índice */
/* */
/*************************************************************************/
void TDMC45::SaveRules()
{
short ri, d, v, Bytes;
RuleNo r;
Test Tst;
strcpy(Fn, FileName.c_str());
strcat(Fn, ".rules");
if ( ! ( TRf = fopen(Fn, "w") ) )
{FReglas<<"Error al abrir el archivo de reglas";}
if ( TRf ) fclose(TRf);
strcpy(Fn, FileName.c_str());
strcat(Fn, ".rules");
if ( ! ( TRf = fopen(Fn, "w") ) )
{FReglas<<"Error al abrir el archivo de reglas";}
StreamOut((char *) &NRules, sizeof(RuleNo));
StreamOut((char *) &DefaultClass, sizeof(ClassNo));
ForEach(ri, 1, NRules)
{
r = RuleIndex[ri];
StreamOut((char *) &Rule[r].Size, sizeof(short));
ForEach(d, 1, Rule[r].Size)
{
Tst = Rule[r].Lhs[d]->CondTest;
{
StreamOut((char *) Tst->Subset[v], Bytes);
}
}
StreamOut((char *) &Rule[r].Lhs[d]->TestValue, sizeof(short));
}
StreamOut((char *) &Rule[r].Rhs, sizeof(ClassNo));
StreamOut((char *) &Rule[r].Error, sizeof(float));
}
SaveDiscreteNames();
}
/*************************************************************************/
/* */
/* Obtiene un nuevo conj de reglas del archivo de reglas */
/* */
/*************************************************************************/
void TDMC45::GetRules()
{
RuleNo nr, r;
short n, d, v, Bytes;
Condition *Cond;
Test Tst;
ClassNo c;
float e;
if ( TRf ) fclose(TRf);
strcpy(Fn, FileName.c_str());
strcat(Fn, ".rules");
if ( ! ( TRf = fopen(Fn, "r") ) )
{FReglas<<"Error al abrir el archivo de reglas";}
ForEach(r, 1, nr)
{
StreamIn((char *) &n, sizeof(short));
Cond = (Condition *) calloc(n+1, sizeof(Condition));
ForEach(d, 1, n)
{
Tst = (Test) malloc(sizeof(struct TestRec));
Bytes = (MaxAttVal[Tst->Tested]>>3) + 1;
ForEach(v, 1, Tst->Forks)
{
Tst->Subset[v] = (Conjunto) malloc(Bytes);
StreamIn((char *) Tst->Subset[v], Bytes);
}
}
RecoverDiscreteNames();
}
/*************************************************************************/
/* */
/* Busca una prueba en el vector de pruebas, si no está allí, la agrega */
/* */
/*************************************************************************/
Test TDMC45::FindTest(Test Newtest)
{
static short TestSpace=0;
short i;
ForEach(i, 1, NTests)
{
if ( SameTest(Newtest, TestVec[i]) )
{
free(Newtest);
return TestVec[i];
}
}
NTests++;
if ( NTests >= TestSpace )
{
TestSpace += 1000;
if ( TestSpace > 1000 )
{
TestVec = (Test *) realloc(TestVec, TestSpace * sizeof(Test));
}
else
{
TestVec = (Test *) malloc(TestSpace * sizeof(Test));
}
}
TestVec[NTests] = Newtest;
return TestVec[NTests];
}
/*************************************************************************/
/* */
/* Verifica si la prueba t1 es igual a la prueba t2 */
/* */
/*************************************************************************/
bool TDMC45::SameTest(Test t1,Test t2)
{
short i;
if ( t1->NodeType != t2->NodeType ||
t1->Tested != t2->Tested )
{
return false;
}
switch ( t1->NodeType )
{
case BrDiscr: return true;
case ThreshContin: return t1->Cut == t2->Cut;
case BrSubset: ForEach(i, 1, t1->Forks)
{
if ( t1->Subset[i] != t2->Subset[i] )
{
return false;
}
}
}
return true;
}
/*************************************************************************/
/* */
/* Limpia las variables para un nuevo conj de reglas */
/* */
/*************************************************************************/
void TDMC45::InitialiseRules()
/* ---------------- */
{
NRules = 0;
Rule = 0;
RuleSpace = 0;
}
/*************************************************************************/
/* */
/* Agrega una nueva regla, al conjunto de reglas actual. Actualiza */
/* Rule[],NRules y,en caso de ser necesario,RuleSpace */
/* */
/*************************************************************************/
bool TDMC45::NewRule(Condition Cond[],short NConds,ClassNo TargetClass,float Err)
{
short d, r;
ForEach(r, 1, NRules)
{
if ( SameRule(r, Cond, NConds, TargetClass) )
{
Verbosity(1) printf("\tduplica la regla %d\n", r);
return false;
}
}
NRules++;
if ( NRules >= RuleSpace )
{
RuleSpace += 100;
if ( RuleSpace > 100 )
{
Rule = (PR *) realloc(Rule, RuleSpace * sizeof(PR));
}
else
{
Rule = (PR *) malloc(RuleSpace * sizeof(PR));
}
}
Rule[NRules].Size = NConds;
Rule[NRules].Lhs = (Condition *) calloc(NConds+1, sizeof(Condition));
ForEach(d, 1, NConds)
{
Rule[NRules].Lhs[d] = (Condition) malloc(sizeof(struct CondRec));
Rule[NRules].Lhs[d]->CondTest = Cond[d]->CondTest;
Rule[NRules].Lhs[d]->TestValue = Cond[d]->TestValue;
}
Rule[NRules].Rhs = TargetClass;
Rule[NRules].Error = Err;
Verbosity(1) PrintRule(NRules);
return true;
}
/*************************************************************************/
/* */
/* Decide si la regla actual duplica la regla r */
/* */
/*************************************************************************/
bool TDMC45::SameRule(RuleNo r,Condition Cond[],short NConds,ClassNo TargetClass)
{
short d, i;
Test SubTest1, SubTest2;
ForEach(d, 1, NConds)
{
if ( Rule[r].Lhs[d]->CondTest->NodeType != Cond[d]->CondTest->NodeType ||
Rule[r].Lhs[d]->CondTest->Tested != Cond[d]->CondTest->Tested )
{
return false;
}
switch ( Cond[d]->CondTest->NodeType )
{
case BrDiscr:
if ( Rule[r].Lhs[d]->TestValue != Cond[d]->TestValue )
{
return false;
}
break;
case ThreshContin:
if ( Rule[r].Lhs[d]->CondTest->Cut != Cond[d]->CondTest->Cut )
{
return false;
}
break;
case BrSubset:
SubTest1 = Rule[r].Lhs[d]->CondTest;
SubTest2 = Cond[d]->CondTest;
ForEach(i, 1, SubTest1->Forks)
{
if ( SubTest1->Subset[i] != SubTest2->Subset[i] )
{
return false;
}
}
}
}
return true;
}
/*************************************************************************/
/* */
/* Imprime el conjunto indexado de reglas actual */
/* */
/*************************************************************************/
void TDMC45::PrintIndexedRules()
{
short ri;
ForEach(ri, 1, NRules )
{
PrintRule(RuleIndex[ri]);
}
printf("\nClase por defecto: %s\n", ClassName[DefaultClass]);
}
/*************************************************************************/
/* */
/* Imprime la regla r */
/* */
/*************************************************************************/
void TDMC45::PrintRule(RuleNo r)
{
short d;
/*************************************************************************/
/* */
/* Imprime la condición c de una regla de producción */
/* */
/*************************************************************************/
void TDMC45::PrintCondition(Condition c)
{
Test tp;
DiscrValue v, pv, Last, Values=0;
Boolean First=true;
Attribute Att;
tp = c->CondTest;
v = c->TestValue;
Att = tp->Tested;
printf("\t%s", AttName[Att]);
if ( v < 0 )
{
printf(" es desconocido\n");
return;
}
switch ( tp->NodeType )
{
case BrDiscr:
printf(" = %s\n", AttValName[Att][v]);
break;
case ThreshContin:
printf(" %s %g\n", ( v == 1 ? "<=" : ">" ), tp->Cut);
break;
case BrSubset:
/* Contamos los valores en la rama */
if ( Values == 1 )
{
printf(" = %s\n", AttValName[Att][Last]);
break;
}
printf(" en ");
ForEach(pv, 1, MaxAttVal[Att])
{
if ( In(pv, tp->Subset[v]) )
{
if ( First )
{
printf("{");
First = false;
}
else
{
printf(", ");
}
printf("%s", AttValName[Att][pv]);
}
}
printf("}\n");
}
}
/*************************************************************************/
/* */
/* Poda la regla dadas las condiciones Cond, y el nro de condiciones */
/* NCond, y agrega la regla resultante al actual conjunto de reglas */
/* si es lo suficientemente precisa */
/* */
/*************************************************************************/
void TDMC45::PruneRule(Condition Cond[],short NCond,ClassNo TargetClass)
{
short d, dd, id, Bestd, Bestid, Remaining=NCond;
float DefaultError, Extra;
Boolean Alter;
Condition Hold;
ItemNo i;
ForEach(d, 0, NCond)
{
Deleted[d] = false;
}
TargetClassFreq[0] = TargetClassFreq[1] = 0;
ForEach(i, 0, MaxItem)
{
ForEach(d, 1, NCond)
{
CondSatisfiedBy[d][i] = Satisfies(Item[i], Cond[d]);
}
TargetClassFreq[IsTarget(Item[i])]++;
}
Verbosity(1)
{
FReglas<<endl;
FReglas<<"Poda de la regla para ";
FReglas<<ClassName->Strings[TargetClass].c_str();
}
do
{
Alter = false;
FindTables(NCond, TargetClass);
Bestd = id = 0;
Verbosity(1)
printf("\n Err Utilizado Pesim\tCondición ausente\n");
ForEach(d, 0, NCond)
{
if ( Deleted[d] ) continue;
if ( Total[d] )
{
Actual[d] = Errors[d] / (float) Total[d];
Extra = AddErrs((float) Total[d], (float) Errors[d]);
Pessimistic[d] = (Errors[d] + Extra) / Total[d];
}
else
{
Actual[d] = 0;
Pessimistic[d] = DefaultError;
}
Verbosity(1)
{
FReglas<<" ";
FReglas<<Errors[d];
FReglas<<" ";
FReglas<<Total[d];
FReglas<<100 * Pessimistic[d];
}
if ( ! d )
{
Verbosity(1)
{
FReglas<<" <regla base>";
FReglas<<endl;
}
}
else
{
id++;
if ( SIGTEST )
{
CondSigLevel[d] =
TableProb(Errors[0],
Errors[d]-Errors[0],
Total[0]-Errors[0],
Total[d]-Total[0]-Errors[d]+Errors[0]);
Verbosity(1)
{
FReglas<<" Sig=";
FReglas<<CondSigLevel[d];
}
}
Verbosity(1) PrintCondition(Cond[d]);
if ( Alter )
{
Verbosity(1)
{
FReglas<<" Eliminamos la prueba ";
FReglas<<Bestid;
FReglas<<endl;
}
Deleted[Bestd] = true;
Remaining--;
}
if ( ! Remaining || ! Total[0] )
{
return;
}
return;
}
ForEach(d, 1, Remaining)
{
dd = 0;
ForEach(id, d, NCond)
{
if ( ! Deleted[id] &&
( ! dd ||
Before(Cond[id]->CondTest, Cond[dd]->CondTest) ) )
{
dd = id;
}
}
if ( dd != d )
{
Hold = Cond[d];
Cond[d] = Cond[dd];
Cond[dd] = Hold;
Deleted[dd] = Deleted[d];
}
Deleted[d] = true;
}
/*************************************************************************/
/* */
/* Verifica si la condición R es redundante */
/* */
/*************************************************************************/
bool TDMC45::Redundant(short R,Condition Cond[],short NCond)
{
short d, v, vv;
Test t, Rt;
Rt = Cond[R]->CondTest;
v = Cond[R]->TestValue;
ForEach(d, 1, NCond)
{
if ( Deleted[d] || d == R ) continue;
t = Cond[d]->CondTest;
vv = Cond[d]->TestValue;
switch ( t->NodeType )
{
case BrDiscr: /* prueba sobre un atrib discreto */
return false;
if ( vv == v &&
( v == 1 ? t->Cut < Rt->Cut : t->Cut > Rt->Cut ) )
{
return true;
break;
return false;
}
/*************************************************************************/
/* */
/* Decide si el subconj de valores S1 está contenido en S2 */
/* */
/*************************************************************************/
bool TDMC45::IsSubset(Conjunto S1,Conjunto S2,Attribute Att)
{
DiscrValue v;
ForEach(v, 1, MaxAttVal[Att])
{
if ( In(v, S1) && ! In(v, S2) ) return false;
}
return true;
}
/*************************************************************************/
/* */
/* Busca las tablas de distribución de frecuencias para la condiciones */
/* actuales: */
/* */
/* Total[0] = items q satisfacen todas las condiciones */
/* Total[d] = items q satisfacen todas las condiciones menos d */
/* */
/* Errors[0] = items de clase errónea q satisfacen todas las cond */
/* Errors[d] = items de clase errónea q satisfacen todas las cond */
/* menos d */
/* */
/*************************************************************************/
void TDMC45::FindTables(short NCond,ClassNo TargetClass)
{
ItemNo i;
short Misses, Missed[2], d;
Boolean CorrectClass;
ForEach(d, 0, NCond)
{
Total[d] = Errors[d] = 0;
}
ForEach(i, 0, MaxItem)
{
Misses = 0;
CorrectClass = IsTarget(Item[i]);
if ( ! Misses )
{
UpdateCount(Total, Errors, 0, CorrectClass);
}
else
if ( Misses == 1 )
{
UpdateCount(Total, Errors, Missed[0], CorrectClass);
}
}
ForEach(d, 1, NCond)
{
if ( ! Deleted[d] )
{
Total[d] += Total[0];
Errors[d] += Errors[0];
}
}
}
/*************************************************************************/
/* */
/* Incrementa los valores de Total[d] y Errors[d] */
/* */
/*************************************************************************/
void TDMC45::UpdateCount(ItemNo T[],ItemNo E[],short d,bool OK)
{
T[d]++;
if ( ! OK ) E[d]++;
}
/*************************************************************************/
/* */
/* Determina si la descripción de un caso satisface la cond dada */
/* */
/*************************************************************************/
bool TDMC45::Satisfies(Description CaseDesc,Condition OneCond)
{
DiscrValue v;
float cv;
Test t;
short s;
bool Outcome;
t = OneCond->CondTest;
switch ( t->NodeType )
{
case BrDiscr: /* prueba sobre un atrib discreto */
v = DVal(CaseDesc, t->Tested);
Outcome = ( v == 0 ? -1 : v );
break;
cv = CVal(CaseDesc, t->Tested);
Outcome = ( cv == Unknown ? -1 : cv <= t->Cut ? 1 : 2 );
break;
v = DVal(CaseDesc, t->Tested);
Outcome = -1;
ForEach(s, 1, t->Forks)
{
if ( In(v, t->Subset[s]) )
{
Outcome = s;
break;
}
}
/*************************************************************************/
/* */
/* Distribución hipergeometrica (usa los log de los factoriales */
/* tabulados) */
/* */
/*************************************************************************/
double TDMC45::Hypergeom(int a,int r,int A,int B)
{
return exp( LogFact[A] + LogFact[B] + LogFact[r] + LogFact[A+B-r] -
( LogFact[a] + LogFact[r-a] + LogFact[A-a]
+ LogFact[B-(r-a)] + LogFact[A+B]) );
}
/*************************************************************************/
/* */
/* TableProb examina la tabla t de contingencias de 2x2 y calcula la */
/* probabilidad de que una división al azar pueda producir una división */
/* al menos tan extrema como esta. Esto también se conoce como la */
/* "Prueba Exacta de Fisher" */
/* */
/*************************************************************************/
float TDMC45::TableProb(int t11,int t12,int t21,int t22)
{
double Sum=0.0;
int A, B, r, a, k, a0;
k = Min(r, A);
ForEach(a, a0, k)
{
Sum += Hypergeom(a, r, A, B);
}
return Sum;
}
/*************************************************************************/
/* */
/* Evalúa todos los conj de reglas */
/* */
/*************************************************************************/
void TDMC45::EvaluateRulesets(bool DeleteRules)
{
short t;
ItemNo *Errors;
float AvSize=0, AvErrs=0;
Boolean Final;
if ( TRIALS == 1 )
{
/* Evaluamos el conj de reglas actual como si no existiera un conj
compuesto de reglas */
ForEach(t, 0, TRIALS)
{
NRules = PRSet[t].SNRules;
Rule = PRSet[t].SRule;
RuleIndex = PRSet[t].SRuleIndex;
DefaultClass = PRSet[t].SDefaultClass;
if ( t < TRIALS )
{
FReglas<<endl;
FReglas<<"Conjunto de reglas ";
FReglas<<t;
FReglas<<endl;
}
else
{
FReglas<<endl;
FReglas<<"Conjunto compuesto de reglas:";
FReglas<<endl;
}
Final = (t == TRIALS);
Errors[t] = Interpret(0, MaxItem, DeleteRules, Final, Final);
AvSize += NRules;
AvErrs += Errors[t];
if ( DeleteRules )
{
PRSet[t].SNRules = NRules;
}
}
FReglas<<endl;
FReglas<<"Prueba Tamaño Errores";
FReglas<<endl;
FReglas<<"------ ------ -------";
FReglas<<endl;
ForEach(t, 0, TRIALS)
{
if ( t < TRIALS )
{
FReglas<<t;
}
else
{
FReglas<<" **";
}
FReglas<<" ";
FReglas<<PRSet[t].SNRules;
FReglas<<" ";
FReglas<<Errors[t];
FReglas<<"(";
FReglas<<100 * Errors[t] / (MaxItem+1.0);
FReglas<<"%)";
FReglas<<endl;
}
AvSize /= TRIALS + 1;
AvErrs /= TRIALS + 1;
FReglas<<" Tamaño Promedio = ";
FReglas<<AvSize;
FReglas<<", errores promedio = ";
FReglas<<AvErrs;
FReglas<<" (";
FReglas<< 100 * AvErrs / (MaxItem+1.0);
FReglas<<"%)";
FReglas<<endl;
}
/*************************************************************************/
/* */
/* Evalúa el conjunto de reglas actual */
/* */
/*************************************************************************/
ItemNo TDMC45::Interpret(ItemNo Fp,ItemNo Lp,bool DeleteRules, bool CMInfo, bool Arrow)
{
ItemNo i, Tested=0, Errors=0, *Better, *Worse, *ConfusionMat;
bool FoundRule;
ClassNo AssignedClass, AltClass;
Attribute Att;
RuleNo p, Bestr, ri, ri2, riDrop=0;
float ErrorRate, BestRuleConfidence;
if ( CMInfo )
{
ConfusionMat = (ItemNo *) calloc((MaxClass+1)*(MaxClass+1), sizeof(ItemNo));
}
ForEach(ri, 1, NRules)
{
p = RuleIndex[ri];
Rule[p].Used = Rule[p].Incorrect = 0;
}
ri = BestRuleIndex(Item[i], 1);
Bestr = ( ri ? RuleIndex[ri] : 0 );
FoundRule = Bestr > 0;
if ( FoundRule )
{
Rule[Bestr].Used++;
AssignedClass = Rule[Bestr].Rhs;
BestRuleConfidence = Confidence;
else
{
AssignedClass = DefaultClass;
}
if ( CMInfo )
{
ConfusionMat[Class(Item[i])*(MaxClass+1)+AssignedClass]++;
}
Tested++;
if ( AssignedClass != Class(Item[i]) )
{
Errors++;
if ( FoundRule ) Rule[Bestr].Incorrect++;
Verbosity(3)
{
FReglas<<endl;
ForEach(Att, 0, MaxAtt)
{
FReglas<<" ";
FReglas <<AttName->Strings[Att].c_str();
FReglas<<": ";
if ( MaxAttVal[Att] )
{
if ( DVal(Item[i],Att) )
{
FReglas<<AttValName[Att][DVal(Item[i],Att)].c_str();
FReglas<<endl;
}
else
{
FReglas<<"?";
FReglas<<endl;
}
}
else
{
if ( CVal(Item[i],Att) != Unknown )
{
FReglas<<CVal(Item[i],Att);
FReglas<<endl;
}
else
{
FReglas<<"?";
FReglas<<endl;
}
}
}
FReglas<<" ";
FReglas<<i;
FReglas<<": Clase dada";
FReglas<<ClassName->Strings[Class(Item[i])].c_str();
if ( FoundRule )
{
FReglas<<" La regla ";
FReglas<<Bestr;
FReglas<<" [";
FReglas<<100 * BestRuleConfidence;
FReglas<<"%] determina la clase";
}
else
{
FReglas<<" Clase por defecto ";
}
FReglas<<ClassName->Strings[AssignedClass].c_str();
FReglas<<endl;
}
}
}
FReglas<<endl;
FReglas<<"Regla Tamaño Error Usada Errores Ventaja";
FReglas<<endl;
FReglas<<"----- ------ ----- ----- ------- -------";
FReglas<<endl;
ForEach(ri, 1, NRules)
{
p = RuleIndex[ri];
if ( Rule[p].Used > 0 )
{
ErrorRate = Rule[p].Incorrect / (float) Rule[p].Used;
FReglas<<p;
FReglas<<" ";
FReglas<<Rule[p].Size;
FReglas<<" ";
FReglas<<100 * Rule[p].Error;
FReglas<<"% ";
FReglas<<Rule[p].Used;
FReglas<<" ";
FReglas<< Rule[p].Incorrect;
FReglas<<" (";
FReglas<<100 * ErrorRate;
FReglas<<"%) ";
FReglas<<Better[ri]-Worse[ri];
FReglas<<" (";
FReglas<<Better[ri];
FReglas<<"|";
FReglas<<Worse[ri];
FReglas<<") ";
FReglas<<ClassName->Strings[Rule[p].Rhs].c_str();
FReglas<<endl;
free(Better);
free(Worse);
if ( riDrop )
{
FReglas<<endl;
FReglas<<"Eliminamos la regla ";
FReglas<<RuleIndex[riDrop];
FReglas<<endl;
if ( CMInfo ) free(ConfusionMat);
return Interpret(Fp, Lp, DeleteRules, true, Arrow);
}
else
{
FReglas<<endl;
FReglas<<"Probadas ";
FReglas<<Tested;
FReglas<<", errores ";
FReglas<<Errors;
FReglas<<" (";
FReglas<<100 * Errors / (float) Tested;
FReglas<<"%)";
FReglas<<( Arrow ? " <<" : "" );
FReglas<<endl;
}
if ( CMInfo )
{
PrintConfusionMatrix(ConfusionMat);
free(ConfusionMat);
}
return Errors;
}
/*************************************************************************/
/* */
/* Busca la mejor regla para el caso dado, dejando la probabilidad */
/* en Confidence */
/* */
/*************************************************************************/
RuleNo TDMC45::BestRuleIndex(Description CaseDesc,RuleNo Start)
{
RuleNo r, ri;
Confidence = 0.0;
return 0;
}
D.5.1. UTipos
D.5.1.1. UTipos.h
//---------------------------------------------------------------------------
#ifndef UTiposH
#define UTiposH
//---------------------------------------------------------------------------
//Definición de constantes
#define LONG_CLASSIFIER 3 /*Longitud del campo del clasificador*/
#define MAX_CANT_DESCRIPTORES 20 /*Cantidad máxima de descriptores que puede
haber en la tabla*/
#define MAX_CANT_CLASIFICADORES 10 /*Cantidad máxima de valores distintos que
puede haber para el clasificador*/
#define MAX_HIJOS 20 /*Máxima de cantidad de hijos que puede tener un nodo del
árbol*/
#define MAX_NIVELES 200
#define MAX_CHAR 100
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// DECLARACION DE TIPOS
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Tabla de correlación
//*****************EL TAMAÑO DE LA TABLA DEBERÍA SER DINÁMICO
typedef double TTablaCorrel[MAX_CANT_DESCRIPTORES+1][MAX_CANT_CLASIFICADORES+1];
struct tree_node {
short int flag_hoja; /* 0 si es hoja y todos los valores son "Yes"
1 si es hoja y todos los valores son "No"
2 si es hoja y sus valores son mixtos
3 si no es hoja*/
int desc;
class TParametros {
public:
AnsiString ArchivoIni;
AnsiString NombreBD;
AnsiString TablaBD;
AnsiString TablaReglas;
AnsiString ColClasificador;
AnsiString ExtensionFileReglas;
TStringList *LClasificadores;
int iFormatoReglas; /* 0: Insight2+
1: KappaPC
2: ambos
3: ninguno*/
int iSistema; /* 0: ID3
1: C4.5 */
int iCriterio; /* 0: Ganancia
1: Proporción de ganancia */
int iPoda; /* 0: Si
1: No */
int iConjuntos; /* indica si las pruebas en el C4.5 se
realizan sobre grupos de atrib discretos
0: Si
1: No */
int iMinObjs; /* Cantidad mínima de objetos que debe tener
una rama en el C4.5 */
int iCF; /* Nivel de confianza para la poda en el
C4.5 */
//---------------------------------------------------------------------------
#endif
D.5.1.2. UTipos.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "UTipos.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// IMPLEMENTACIÓN DE LA CLASE TParametros
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
ArchivoIni=ArchIni;
iConjuntos=StrToInt(Trim(Buffer));
//---------------------------------------------------------------------------
TParametros::~TParametros() //destructor
{
ArchivoIni.~AnsiString();
NombreBD.~AnsiString();
TablaBD.~AnsiString();
TablaReglas.~AnsiString();
ColClasificador.~AnsiString();
ExtensionFileReglas.~AnsiString();
delete LClasificadores;
}
//---------------------------------------------------------------------------
int TParametros::RefrescarParams()
//Refresca los parámetros a partir del archivo .ini
/*Devuelve 0 si no hubo problemas,
-1 en caso contrario*/
{
int i;
char Buffer[80];
try {
LClasificadores = new TStringList();
return(0);
}
catch(...) {
return(-1);
}
}
//---------------------------------------------------------------------------
int TParametros::GrabarParams()
//Guardar los parámetros en el .ini
/*Devuelve 0 si no hubo problemas,
-1 en caso contrario*/
{
AnsiString lista;
int index;
try {
//Clasificadores
lista= "";
index=0;
while (index<LClasificadores->Count)
{
AppendStr(lista, LClasificadores->Strings[index]);
AppendStr(lista, ", ");
index++;
}
//Sistema de resolución
lista=IntToStr(iSistema);
WritePrivateProfileString("Resolucion", "Sistema", lista.c_str(),
ArchivoIni.c_str());
lista=IntToStr(iCriterio);
WritePrivateProfileString("Resolucion", "Criterio", lista.c_str(),
ArchivoIni.c_str());
lista=IntToStr(iPoda);
WritePrivateProfileString("Resolucion", "Poda", lista.c_str(),
ArchivoIni.c_str());
//C4.5
lista=IntToStr(iConjuntos);
WritePrivateProfileString("C45", "Conjuntos", lista.c_str(),
ArchivoIni.c_str());
lista=IntToStr(iMinObjs);
WritePrivateProfileString("C45", "MinObjs", lista.c_str(), ArchivoIni.c_str());
lista=IntToStr(iCF);
WritePrivateProfileString("C45", "CF", lista.c_str(), ArchivoIni.c_str());
lista.~AnsiString();
return(0);
}
catch(...) {
lista.~AnsiString();
return(-1);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// FIN DE LA CLASE TParametros
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
D.5.2. Types.h
/*************************************************************************/
/* */
/* Definiciones de tipos para C4.5 */
/* ------------------------------- */
/* */
/*************************************************************************/
struct TestRec
{
short NodeType; /* tipo de prueba */
Attribute Tested; /* atributo testeado */
short Forks; /* ramas posibles */
float Cut; /* valor límite (si es relevante) */
Conjunto *Subset; /* subconjunto (si es relevante) */
};
struct CondRec
{
Test CondTest; /* antecedente de la prueba */
short TestValue; /* resultado de la prueba */
};
struct ProdRuleRec
{
short Size; /* nro de condiciones */
Condition *Lhs; /* condiciones */
ClassNo Rhs; /* clase resultante de la regla */
float Error, /* proporción estimada de error */
Bits; /* bits para codificar la regla */
ItemNo Used, /* cant de veces en q se uso la regla */
Incorrect; /* cant de veces en q la regla fue
incorrecta */
};
struct RuleSetRec
{
PR *SRule; /* reglas */
RuleNo SNRules, /* cant de reglas */
D.5.3. Defns.h
/*************************************************************************/
/* */
/* Definiciones utilizadas en el C4.5 */
/* ---------------------------------- */
/* */
/*************************************************************************/
#include <stdio.h>
#include <math.h>
#define None -1
#define Epsilon 1E-3
long random();
#define Random ((random()&2147483647) / 2147483648.0)
D.5.4. Rulex.h
/*************************************************************************/
/* */
/* Datos globales para la construcción y aplicación de las reglas */
/* -------------------------------------------------------------- */
/* */
/*************************************************************************/
poda de reglas */
SIMANNEAL; /* usar simulated annealing */
REFERENCIAS
[Babic et al, 1998] Babic, A., Mathiesen, U., Hedin, K., Bodemar, G., Wigertz, O. 1998.
Assessing an AI Knowledge-Base for Asymptomatic Liver Diseases.
Department of Medical Informatics, Department of Internal Diseases,
Department of Infectious Diseases, Linköping University Hospital,
Suecia. Faculty of Electrical and Computer Engineering, University of
Ljubljana, Eslovenia. Department of Internal Diseases, Oskarshamn
County Hospital, Suecia.
[Baldwin et al, 2000] Baldwin, J.F., Lawry, J., Martin, T.P. 2000 Mass Assignment
Induction of Decision Trees on Words. A.I. Group, Departament of
Engineering Mathematics, University of Bristol, Reino Unido,
{jim.baldwin, j.lawry, trevor.martin}@bristol.ac.uk
[Bergadano et al, 1992] Bergadano, F., Matwin, S. Michalski, R. S., Zhang, J. (1992)
Learning Two-TieredDescriptions of flexible Concepts: the
POSEIDON System. En Machine Learning, Volumen 8, páginas 5-43,
DBLP, www.dblp.uni-tier.de, Dinamarca.
[Blockeel y De Raedt, 1997] Blockeel, H., De Raedt, L., 1997 Top-Down Induction of Logical
Decision Trees. Katholieke Universiteit Leuven, Departament of
Computer Science, Celestijnelaan, Bélgica
[Blum, Langley, 1997] Blum, A., Langley, P. 1997 Selection of Relevant Features and
Examples in Machine Learning. School of Computer Science,
Carnegie Mellon University, Pittisburgh, Pennsylvania, Institute for
the Study of Learning and Expertise, Palo Alto, California, EE.UU.
[Blurock, 1996] Edward S. Blurock, 1996 The ID3 Algorithm, Research Institute for
Symbolic Computation, www.risc.uni-
linz.ac.at/people/bulrock/ANALYSIS/manual/document, Austria
[Cabena et al, 2000] Cabena, P., Choi, H.H., Kim, S., Otsuka, S., Reinschmidt, J.,
Saarenvirta, G. 2000. Intelligent Miner for Data Applications Guide,
International Technical Support Organization, IBM,
http://www.redbooks.ibm.com
[Callahan, B., Coombs, 1998] Callahan, B., Coombs, J. 1998 Training Decision Trees with ID3,
http://www.css.tayloru.edu/~jcoombs/proj/ml/id3.htm
[Chen, 1994] Chen, H. 1994. Machine Learning for Information Retrieval: Neural
Networks, Symbolic Learning, and Genetic Algorithms. JASIS,
http://ai.bpa.arizona.edu/papers/mlir93/mlir93.html
[Davidsson, 1995] Davidsson, P. 1995. ID3-SD: An Algortithm for Learning
Characteristic Decision Trees by Controlling the Degree of
Generalization. Departament of Computer Science, Lund University,
Suecia
[DeJong, Mooney, 1986] DeJong, G.F., Mooney, R.J. 1986. Explanation-Based Learning. An
Alternative View, en Machine Learning, Volumen 1, páginas 145-176.
Kluwer Academic Publishing
[Elomaa, 1993] Elomaa, T. 1993. In Defense of C4.5: Notes on Learning One-Level
Decision Trees. Departament of Computer Science, University of
Helsinki, Finlandia
[Espasa-Calpe, 1974] 1974 Diccionario Enciclopédico Abreviado. Espasa-Calpe S.A.,
Madrid. Tomo I, Séptima Edición, España.
[Fayad et al, 1996] Fayad, U. M., Piatetsky-Shapiro, G., Smyth, P., Uhturudsamy, R.
(eds). 1996 Advances in Knowledge Discovery and Data Mining, San
Mateo, AAAI Press, EE.UU.
[Fjara, 2000] Fjara, 2000. A Decision Tree Algorithm.
www.cs.uml.edu/~fjara/mineset/id3/id3_example/id3_algoritm.html
[Frank y Witten, 1999] Frank, E. , Witten, I.H. 1999. Making Better Use of Global
Discretization, Proceedings 16th International Conference on Machine
Learning, páginas 115-123. Departament of Computer Science,
University of Waikato, Nueva Zelanda
[Gallion et al, 1993] Gallion, R., St Clair, D., Sabharwal, C., Bond, W.E. 1993. Dynamic
ID3: A Symbolic Learning Algorithm for Many-Valued Attribute
Morgan-Kauffman, EE.UU.
[Quinlan, 1996a] Quinlan, J.R. 1996. Improved Use of Continuous Attributes in C4.5.
Basser Departament of Computer Science, University of Science,
Australia.
[Quinlan, 1996b] Quinlan, J.R.1996. Learning First-Order Definitions of Functions.
Basser Departament of Computer Science, University of Science,
Australia
[Riddle, 1997] Riddle, P.J. 1997. ID3 Algorithm.
www.cs.auckland.ac.nz/~pat/706_99/ln/node75.html, Nueva Zelanda
[Rissanen, 1983] Rissanen, J. 1983. A universal prior for integers and estimation by
minimum description length. En Annals of Statistics 11, Vol 2, p. 416-
431
[S/A, 19950] S/A. 1995. Building Classification Models: ID3 and C4.5,
yoda.cis.temple.edu:8080/UGAIWWW/lectures/C45, Pensilvania,
EE.UU.
[S/A, 1998] S/A 1998.Confidence intervals for small sample sizes. En Engineering
Statistics Handbook, Information Technology Laboratory, NIST,
http://www.itl.nist.gov/div898/handbook/prc/section2/prc242.htm,
EE.UU.
[S/A, 1999] S/A. 1999. What is Data Mining?,
www.citeseer.nj.nec.com/69212.html.
[Thakore, 1993] Thakore, M., St Clair, D. 1993. Effect of the X2 test on the
Construction of ID3 decision trees, Sun Microsystems, University of
MO-Rolla, Engineering Education Center, St. Louis, EE.UU.
[Thrun et al, 1991] Thrun, S., Bala, J., Bratko, I., Cestnik, B., Cheng, J., De Jong, K.,
Dzeroski, S., Fahlman, S.E., Fisher, D., Hamann, R., Kaufman, K.,
Keller, S., Kononenko, I., Michalski, R.S., Mitchell, T., Pachowicz,
P., Reich, Y., Vafaie, H., Van de Welde, W., Wenzel, W., Wnek, J,
Zhang, J. 1991 The MONK’s Problems. A Performance Comparison
of Different Learning Algorithms, Carnegie Mellon University,
Pittisburgh, EE.UU.
[Thrun et al, 1998] Thrun, S., Faloustos, C., Mitchell, T., Wasserman, L. 1998 Automated
Learning and Discovery: State-Of-The-Art and Research Topics in a
Rapidly Growing Field. CMU-CALD-98-100, Center for Automated
Learning and Discovery, Carnegie Mellon University, Pittisburgh,
EE.UU.
[Witten y Frank, 2000] Witten, I.H., Frank, E. 2000. Data Mining: Practical Machine
Learning Tools and Techniques with Java Implementations. Morgan
Kaufmann, San Diego, EE.UU.