Codificación de variables categóricas en aprendizaje automático
Abstract
Las variables categóricas aparecen con gran frecuencia en los conjuntos de datos de problemas reales. Sin embargo, debido a su naturaleza, no pueden ser tratadas directamente por un número significativo de técnicas de aprendizaje automático. Para afrontar esta situación y poder sacar provecho de la información que estas variables condensan, se van a estudiar diversas técnicas de codificación que permiten transformarlas en otras variables continuas que sí son más aptas para ser empleadas en la resolución de problemas. De esta manera, el propósito de este trabajo consiste en el análisis y comparación de dichas codificaciones.
Full text
Proyecto Fin de Carrera Ingeniería de Telecomunicación Formato de Publicación de la Escuela Técnica Superior de Ingeniería Autor: F. Javier Payán Somet Tutor: Juan José Murillo Fuentes Dep. Teoría de la Señal y Comunicaciones Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2013 Trabajo Fin de Máster Máster en Ingeniería Industrial Codificación de variables categóricas en aprendizaje automático Autor: Adrián Rocha Íñigo Tutores: Amparo Núñez Reyes Fernando Pavón Pérez Dpto. de Ingeniería de Sistemas y Automática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2020
Trabajo Fin de Máster Máster en Ingeniería Industrial Codificación de variables categóricas en aprendizaje automático Autor: Adrián Rocha Íñigo Tutores: Amparo Núñez Reyes Profesora Titular Fernando Pavón Pérez CEO Gamco S.L Dpto. de Ingeniería de Sistemas y Automática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2020
Trabajo Fin de Máster: Codificación de variables categóricas en aprendizaje automático Autor: Adrián Rocha Íñigo Tutores: Amparo Núñez Reyes Fernando Pavón Pérez El tribunal nombrado para juzgar el trabajo arriba indicado, compuesto por los siguientes profesores: Presidente: Vocal/es: Secretario: acuerdan otorgarle la calificación de: El Secretario del Tribunal Fecha:
Agradecimientos Es te trabajo ha sido posible gracias al proyecto SEKAS – Arquitectura de conocimiento con auto-aprendizaje y ajuste autónomo para aplicaciones de seguridad del Ministerio de Ciencia, Innovación y Universidades (Nºdossier: RTC-2017-6178-8). En primer lugar, me gustaría dar las gracias a todos los buenos profesores por cuyas manos he pasado. Su dedicación, entrega y amor por la enseñanza conforman la base de este trabajo y mis estudios en ingeniería. En especial, le agradezco a Amparo Núñez la confianza depositada en mí para llevar a cabo esta tarea, y la ayuda que siempre me ha proporcionado cuando la he necesitado. También a Fernando Pavón y Adrián García, quienes, con infinita paciencia, me brindaron su valioso tiempo y conocimientos para ayudarme a vislumbrar los primeros trazos de este inmenso mundo de la inteligencia artificial. Por último, y más importante, gracias a mis padres, por hacerme la persona que soy hoy, por enseñarme a aspirar cada vez más alto y, porque sin sus esfuerzos, nada de esto sería posible. A mi hermana, por ser el mejor ejemplo a seguir. Y a mi pareja, porque su apoyo incondicional es un pilar fundamental. Adrián Rocha Íñigo Máster en Ingeniería Industrial Sevilla, 2020 V
Resumen La s variables categóricas aparecen con gran frecuencia en los conjuntos de datos de problemas reales. Sin embargo, debido a su naturaleza, no pueden ser tratadas directamente por un número significativo de técnicas de aprendizaje automático. Para afrontar esta situación y poder sacar provecho de la información que estas variables condensan, se van a estudiar diversas técnicas de codificación que permiten transformarlas en otras variables continuas que sí son más aptas para ser empleadas en la resolución de problemas. De esta manera, el propósito de este trabajo consiste en el análisis y comparación de dichas codificaciones. VII
Índice de Tablas 1.1 Ejemplo ficticio para la codificación de la variable clase social 4 1.2 Ejemplo ficticio para la codificación de la variable día de la semana 4 2.1 Ejemplo de codificación one-hot 12 2.2 Ejemplo de codificación one-cold 13 2.3 Ejemplo de codificación rank-hot 14 2.4 Ejemplo de codificación binaria 15 2.5 Correspondencia entre código binario y Gray 16 2.6 Ejemplo de codificación WoE 25 2.7 Ejemplo de codificación por similitud 27 4.1 Matriz de confusión 37 4.2 Resultados de las codificaciones en CensusIncome 39 4.3 Resultados de las codificaciones en la predicción del volumen de llamadas 44 XV
Notación sim Similitud dist Distancia RConjunto de los números reales NConjunto de los número naturales DConjunto de datos dNúmero de muestras en el conjunto de datos XkVector de características de la muestra k-ésima Xk iElemento i-ésimo del vector de características para la muestra k-ésima mTamaño del vector de características Xi Conjunto de posibles valores para el elemento i-ésimo del vector de características piTamaño del conjunto de posibles valores de la variable i-ésima zxiElemento genérico z-ésimo del conjunto Xi YkVector de salida de la muestra k-ésima Yk jElemento j-ésimo del vector de características para la muestra k-ésima ˆ YkValor predicho de salida de la muestra k-esima nTamaño del vector de salida YjConjunto de posibles valores para el elemento j-ésimo del vector de salida qjTamaño del conjunto de posibles valores de la variable de salida j-ésima syjElemento genérico del conjunto Yj ΦAplicación de codificación de variables categóricas zΦiVector de codificación de la categoría genérica zxi aiTamaño del vector de codificación de la categoría genérica zxi zφi,tElemento t-ésimo del vector zΦi →Correspondencia funcional eNúmero e :Tal que |Tal que ∀Para todo ∈Pertenece ∪Unión de conjuntos ∩Intersección de conjuntos ≤Menor o igual XVII
XVIII Notación ≥Mayor o igual =Igual 6=Diferente ∑b aSumatorio de aab minimo{A}Valor mínimo del conjunto A argmax{A}Argumento que maximiza la expresión A loga(b)Logaritmo en base ade b lnaLogaritmo neperiano de a P(A)Probabilidad de A P(A|B)Probabilidad condicional de Adado B faFrecuencia absoluta del suceso a E(A)Esperanza matemática de A E(A|B)Esperanza condicional de Adado B µMedia aritmética σDesviación típica δab Delta de Kronecker entre las variables ayb δc ab Vector de dimensión cde deltas de Kronecker Λ() Función de cálculo de pesos de ponderación syφValor medio de codificación para la salida s-ésima θVector con todas las variables de codificación WoE(H;E)Peso de la evidencia a favor de Hdado E sim(a,b)Similitud entre las categorías ayb d(a,b)Distancia entre las observaciones ayb KConjunto de observaciones que conforman la vecindad kNúmero de vecinos que conforman la vecindad VN Número de verdaderos negativos VP Número de verdaderos positivos FN Número de falsos negativos FP Número de falsos positivos SEN Sensibilidad PFA Porcentaje de falsas alarmas EFF Eficiencia MAPE Error porcentual medio absoluto
1 Introducción En los últimos años el término Inteligencia Artificial ha cobrado cada vez más relevancia y casi se ha convertido en una constante en nuestro día a día. Si bien es cierto que este hecho puede ser debido a algunas campañas de marketing que publicitan productos que aparentemente integran dicha tecnología con el objetivo de atraer al consumidor, no podemos dudar que este campo perteneciente a las Ciencias de la Computación ha progresado y evolucionado vertiginosamente desde sus inicios hasta la actualidad, otorgando en numerosas situaciones sorprendentes resultados previamente inalcanzables. La primera vez que se empleó el término IA fue en 1956 durante la Conferencia de Dartmouth por John McCarthy, quien la define como: "It is the science and engineering of making intelligent machines, especially intelligent computer programs. It is related to the similar task of using computers to understand human intelligence, but AI does not have to confine itself to methods that are biologically observable." [1] No obstante, las investigaciones en esta línea ya habían comenzado antes, como el caso de Warren McCulloch y Walter Harry Pitts, quienes en 1943 definieron el primer modelo matemático de una red neuronal artificial [ 2 ], dando nombre a la que se conoce hoy en día como neurona de McCulloch-Pitts. Desde entonces hasta la actualidad, numerosos avances han aparecido en áreas que tenemos muy presentes en nuestra vida cotidiana como, por ejemplo: el reconocimiento de caracteres, personas y objetos mediante cámaras; la existencia de chatbots en páginas web para prestar soporte al usuario; los asistentes virtuales capaces de mantener una comunicación oral con las personas mientras cumplen diferentes tareas, los cuales han logrado bastante auge en el hogar y la automoción; y la conducción autónoma, un campo en continua evolución donde aún queda mucho por mejorar. De igual modo, la Inteligencia Artificial ha influido en campos como la medicina [3], las finanzas [ 4 ], la economía [ 5 ], el transporte [ 6 ], las comunicaciones [ 7 ], el ocio [ 8 ], la industria y la robótica [9], entre otros muchos sectores más. En un gran número de las aplicaciones previamente mencionadas existe un denominador común, el uso de cantidades inmensas de datos. Mediante estos se puede llegar a generar conocimiento con el objetivo de construir recursos con la capacidad de originar nuevos datos, extraer conclusiones, tomar decisiones o crear nuevos recursos, todo ello con la finalidad de alcanzar el objetivo del problema que se trata de resolver de la forma más óptima posible. 1
2Capítulo 1. Introducción Es por este uso masivo de datos y sus implicaciones, que importantes empresas han ampliado su línea de negocio en la recopilación, tratamiento, empleo y distribución de ellos; a la par que diversos organismos han comenzado a regular en este ámbito para velar por la privacidad y derechos de la población. 1.1 Motivación y objetivos del proyecto Tras las líneas anteriores se puede apreciar la importancia que tienen los datos y es por ello que se realiza el presente trabajo. Una vez se tiene acceso a ellos, bien por mecanismos propios de adquisición, o bien por obtenerlos a partir de un tercero, es necesario tratarlos y adaptarlos para hacer posible la aplicación de las técnicas conocidas que nos pueden conducir a resolver el problema afrontado. En concreto, el objetivo de los problemas que se van a tratar en este texto será la asignación de una clase, o el cálculo de alguna magnitud, a las distintas unidades de información de las que se dispone, haciendo uso para ello de los datos conocidos de cada unidad. Un ejemplo en el ámbito universitario podría ser la clasificación de los alumnos en una de las siguientes clases: aprobar el examen final de una asignatura al primer intento, o no superarlo. Con este objetivo se podría usar información como la edad, sexo, centros previos de estudio, historial de asignaturas cursadas con las respectivas notas obtenidas, situación laboral, créditos restantes y superados, etc. Otro ejemplo podría ser el uso de los indicadores obtenidos tras una analítica médica, incluyendo incluso la evolución de estos en el tiempo, para asignar a un paciente un porcentaje de predisposición de padecer una enfermedad. En el primer caso, una unidad de información, que a partir de ahora se llamará muestra u observación, es cada alumno, mientras que en el segundo ejemplo es cada paciente. Cabe destacar que en el primer problema se le asigna a cada muestra una de las dos clases, mientras que en el segundo se está estimando el valor de una variable continua. Cada uno de los datos que se conoce de una muestra pasará a usarse como variable del problema y se hará uso de la palabra característica o atributo para hacer referencia a ella. De este modo, los repositorios de datos están formados principalmente por variables estadísticas, las cuales adoptan un valor para cada una de las muestras, individuos o elementos. Además, puede darse el caso de que también se disponga del registro de estas variables en diferentes instantes, o su evolución durante un intervalo de tiempo, dando lugar a un histórico de datos. A continuación. se desarrolla la doble clasificación de las variables [ 10 ] [ 11 ]. Para mayor claridad, en la figura 1.1 se representa de forma esquemática junto a varios ejemplos. 1. Primera clasificación: •Variables categóricas: Son variables que se consideran clasificadoras o calificadoras, ya que catalogan los elementos en grupos, conjuntos, clases o categorías. Se conocen como dicotómicas aquellas que solo pueden adoptar dos valores diferentes, y como politómicas las que poseen un número de valores mayor a dos. Concretamente, el número de valores que puede adoptar una variable categórica se conoce como cardinalidad. A su vez, se subdividen en:
1.1 Motivación y objetivos del proyecto 3 –Nominales: Entre sus valores no se puede establecer un orden o jerarquía. – Ordinales: Entre sus valores sí se puede establecer un orden o jerarquía, pero no una magnitud de diferencia entre ellos. Es decir, se emplea una escala ordinal. – Discretas o de conteo: Son el resultado de contar el número de veces que ocurre un suceso y, por tanto, las categorías están representadas por números enteros. •Variables continuas: Son aquellas cuyos posibles valores están recogidos en una escala métrica continua de medición, indicando así cuantas veces contiene la unidad básica de medida de forma entera y fraccionada. 2. Segunda clasificación: •Variables cualitativas: A este grupo pertenecen las variables que describen características que pueden presentarse en los elementos que conforman el conjunto de datos. La frecuencia es la única propiedad que se puede aportar directamente a partir de estas. Concretamente, agrupa las variables categóricas nominales y ordinales. •Variables cuantitativas: Estas son las variables que se expresan de forma numérica y que, por lo general, están asociadas a una magnitud y unidades concretas. Además de la frecuencia, es posible el cálculo de una amplia gama de propiedades matemáticas. Engloba las variables continuas y las categóricas discretas. Nacionalidad:española,italiana,alemana... Color:rojo,verde,azul,amarillo,gris... Estadocivil:soltero/a,casado/a,viudo/a... Calificación:suspenso,aprobado,notable... Rangomilitar:soldado,cabo,sargento,brigada... Niveldeinglés:A1,A2,B1,B2,C1,C2 Númerodehijos:1,2,3,4... Edad:1,2,3,4,5,6... Númerodevehículos:1,2,3,4... Altura Ingresosenelúltimoperiodo Concentraciónquímica Nominal Ordinal Discreta Continua Categórica Cualitativas Cuantitativas Continua Figura 1.1 Clasificación de las variables. El hecho de que una variable pertenezca a un tipo u otro depende tanto de la naturaleza de esta como de la forma en la que se expresa. Por ejemplo, la acidez de una disolución se puede expresar en función del pH, es decir, un valor perteneciente a una escala continua y, por tanto, sería una variable continua. Sin embargo, también se podría expresar según si la disolución es básica, neutra o ácida, por lo que sería una variable categórica ordinal. Otro ejemplo se puede extraer de la medida de la masa de una población. Lo común sería expresarla en kilogramos, por lo que de nuevo estaríamos antes una variable continua. No obstante, si se usa una balanza con precisión de decigramos y expresamos los resultados en estas mismas unidades se podría considerar como una variable discreta, ya que conocemos el incremento mínimo que se produce entre dos valores consecutivos y nunca se obtendría uno nuevo entre ellos dos.
10 Capítulo 2. Codificación de variables categóricas Las codificaciones de las variables se van a definir como una aplicación Φ:Xi→Rai , donde ai representa la dimensión resultante y, por tanto, el número de variables nuevas por cada original codificada. Este valor dependerá de la transformación realizada. De esta forma, la codificación de la categoría z-ésima de la variable i-ésima queda como Φzxi=zΦi=hzφi,1,zφi,2, ..., zφi,t, ..., zφi,aii , siendo zφi,t un elemento genérico del vector. Evidentemente, la misma aplicación que define la codificación se puede emplear con las variables del vector de salida Y. En la situaciones en las que algún índice de la notación matemática no sea necesario será omitido. Esto ocurre cuando, por ejemplo, el vector de salida o el de entrada tienen un único elemento, Yk 1=YkyXk 1=Xk, respectivamente. Algunas codificaciones requieren el conocimiento de las variables de salida para poder ser aplicadas y, como se describirá en líneas posteriores, hacen uso de todas las observaciones de las que se tiene esta información. No obstante, es recomendable dividir al menos en dos partes el conjunto de muestras con salida conocida: uno de entrenamiento para calcular tanto la codificación como el modelo predictivo, y otro de validación para posteriormente comprobar la aptitud de los resultados. Para concluir, cabe mencionar que en la mayoría de problemas el vector de salida está formado por un único elemento, n=1 , y podemos distinguir dos situaciones según su naturaleza. Por un lado, estaríamos ante un problema de regresión si este elemento fuese una variable continua, por ejemplo: estimación de las ventas de un ejercicio económico, predicción del volumen de tráfico, etc. Por otro lado, se trataría de un problema de clasificación si la variable fuese categórica, ya que el objetivo consistiría en determinar a qué clase o categoría pertenece cada observación. Algunos casos de ejemplo podrían ser: determinar si una persona es afín a adquirir un producto, catalogar la música en géneros musicales o determinar si una imagen es apta para todos los públicos. A continuación, se procede con la exposición de las técnicas para llevar a cabo la codificación. 2.1 Codificación por etiquetado Se va a presentar brevemente esta codificación ya que, aunque no es demasiado rigurosa, es la que se emplea normalmente en las primeras iteraciones durante la resolución del problema [ 12 , 27 ]. Esto se debe principalmente a que resulta intuitiva, es de las ideas iniciales que suelen surgir y, además, es sencilla de implementar. El tratamiento que hace de las variables categóricas depende en esencia de la naturaleza de estas. Por un lado, cuando se trata de una variable discreta, hace uso de su misma etiqueta numérica para representar cada categoría. Así, por ejemplo, las variables número de vehículos propios o puntos restantes del permiso de conducir serían codificadas de esta manera, haciendo uso en el modelo predictivo de los propios números naturales que representan las categorías. Por otro lado, cuando la variable es ordinal, la codificación consiste en la traducción de ese orden en una enumeración, donde cada categoría es asociada con un número natural respetando el orden implícito existente entre categorías. Normalmente se comienza por el 0 o 1, y se continúa con el resto de números consecutivos. Por ejemplo, los niveles de dolor leve, moderado y fuerte se podrían asociar con los números 1, 2 y 3, respectivamente.
2.2 Codificación one-hot 11 Por último, si la variable es nominal, también se realiza una enumeración similar, aunque en esta ocasión será de forma prácticamente aleatoria debido a la ausencia de una pauta. Así, para la variable color, la asociación de amarillo con 1, azul con 2 y rojo con 3, es totalmente arbitraria y resulta en una posible codificación de dicha variable. En resumen, en todos los casos se realiza una aplicación biyectiva Φ:Xi→N que consiste en una enumeración del conjunto de categorías posibles. Los resultados obtenidos con este método se pueden mejorar mediante el uso de la razón y un análisis detallado del conjunto de datos y sus variables categóricas. De esta forma, mediante un proceso de prueba y error, se persigue modificar las asignaciones previas hasta conseguir dicho fin. Un hipotético ejemplo podría ser la variable día de la semana en un problema de predicción de la demanda eléctrica. En vez de relacionar los días con los números del 1 al 7, se podrían asociar de distinta forma teniendo en cuenta la variabilidad de la actividad humana a lo largo de la semana: de lunes a jueves se podría identificar con el 1, los viernes y sábado con el 4, y los domingos con el 3. Esto podría dar lugar a un mejor desempeño. Sin embargo, hay que tener en cuenta que esta tarea puede resultar no ser sencilla y que, además, no es seguro que se logre mejorar el desempeño. A esto hay que añadir que, a pesar del tiempo que es posible que conlleve conseguirlo, la codificación final no es extrapolable a otros problemas, donde habría que repetir el proceso de análisis y prueba. 2.2 Codificación one-hot Esta codificación, también conocida como variables dummy, es una de las más empleadas tanto en la resolución de problemas como en la investigación. Esto se debe a su fácil integración y a los buenos resultados que normalmente produce cuando las categorías de la variable que se codifica son mutuamente excluyentes. En obras como la de Cohen et al. [ 28 ] podemos encontrar un exhaustivo análisis estadístico de este método, y en otros muchos artículos científicos hacen uso de él para resolver problemas reales, por ejemplo, sobre la predicción de la demanda de bicicletas en una empresa de transporte [29]. La base del método es sencilla. En primer lugar, a partir de una variable categórica Xi se crean pi nuevas variables φi , y cada una de estas nuevas se asocia de forma individual a un elemento del conjunto Xi . En segundo lugar, todas las variables de codificación toman el valor 0, no obstante, en cada muestra k, la variable zφi,tasociada al valor que toma la variable original, zxk i, es igual a 1. A modo de ejemplo, se recoge en la tabla 2.1 el resultado de la codificación de la variable ficticia color, la cual puede adoptar cinco valores diferentes: azul, rojo, verde, blanco y negro. Algunos autores proponen usar solo pi−1 variables en la codificación para evitar que aparezca redundancia 1 y mejorar algunas propiedades estadísticas [ 28 ]. Para conseguirlo, a una de las categorías se le asigna el vector nulo sin ninguna variable igual a 1. No obstante, con esta práctica se pierde una característica importante de la codificación que, por lo general, es conveniente para las técnicas de aprendizaje automático que se basan en el cálculo de distancias: produce un espacio 1 En algunos modelos predictivos es necesario que no existan combinaciones lineales en la entrada para asegurar un buen desempeño.
12 Capítulo 2. Codificación de variables categóricas Tabla 2.1 Ejemplo de codificación one-hot. Categorías Vector de codificación Azul Rojo Verde Blanco Negro AzulΦColor 1 0 0 0 0 Ro joΦColor 0 1 0 0 0 VerdeΦColor 0 0 1 0 0 BlancoΦColor 0 0 0 1 0 NegroΦColor 0 0 0 0 1 ortogonal donde cada categoría de un atributo se asocia con una dimensión, provocando que todas las categorías sean equidistantes con una distancia igual a √2 entre cualquier par de valores. En la figura 2.1 se representa esta propiedad, para ello, se simula una variable con solo tres categorías y se representan los tres vectores que produce la codificación junto a la distancia entre ellos. (0,1,0) (0,0,1) (1,0,0) √2 √2 √2 Figura 2.1 Espacio ortogonal generado por la codificación one-hot. Un caso excepcional aparece cuando se aplica la codificación one-hot a una variable categórica de cardinalidad 2. Si se realiza tal y como se ha expuesto, daría lugar a dos variables nuevas, lo cual no es necesario. Con una sola variable, que toma el valor 0 para una categoría y 1 para la otra, se cumpliría la equidistancia entre ellas. Esta técnica no está exenta de inconvenientes, siendo el más evidente el número de variables nuevas que aparecen. Esto tiene implicaciones directas en la cantidad de memoria necesaria para almacenar toda la información del conjunto de datos, problemática que se acentúa cuando crece la cifra de variables categóricas de gran cardinalidad. No obstante, con las capacidades de almacenamiento de los ordenadores de hoy en día no supone un gran problema. Donde sí afecta realmente es en el número de cálculos que los algoritmos posteriores tendrán que realizar sobre los datos, lo que se traduce directamente en mayor tiempo de ejecución. Por ejemplo, si se trabaja con un conjunto D de 10 atributos donde 3 son categóricos con cardinalidad 5, 6 y 3; el tiempo requerido será evidentemente mayor cuando se emplea un espacio de dimensión 21 en vez de 10. Para paliar este efecto se pueden usar matrices dispersas. Estas no se almacenan de la forma tradicional, sino que guardan los pares valor y posición de los elementos diferentes a 0, asumiendo que los restantes son todos nulos. Así es posible reducir el tamaño de los datos cuando existe un gran número de ceros, y además, bajo determinadas condiciones, agilizar los cálculos [30].
2.2 Codificación one-hot 13 No obstante, es más frecuente emplear otro tipo de recursos para realmente reducir la dimensionalidad del problema resultante. Por ejemplo, se podría realizar un análisis de las categorías de Xi con el objetivo de encontrar aquellas con menor frecuencia de aparición, posteriormente, agrupar estas bajo una misma categoría codificada en una única variable φi,t . Así se consigue reducir el número de variables afectando a un número reducido de muestras. Esta agrupación también se podría realizar unificando categorías similares bajo una única que sea representativa de las que agrupa, bien de forma manual por inspección y estudio de los datos, o bien recurriendo a medidas de similitud entre categorías [ 19 ] o mediante técnicas de clustering. Otra opción sería aplicar procedimientos para reducir la dimensionalidad una vez se han codificado las variables: análisis de componentes principales (PCA), análisis discriminante lineal (LDA) o análisis de la correlación canónica (CCA). El otro inconveniente de este método se presenta cuando aparecen nuevos valores de una variable categórica. Es posible que durante las fases de extracción de conocimiento y generación de modelos se use un conjunto de datos que no represente toda la cardinalidad de las variables categóricas, lo que puede provocar que cuando se apliquen los modelos obtenidos sobre futuras muestras aparezcan nuevas categorías que no tengan cabida en ninguna variable de codificación φ . Como consecuencia, puede que sea necesario volver a pasar por las primeras etapas y procesar de nuevo los datos para poder incluir dicha categoría. Para evitar esta situación, se puede prever una variable cuyo valor sea 1 para todas las categorías nuevas que no habían sido consideradas, lo cual ayuda a atajar el problema ahorrando tiempo, a pesar de no ser lo óptimo. 2.2.1 Codificación one-cold Existe un método muy similar al anterior llamado one-cold. La diferencia reside en que actúa de forma inversa: todas las variables son 1 excepto la asociada al valor que toma la variable categórica en cada muestra. El efecto que consigue es el mismo, dos categorías diferentes vuelven a estar separadas una distancia de √2 , por lo que los resultados que produce deberían ser similares; aunque es posible que aparezcan discrepancias por la etapa de normalización de datos que se aplica siempre durante el tratamiento de estos. Todas las particularidades de one-hot descritas en las líneas anteriores son también de aplicación en esta situación, sin embargo, por motivos obvios, el uso de matrices dispersas no resulta ahora de gran utilidad. A modo de ejemplo, se realiza en la tabla 2.2 la codificación one-cold de la misma variable color de la tabla 2.1. Tabla 2.2 Ejemplo de codificación one-cold. Categorías Vector de codificación Azul Rojo Verde Blanco Negro AzulΦColor 0 1 1 1 1 Ro joΦColor 1 0 1 1 1 VerdeΦColor 1 1 0 1 1 BlancoΦColor 1 1 1 0 1 NegroΦColor 1 1 1 1 0
14 Capítulo 2. Codificación de variables categóricas 2.2.2 Codificación rank-hot En las dos codificaciones anteriores se está eliminando por completo cualquier representación implícita de una ordenación entre categorías. Sin embargo, la codificación rank-hot sí refleja un orden entre los valores que puede adoptar la variable categórica, lo cual puede ser beneficioso en caso de ser ordinal [31]. Para ello, se usa un vector de codificación de tamaño igual a la cardinalidad de la variable categórica menos uno. Luego, a cada categoría, se le asigna un número entero para establecer la ordenación, se debe empezar por cero y usar números consecutivos. Posteriormente, para codificar una categoría, se igualan a 1 las variables del vector de codificación cuya posición sea igual o menor al entero asociado a dicha categoría. Al resto de variables que no cumplen esta condición se les asigna un 0. Con rank-hot se consigue que la distancia entre dos muestras dependa de la categoría que adopta cada una. Si son iguales, será 0, y sin son diferentes, la distancia será mayor mientras más separadas estén las categorías en la ordenación establecida. De nuevo, se realiza con este método la codificación de la variable color, tabla 2.3. Para el ejemplo, se ha realizado una ordenación totalmente aleatoria de los colores siguiendo la disposición que aparece en la tabla. Tabla 2.3 Ejemplo de codificación rank-hot. Categorías Variable de codificación Azul Rojo Verde Blanco Negro φColor,10 1 1 1 1 φColor,20 0 1 1 1 φColor,30 0 0 1 1 φColor,40 0 0 0 1 2.3 Codificación binaria El método de codificación binaria guarda ciertas similitudes con el presentado previamente. En concreto, las variables de codificación que genera también toman solo el valor uno o cero. Además, el valor ai , es decir, el tamaño del vector de codificación, también es mayor a 1; aunque como veremos, crece de forma más moderada con respecto a la cardinalidad. Para aplicar esta codificación sobre una variable categórica genérica, Xi , es necesario primero establecer un orden entre las categorías que puede adoptar. La ordenación se puede realizar aplicando cualquier criterio y, según el problema, tendrá efectos diferentes o incluso arbitrarios: por frecuencia absoluta de cada categoría, por orden alfabético, por orden de aparición, por alguna relación con la variable de salida como la probabilidad condicional, de forma aleatoria, etc. Una vez se tienen las categorías ordenadas, se les asigna un número entero siguiendo el orden obtenido y empezando por el 0. Por último, esta asignación se transforma a base binaria y cada uno de los dígitos pasará a ser una variable de codificación φi,t.
2.3 Codificación binaria 15 Para mayor claridad se añade un ejemplo en la tabla 2.4, donde se recoge la codificación binaria de la variable categórica estado civil, la cual puede ser: soltero/a, casado/a, pareja de hecho, separado/a, divorciado/a o viudo/a. Las categorías se han ordenado de forma totalmente arbitraria siguiendo el mismo orden en el que se han enumerado. Tabla 2.4 Ejemplo de codificación binaria. Categorías Variable de codificación Soltero/a Casado/a Pareja de hecho Separado/a Divorciado/a Viudo/a φEstadoCivil,10 0 0 0 1 1 φEstadoCivil,20 0 1 1 0 0 φEstadoCivil,30 1 0 1 0 1 El número de variables nuevas requeridas para la codificación de Xi , que también es dependiente de la cardinalidad de la variable que se está tratando, es igual al número menor de bits necesarios para representar de forma individual todas las categorías. Este valor se puede obtener mediante: ai=mínimo {N| ≥log2(pi)}(2.2) La ampliación de la dimensión del espacio de características es menor que con la codificación previa gracias a la presencia del logaritmo. Este hecho queda reflejado en la figura 2.2, donde se representa la dimensión del vector Φi en función de la cardinalidad de la variable codificada, quedando en evidencia el beneficio de la evolución logarítmica frente a la lineal. 0 25 50 75 100 125 150 Cardinalidad de Xi: pi 100 102 Dimensión de i: ai Cod. one-hot Cod. binaria Figura 2.2 Comparación de la codificación binaria y one-hot respecto al número de variables nuevas. 2.3.1 Código Gray Cuando se aplica la transformación a binario es muy posible que no se aproveche el potencial de la ordenación que se realiza previamente. Por ello, puede ser interesante pasar a base 2 haciendo uso del código Gray, ya que este tiene la ventaja de que dos números consecutivos solo se diferencian en un dígito.
16 Capítulo 2. Codificación de variables categóricas Para pasar de binario a Gray tan solo es necesario realizar una operación XOR desechando el acarreo del número a convertir consigo mismo desplazado un bit a la derecha [ 32 ]. En la tabla 2.5 se recoge la transformación para 4 bits. Tabla 2.5 Correspondencia entre código binario y Gray. Base 10 Binario Gray Base 10 Binario Gray 0 000 000 4 100 110 1 001 001 5 101 111 2 010 011 6 110 101 3 011 010 7 111 100 2.4 Codificación mediante entity embeddings La técnica de codificación recogida en esta sección se ha extraído del artículo Entity Embeddings of Categorical Variables de los autores Cheng Guo y Felix Berkhahn [ 33 ]. En el documento describen como emplear el método conocido como entity embeddings, ampliamente usado en el procesamiento del lenguaje natural (NLP), para llevar a cabo de forma automática la representación de variables categóricas en un espacio multidimensional, de manera que, categorías similares se encuentran próximas entre sí y, al mismo tiempo, alejadas de otras categorías con un menor grado de similitud. Bajo este contexto, la similitud entre categorías es evaluada en función de la relación de estas con la variable de salida. El hecho de que los valores similares queden próximos entre sí al realizar el mapeo evidencia las relaciones intrínsecas entre categorías, ayudando a las técnicas más comunes de aprendizaje automático a resolver los problemas. Además, al permitir el cálculo de distancias, esta técnica puede ser también empleada para visualizar datos o realizar clustering. La codificación está sumamente ligada a las redes neuronales. En realidad, es presentada como una forma efectiva de poder hacer uso de ellas en problemas que contienen variables categóricas, ya que sus capacidades para hacer frente a estas variables son limitadas por tratarse de modelos continuos. Al mismo tiempo que las redes resuelven el problema, generan la codificación de las variables categórica, la cual puede ser fácilmente extraída y usada en otros tipos de modelos predictivos. El método toma como base la codificación one-hot, la cual genera un espacio de dimensión igual a la cardinalidad de la variable codificada, tal como se describió en la sección 2.2. Partiendo de este punto, la nueva codificación trata de condensar dicho espacio en otro continuo de dimensión más reducida aprovechando las posibles relaciones que existen entre categorías. Tras esto, representa cada valor categórico como un vector en el dominio del espacio condensado. La idea de los autores para llevar a cabo esta tarea consiste en la modificación de la capa de entrada de la red, tal y como se refleja en la figura 2.3. Ahora, las variables de entrada están conectadas a la primera capa mediante un codificador one-hot. Como se puede observar, la capa está segmentada en grupos independientes y, cada uno de estos, tiene el mismo número de neuronas que la cardinalidad de la variable que trata. La codificación
2.4 Codificación mediante entity embeddings 17 One-hot One-hot One-hot ai ai+1 am pi pi+1 pm xi xi+1 xm Salida Capasdensas completamenteconectadas Concatenación Capade embedding Codificación one-hot Variables categóricas Capade tratamiento one-hot Figura 2.3 Estructura de la red neuronal para el tratamiento y codificación de variables categóricas. se puede definir matemáticamente como una aplicación f:Xi→δpi zxiα , donde el resultado es un vector de dimensión igual a la cardinalidad, pi , y sus componentes son deltas de Kronecker, δzxiα . El subíndice α es diferente para cada elemento del vector y puede adoptar los mismos valores que Xi , α∈Xi. En definitiva, el resultado es el mismo que el presentado en one-hot, por lo que, por cada categoría a la entrada, todas las neuronas de un grupo reciben un cero a excepción de la asociada a dicha categoría, que recibe un 1. δzxiα=1si α=zxi 0si α6=zxi (2.3) Tras esta capa que recibe las variables codificadas se añade una nueva formada por neuronas lineales conocida como capa de embedding. Al igual que ocurría con la primera, las neuronas de esta capa se encuentran divididas en tantos grupos como variables categóricas. Además, las conexiones de neuronas de ambas capas solo se producen entre las que pertenecen al grupo asociado a la misma variable. En una etapa posterior, todas las neuronas de la capa de embedding son concatenadas para unirse con la primera de las capas densas de la red completamente conectada. Si en el problema existiesen también variables continuas, sus correspondientes entradas a la red se podrían añadir en esta concatenación. Cuando una entrada toma el valor zxi , se puede calcular la salida de las neuronas de la segunda capa pertenecientes al grupo asociado a esa entrada mediante la expresión 2.4. En esta, el término ωα hace referencia a la matriz de pesos de ponderación de las uniones de la capa de codificación con la capa de embedding. Al ser solamente un elemento del vector δpi zxiα igual a 1, el resultado es el vector de pesos que une la neurona de la capa de codificación asociada a la categoría zxi con las neuronas de la capa de embedding. zΦi=∑ α ωα·δpi zxiα=ωzxi(2.4)
18 Capítulo 2. Codificación de variables categóricas Tal como se ha adelanto con la nomenclatura en la expresión 2.4, estos pesos entre las dos capas descritas constituyen la codificación de las categorías. El cálculo de estos es directo mediante la aplicación de las técnicas convencionales de entrenamiento supervisado. De este modo, al mismo tiempo que la red es entrenada y las capas más ocultas aprenden a resolver el problema, la capa de embedding entiende y condensa las relaciones implícitas entre categorías, quedando el resultado reflejado en los pesos. Un hecho muy importante es que el número de neuronas en cada grupo de la capa de embedding es un parámetro que se puede ajustar. Como consecuencia, el número de pesos entre ambas capas es variable y, por tanto, el tamaño del vector de codificación zΦi también. Concretamente, su dimensión ai es igual al tamaño del grupo de neuronas que codifica la variable en cuestión. Para evitar la aparición de dependencias lineales, el número de neuronas está acotado superiormente por la cardinalidad de la variable menos 1, pi−1. Una aplicación de este método de codificación se puede encontrar en el artículo de Bo Wang et al., en el cual tratan de predecir el flujo de tráfico en un sistema de bicicletas compartidas ubicado en Sozhou usando solo variables categóricas en una red neuronal [ 34 ]. En esta situación, el tamaño que recomiendan para la capa de embedding es igual a la raíz cuadrada de la cardinalidad, √p1. Por último, es importante destacar que para aplicar esta técnica es necesario conocer la salida de un subconjunto representativo de muestras para poder entrenar la red, requisito que no era necesario con one-hot. No obstante, posee la ventaja de que el tamaño del vector de codificación es un parámetro independiente de la cardinalidad y, sobre todo, variable según se crea conveniente. Esto permite reducir el tamaño del espacio de características, lo que ahorra memoria y acelera los modelos predictivos. 2.5 Codificación basada en estadísticas de la variable de salida Este método corresponde con el presentado por Daniele Micci-Barreca en su trabajo A Preprocessing Scheme for High-Cardinality Categorical Attributes in Classification and Prediction Problems [ 35 ]. El autor desarrolla una técnica sencilla para la codificación de variables categórica que no se ve afectada por variables de gran cardinalidad. Se basa principalmente en el método de Bayes empírico, de forma que cada categoría de una variable pasa a estar representada por una probabilidad estimada de la variable de salida del problema: cuando se trata de un problema de clasificación, se emplea la probabilidad a posteriori de la salida condicionada por el valor de la variable categórica; cuando el problema es de regresión, se usa el valor numérico esperado de salida condicionado nuevamente por el valor categórico que se está codificando. De esta manera, las variables categóricas se convierten en variables cuasi-continuas, lo que facilita la integración de estas en modelos predictivos. Barreca también describe una modificación para tratar variables cuyas categorías guardan una estructura jerárquica implícita, sin embargo, no se recoge en este texto por ser solo aplicable en determinados casos concretos.
2.5 Codificación basada en estadísticas de la variable de salida 19 2.5.1 Aplicación en clasificación con salida dicotómica Aunque en líneas posteriores se mostrará que la siguiente asunción no es un requisito necesario, se va a asumir que el problema sobre el que se aplica esta técnica reparte las observaciones entre dos clases, es decir, el vector de salida es realmente un único valor dicotómico, Yk∈{1Y=1,2Y=0} , donde la representación 0 y 1 se ha tomado por comodidad. Bajo esta situación, el método realiza la codificación de una variable categórica Xi mapeando cada valor de su conjunto Xi con un escalar. Esto quiere decir que la dimensión del vector de codificación es 1, ai=1 , puesto que solo aparece una variable de codificación por cada variable categórica original del problema, gracias a lo cual, no se altera la dimensión del espacio de características. El escalar usado para mapear una categoría representa la probabilidad estimada de que la salida sea igual a 1 condicionada a que la variable original adopte el valor categórico a codificar, matemáticamente: zΦi=P(Y=1|Xi=zxi)(2.5) Es evidente que, para poder calcular esta probabilidad, es necesario disponer previamente de un conjunto de datos ya clasificado. Si el número de muestras con Xi=zxi para todo zxi∈X es suficientemente grande, la probabilidad se puede calcular simplemente como el cociente del número de observaciones con Y=1yXi=zxientre el número total de observaciones con la categoría zxi: zΦi=P(Y=1|Xi=zxi) = f(Y=1∩Xi=zxi) f(Xi=zxi) (2.6) No obstante, en los conjuntos de datos reales es muy probable que algunas categorías no aparezcan un número suficiente de veces, sobre todo cuando las variables tienen gran cardinalidad, por lo que la probabilidad calculada según la ecuación 2.6 no sería fiable. Por este motivo, el autor propone el cálculo de la probabilidad necesaria en la codificación mediante la suma ponderada de dos probabilidades: la a posteriori, calculada con la misma expresión 2.6; y la a priori, siendo la probabilidad de obtener la salida Y=1 calculada sobre el conjunto completo con independencia de las variables de entrada, hipótesis nula: zΦi=Λ(fXi=zxi)f(Y=1∩Xi=zxi) f(Xi=zxi) +(1−Λ(fXi=zxi)) fY=1 d(2.7) Por la forma en la que está definida la expresión 2.7, parece razonable que la función de ponderación Λ(f) sea monótona creciente y, además, una condición que sí debe cumplir es que se encuentre acotada en el rango [0,1] . Así, cuando la cantidad de observaciones con Xi=zxi sea representativa, el peso de la probabilidad a posteriori será cercano a 1 y el de la a priori será aproximadamente 0, y viceversa. Esto permite usar la misma expresión en la codificación de todas las categorías independientemente del número de veces que aparezcan en el conjunto de datos.
26 Capítulo 2. Codificación de variables categóricas 2.7.1 Factor de Bayes Realmente, el cálculo del peso de la evidencia no es más que el logaritmo neperiano del factor de Bayes [ 40 ]. Este deriva directamente del teorema de Bayes, y también es usado como medida cuantitativa de la robustez de la evidencia. En esencia, el factor de Bayes es una comparación de cómo de bien dos hipótesis complementarias predicen los datos. Por ello, parece buena idea emplear también este cociente de probabilidades para codificar las categorías. Matemáticamente queda como: zφi=100 P(Xi=zxi|Y=Goody) P(Xi=zxi|Y=Bady)=100 f(Xi=zxi∩Y=Good y) f(Y=Good y) f(Xi=zxi∩Y=Bad y) f(Y=Bad y) (2.25) Indistintamente de la expresión que se utilice, 2.24 o 2.25, este método de codificación resulta en una única variable nueva por cada variable original categórica, por lo que, de nuevo, no se incrementa el tamaño del espacio de características. El inconveniente vuelve a ser la necesidad de conocer la salida de un subconjunto de muestras y, además, que esta debe ser categórica dicotómica. 2.8 Codificación por similitud En el artículo Similarity encoding for learning with dirty categorical variables [ 24 ], los autores mencionan sin entrar en detalle una forma de codificar las categorías de una variable haciendo uso de la similitud entre ellas. La idea consiste en usar para cada variable categórica Xi un vector de codificación zΦi de tamaño igual a la cardinalidad de la variable, ai=pi . Luego, asociar cada elemento de dicho vector, zφi,t , con una categoría de la variable original, zxi . Posteriormente, calcular cada elemento del vector como la similitud entre la categoría a codificar, z, y la categoría asociada al elemento. zΦi=hzφi,1,zφi,2, ... ,zφi,t, ... ,zφi,aii= =sim(zxi,1xi),sim(zxi,2xi), ... ,sim(zxi,zxi), ... ,sim(zxi,aixi)(2.26) Para el cálculo de la similitud existen numerosas expresiones, y el uso de una u otra dará lugar a diferentes resultados. En el artículo de Boriah et al. [ 19 ] se puede consultar una recopilación de 14 funciones para calcular la similitud y, de entre todas ellas, se ha escogido la siguiente: sim(rxi,zxi) = (1si rxi=zxi 1 1+ln d f(Xi=rxi)·ln d f(Xi=zxi) si rxi6=zxi(2.27)
2.9 Codificación basada en la frecuencia 27 El uso de la expresión previa hace que este método guarde cierta semejanza con one-hot. En concreto, por cada categoría existe una variable de codificación que toma el valor de 1 cuando la variable original es igual a dicha categoría. La diferencia radica en que ahora el resto de variables de codificación no son 0, sino que adoptan un valor entre 0 y 1 en función de la similitud entre categorías. Para evidenciar este hecho se adjunta la tabla 2.7 con la codificación de la misma variable que se usó de ejemplo en one-hot, tabla 2.1. Se comprueba como la diagonal sigue estando formada por unos, pero el resto de elementos ya no son ceros. La simetría de la tabla se debe a la propiedad conmutativa de la función de cálculo de la similitud. Tabla 2.7 Ejemplo de codificación por similitud. Categorías Vector de codificación Azul Rojo Verde Blanco Negro AzulΦColor 1 0.353 0.179 0.657 0.965 Ro joΦColor 0.353 1 0.576 0.335 0.634 VerdeΦColor 0.179 0.576 1 0.295 0.523 BlancoΦColor 0.657 0.335 0.295 1 0.205 NegroΦColor 0.965 0.634 0.523 0.205 1 2.9 Codificación basada en la frecuencia Esta última codificación se basa en la única propiedad que se puede medir de forma directa en las variables categóricas: la frecuencia de aparición de cada categoría. Su aplicación resulta tan sencilla como sustituir cada valor categórico por su frecuencia relativa. Matemáticamente queda como: zΦi=f(Xi=zxi) d(2.28) La ventaja de esta codificación es que se realiza con una única variable continua, independientemente de la cardinalidad y sin necesidad de conocer la salida. Además, al utilizar la frecuencia relativa, la codificación está normalizada en el intervalo [0,1]. No obstante, la sencillez del método deriva en que solo es idóneo cuando la salida del problema guarda cierta relación con la frecuencia. En otros casos, el resultado será inservible, como por ejemplo, en la variable día de la semana en una serie temporal continua: debido a que todos sus valores se repiten de forma cíclica a lo largo del tiempo, todos tienen la misma frecuencia relativa.
3 Técnicas de análisis predictivo empleadas Pa ra resolver los dos tipos de problemas descritos en la introducción, clasificación y regresión, y de esta manera poder comparar el desempeño de las codificaciones previas, se va a hacer uso de recursos pertenecientes al campo del aprendizaje automático, en concreto, técnicas de análisis predictivo. Mediante la aplicación de estas es posible aprovechar la información condensada en acontecimientos o casos conocidos para generar modelos predictivos. Estos últimos nos permiten pronosticar eventos y resultados que son futuros o desconocidos. Para realizar la tarea de predicción se ha desarrollado a lo largo de los años un gran número de técnicas, y el desempeño que produce una u otra está estrechamente vinculado con la naturaleza de los datos y del problema tratado. En concreto, se va a usar el método conocido como kvecinos más cercanos en el problema de clasificación que se va a tratar. Para el problema de regresión se usará una red neuronal de función de base radial. A continuación, se realiza una descripción de ambas técnicas. 3.1 Método de los kvecinos más cercanos Esta técnica fue descrita la primera vez por E. Fix y J. L. Hodges para llevar a cabo tareas de clasificación [ 41 ], siendo más conocida por la abreviatura k-NN con origen en su nombre en inglés, k-nearest neighbors. Más tarde, T. M. Cover y P. Hart continuaron con el estudio de las propiedades del método y acotaron inferior y superiormente el error obtenido en la clasificación [17]. Su principio de funcionamiento es muy sencillo y se basa en el cálculo de distancias. Primero, se parte de un conjunto de muestras cuya salida, también denominada clase, es conocida. Luego, durante la clasificación, una muestra con salida desconocida es clasificada en función de las salidas de las observaciones con las que guarda mayor relación, es decir, las que se encuentran más próximas. En concreto, el parámetro k que aparece en el nombre es el número de vecinos de la muestra desconocida que se tienen en cuenta durante la clasificación, y su finalidad es filtrar posibles muestras anómalas. 29
30 Capítulo 3. Técnicas de análisis predictivo empleadas X1 X2 X K=1 K=2 K=3 Figura 3.1 Ejemplo gráfico de clasificación k-nn. El método es más sencillo de explicar con un ejemplo gráfico. En la figura 3.1 se representa un hipotético problema cuyo espacio de características es de dimensión 2, por lo que las variables pueden ser representadas en un plano. Todas las muestras con salida conocida son situadas en el plano en función del valor que toman sus variables de entrada, y la salida de cada una es ilustrada en función del símbolo que las representa. Posteriormente, cuando se realiza la clasificación de una muestra desconocida, representada con una cruz, tan solo es necesario observar su entorno. Si k es uno, la salida predicha es igual a la de las muestras representadas con un cuadrado, puesto que la observación más cercana pertenece a esa clase. Si k es dos, la vecindad está formada por una observación con la clase representada por el cuadrado y otra por el círculo; ante este empate habría que establecer algún criterio de prioridad para seleccionar una de las dos, por ejemplo, la más cercana, que seguiría siendo el cuadrado como en la situación anterior. Por último, si k es tres, la salida representada por el círculo es mayoritaria, por lo que es esta la que se le asigna a la muestra clasificada. En definitiva, para un tamaño de vecindad genérico k , la salida predicha de una muestra, ˆyi , será igual a la más repetida de entre las k observaciones más cercanas con salida conocida. Matemáticamente se puede expresar como queda recogido en la ecuación 3.1, donde K es el conjunto de las k muestras conocidas más cercanas a la que está siendo clasificada y δ es una delta de Kronecker cuyo valor es uno cuando las dos salidas que compara son iguales. ˆyi=argmax y∈Y∑ j∈K ωjδy,yj(3.1) Además, el término ωj se puede usar para ponderar la importancia de cada uno de los vecinos que intervienen en la clasificación. Lo más habitual es que el factor de ponderación sea función de la distancia entre la muestra nueva y su vecino, incrementando su valor mientras más próximos estén. De esta forma se consigue evitar la situación de empate del ejemplo para el caso k=2 . Una posibilidad es usar como peso la inversa de la distancia, normalizada con la inversa de todas las distancias con el vecindario. Así, al estar invertida, el peso es mayor conforme más cercas están y, al estar normalizada, la suma de todos los pesos es 1, lo cual siempre es deseable en todas las ponderaciones. Otras formas de calcular los pesos se pueden encontrar en la obra de Dudani [42]. ωj=d(i,j)−1 ∑ p∈K d(i,p)−1(3.2)
3.1 Método de los kvecinos más cercanos 31 Como es evidente, para poder llevar a cabo la clasificación es necesario calcular las distancias entre la muestra nueva y las muestras de salida conocida, para así determinar cuáles de estas últimas son las más cercanas. Además, este cálculo también es requerido cuando la ponderación depende de la proximidad. Se utilizará la notación d(a,b) para hacer referencia a la distancia entre dos observaciones cualesquiera y para su cálculo se podría usar, entre otras, la distancia de Mahalanobis o la de Minkowski. Esta última se expresa como: d(a,b) = m ∑ i=1 wi|Xa i−Xb i|p!1/p (3.3) La métrica resultante depende del valor p y se pueden destacar tres casos, figura 3.2: cuando es 1 se tiene la distancia de Manhattan; cuando es 2, la euclídea; y cuando tiende a infinito, la de Chebyshev. Además, se ha añadido otro factor de ponderación wi para alterar la importancia relativa que tiene cada una de las variables que definen el espacio de características. otro aspecto muy importante es la necesidad de normalizar los datos, ya que, si no se hace, las variables cuyos valores tengan los mayores órdenes de magnitud cancelarán el efecto del resto de variables en el cálculo de la distancia. Figura 3.2 Conjunto de puntos con una distancia de Minkowski unidad respecto al origen para diferentes valores de p. Además de llevar a cabo la clasificación, el método k−nn es capaz de otorgar un valor que refleja la confianza de la predicción mediante un análisis de la vecindad [ 43 , 44 ]. Gracias a esta característica, es posible rechazar predicciones cuya probabilidad de acierto es muy reducida o, al contrario, aprobar aquellas cuyo grado de confianza es muy elevado. Esta característica resulta de gran utilidad en problemas donde el coste asociado a un fallo en la clasificación es muy elevado y, por tanto, solo interesa actuar cuando es superado un umbral de confianza. Por otro lado, para poder hacer uso de este clasificador con variables categóricas es necesario tratar los datos mediante codificaciones como las desarrolladas en el capítulo previo. Otra posibilidad es usar funciones de cálculo de distancias que admiten variables categóricas como las que se mencionaron en el estado del arte [19, 20, 21]. Para acabar, es cierto que la etapa de entrenamiento de este método consiste principalmente en almacenar las muestras con entrada y salida conocida para, posteriormente, ser usadas en el cálculo de distancias durante la clasificación. No obstante, es posible mejorar el resultado global mediante la
32 Capítulo 3. Técnicas de análisis predictivo empleadas aplicación de técnicas más avanzadas que permiten aumentar la tolerancia del clasificador ante casos anómalos, disminuir el error, agilizar la clasificación, reducir la cantidad de memoria y los recursos computacionales necesarios, hacer frente a conjuntos desbalanceados en los que la predominancia de una clase provoca que la clasificación sea sesgada, etc. Algunos ejemplos de estas técnicas son: •Selección de prototipos : Puede resultar beneficioso usar solo un subconjunto de las muestras conocidas en vez de todas de las que se dispone [45]. •Entrenamiento de prototipos : Además de usar solo un subconjunto de las muestras conocidas, se procesan y modifican las elegidas durante un algoritmo de entrenamiento [ 46 ], por ejemplo, Learning Vector Quantization (LVQ). •División del espacio : Para conocer cuáles son los vecinos más cercanos es necesario calcular la distancia a todas las observaciones conocidas. Sin embargo, existen métodos que dividen el espacio de características en regiones, o que almacenan los datos en estructuras especiales, con el objetivo de evitar tener que calcularlas todas [47, 48]. •Entrenamiento del cálculo de distancias : Además de existir numerosas funciones para calcular la proximidad, se han desarrollado algoritmos que aprenden que características son más importantes y lo reflejan en la función de distancia mediante pesos que ponderan las variables de entrada [49]. 3.2 Redes neuronales de base radial Este tipo de redes, conocidas en inglés como Radial Basis Function Network (RBFN), aparecieron con el objetivo de llevar a cabo interpolación o aproximación de funciones. No obstante, su aplicación se ha extendido también en problemas de clasificación y regresión [ 50 ], siendo este último el uso que se le va a dar en este texto. Estas redes tienen la peculiaridad de estar formadas por una capa de entrada, una única capa oculta y la de salida. Esta estructura, ilustrada en la figura 3.3, tiene la ventaja de disminuir el número de capas y neuronas totales en la red, lo que permite reducir los requisitos computacionales y los tiempos de entrenamiento. A continuación, se describe cada una de las capas. Σ X1 X2 X3 Xm W1 W2 W3 Wt Ŷ Vectorde entrada Capadeentrada mneuronas Capaoculta tneuronas Capadesalida 1neurona Salida predicha Figura 3.3 Estructura de una red neuronal de base radial.
3.2 Redes neuronales de base radial 33 La primera capa recibe el vector de entrada y tiene tantas neuronas como variables. Las salidas de estas neuronas no están ponderadas con ningún peso y se encuentran directamente conectadas con las neuronas de la capa oculta. Luego, cada una de las neuronas de la capa oculta implementa una función de base radial. Estas funciones tienen la peculiaridad de depender únicamente de la distancia de la entrada a un centro. Una de las más utilizadas es la función gaussiana, expresión 3.4, que tras adaptarla a esta aplicación queda como la expresión 3.5. Esta última depende de dos parámetros. Uno es el vector XP , el cual constituye el centro de la función y, por lo general, es una observación con salida conocida del conjunto de datos denominada prototipo. El otro parámetro es β y permite controlar la pendiente de la curva resultante. g(x) = aexp−(x−b)2 2c2,C>−1(3.4) g(X) = exp−βd(X,XP)2,β>0(3.5) La distancia euclídea es la que se usa con mayor frecuencia en la aplicación de la función 3.5. Además, teniendo en cuenta que el exponente siempre es un número negativo, el rango está acotado entre 0 y 1. De esta forma, mientras más similares sean el vector de entrada y el prototipo, menor será la distancia entre ellos y, por tanto, más cercano a 1 el valor de la función. Por contra, mientras mayor sea la discrepancia entre ambos vectores, más tenderá la función a 0. Esta respuesta se representa mediante el ejemplo de la figura 3.4. Se han usado vectores de un único elemento para facilitar la visualización y el centro de la campana está en 3, es decir, XP=3 . Se han empleado varios valores de β para mostrar la influencia de este parámetro: cuando crece, la campana se estrecha; y cuanto más se aproxima a 0, más se suaviza la pendiente, ensanchando la campana y ofreciendo una transición más suave entre el valor máximo y mínimo. 0123456 x 0 0.2 0.4 0.6 0.8 1 g(x) = 0.25 = 0.5 = 1 = 2 = 10 Figura 3.4 Familia de curvas centradas en 3 para diferentes valores de βde la expresión 3.5. Obviamente, cada una de las neuronas de esta capa implementa su propia función con diferentes parámetros, ofreciendo así respuestas distintas ante el mismo vector de entrada. El número de neuronas es otro parámetro de diseño de la red: un número demasiado elevado puede provocar sobreajuste, mientras que un número reducido puede inhabilitar la red para producir predicciones correctas.
34 Capítulo 3. Técnicas de análisis predictivo empleadas Por último, la capa de salida está formada por una única neurona. Su función es realizar la suma ponderada de todas sus entradas, las cuales están unidas a la salida de las neuronas de la capa oculta. La ponderación viene dada por los pesos asociados a estas conexiones, W . Toda la red se podría condensar en la expresión 3.6, donde gn(X) hace referencia a la función de base radial asociada a la neurona n-ésima. ˆ Y= t ∑ n=1 Wngn(X)(3.6) Una vez construida la red es necesario llevar a cabo el aprendizaje de esta [ 51 ]. Durante este proceso, aprende de las muestras con salida conocida para, posteriormente, poder realizar predicciones minimizando el error cometido. En el aprendizaje se calcula [52]: el tamaño de la capa oculta, los pesos de unión entre las dos últimas capas, los prototipos que actúan de centro en cada neurona con función de base radial y el parámetro que define la anchura de cada campana. Una forma típica de seleccionar los prototipos para el centro de las neuronas es escogerlos al azar de entre todas las muestras con salida conocida. Otra alternativa más elaborada son las técnicas de clustering [ 53 ]; mediante la aplicación de estas, las observaciones similares se pueden agrupar y se obtiene un representante de cada grupo adecuado para ser usado como prototipo de cada neurona. Algunos algoritmos conocidos son k-means [ 54 ] o k-Harmonic [ 55 ]. También se puede recurrir al método conocido como mínimos cuadrados ortogonales [ 56 ], el cual construye la red de forma iterativa y al mismo tiempo otorga un valor a los pesos de las conexiones. Otras alternativas para el cálculo de los pesos son el uso de métodos de descenso del gradiente [ 57 ] mediante los que se busca reducir el error cometido, la aplicación de algoritmos de esperanzamaximización [58] o el uso de lógica difusa [59]. Para concluir, los comentarios que se realizaron en el método k-nn sobre el uso de variables categóricas y la necesidad de normalizar los datos también se aplican en esta ocasión. El motivo es que ambas técnicas tienen como base el cálculo de distancias.
4 Desempeño de las codificaciones propuestas En este capítulo se van a recoger los resultados obtenidos en dos problemas reales haciendo uso de las codificaciones previas. Uno tratará la clasificación de personas según su nivel económico y, el otro, la predicción del volumen de llamadas. Para cada problema se expone una descripción de los datos, los algoritmos y técnicas empleados, y la evaluación de los resultados. 4.1 Predicción del nivel económico 4.1.1 Descripción de los datos En este problema se va a realizar la clasificación en dos grupos de los miembros de una población según la cantidad de ingresos que generan en un año, en concreto, si esta cantidad supera o no el umbral de 50000 $. El conjunto de datos usado se puede encontrar en el repositorio público CensusIncome [60]. Estos datos se encuentran divididos en dos conjuntos y, tras unirlos y eliminar los registros incompletos, se dispone de la información de un total de 45222 personas. El número de variables, contando la de salida, es 15 y hay tanto categóricas como continuas. A continuación, se describen brevemente: •age: Edad en años. •capital-gain: Ganancia de capital. •capital-loss: Pérdida de capital. •hours-per-week: Número de horas a la semana. •fnlwgt : Pesos calculados por el creador del repositorio. Personas con valores similares poseen características demográficas semejantes. •education-num: Nivel de educación. •workclass: Tipo de trabajo que desempeña. Private (33307), Self-emp-not-inc (3796), Local-gov (3100), State-gov (1946), Selfemp-inc (1646), Federal-gov (1406), Without-pay (21). 35
42 Capítulo 4. Desempeño de las codificaciones propuestas En este caso, solo las dos últimas variables son categóricas, por lo que serán las únicas a las que se les aplique la codificación. Por otro lado, la salida del problema es el volumen de llamadas en el instante a predecir, tal y como se ha anunciado previamente. De nuevo, el conjunto de datos original ha sido dividido en dos. El 80% inicial para el entrenamiento, CE, y el 20% final restante para la validación, CP. En la figura 4.3 se ilustra la evolución del volumen de llamadas durante 4 semanas consecutivas, y se puede apreciar la existencia de un patrón que se repite a lo largo del tiempo. Los días están claramente marcados por un gran pico en el número de llamadas y las noches se reflejan por un descenso a 0. El lunes es el día que más llamadas se recibe y, hasta el viernes, el valor máximo diario no sufre gran variación. No obstante, los sábados y domingos se produce un gran descenso. Además, los dos máximos relativos presentes todos los días se producen casi siempre a las 12 y 18 horas. Tiempo 0 1 2 3 4 5 Volumen de llamadas 104 Figura 4.3 Evolución del volumen de llamadas entrantes durante tres semanas. 4.2.2 Procedimiento para la resolución Para realizar las predicciones se ha empleado una red neuronal de base radial como las descritas en el capítulo previo. Se han usado 30 neuronas en la capa oculta, y la función que implementan es una gaussiana basada en la distancia euclídea. Para el entrenamiento de la red se ha empleado el algoritmo conocido como least mean square (LMS) [ 65 ]. Además, todos los datos han sido normalizados haciendo uso de la misma expresión 4.1 usada en el problema previo. 4.2.3 Desempeño de las codificaciones Para evaluar y comparar los resultados se ha empleado el error porcentual medio absoluto (MAPE), expresión 4.5. El valor d es el tamaño del conjunto sobre el que se está evaluando el error, Y el valor real de la salida e ˆ Yel valor predicho de salida. MAPE =1 n d ∑ k=1|Yk−ˆ Yk| Yk(4.5)
4.2 Predicción del volumen de llamadas 43 A continuación, se describen las pruebas que se han realizado con las codificaciones estudiadas. Hay que tener que no todas ellas son de aplicación debido a que la variable de salida es continua. •Etiquetado : Las variables que representan el día de la semana y la naturaleza del día se combinan en una sola con los siguientes valores: 0.8 los días de lunes a viernes, 0.4 los sábados, y 0.25 los domingos y los días festivos. •Etiquetado Natural : Los días de la semana son sustituidos por números enteros consecutivos del 1 al 7 siguiendo el orden natural de la semana: el lunes es el 1 y el domingo el 7. Los días laborables se representan con el 1 y los festivos con el 2. •One-hot: Esta codificación es aplicada tal como se describe en la sección 2.2. •One-cold: Esta codificación es aplicada tal como se describe en la sección 2.2.1. •Rank-hot : Esta codificación es aplicada como se describe en la sección 2.2.2, para ello, se hace uso del orden natural de los días. •Binaria : Esta codificación es aplicada como se describe en la sección 2.3, para ello, se hace uso del orden natural de los días. •Gray : Esta codificación es aplicada como se describe en la sección 2.3.1, para ello, se hace uso del orden natural de los días. •E. Embeddings 1 : Esta codificación es aplicada como se describe en la sección 2.4; con la salvedad de que se ha entrenado una red por cada variable categórica, y no una única con todas las variables del problema. El tamaño de la capa de embeddings en cada variable es igual a la mitad de la cardinalidad de esta. •E. Embeddings 2 : Esta codificación es aplicada como se describe en la sección 2.4; con la salvedad de que se ha entrenado una red por cada variable categórica, y no una única con todas las variables del problema. El tamaño de la capa de embeddings en cada variable es igual a la cardinalidad de esta. •Estadística Sal. : Esta codificación es aplicada como se describe en la sección 2.5. Para la ponderación se ha usado la constante Λ=1 , por lo que solo se tiene en cuenta la probabilidad a posteriori. En la tabla 4.3 se recogen los resultados numéricos obtenidos. Las primeras 10 filas están referidas a la predicción del volumen de llamadas a la hora siguiente, y las otras 10 restantes, a la próxima semana. Se han resaltado las dos mejores opciones para cada tipo de predicción y conjunto. Las codificaciones por Etiquetado yEstadística Sal. destacan sobre todas las demás. El error cometido con estas es considerablemente inferior que con el resto. Es cierto que ambas producen resultados similares, pero hay que tener en cuenta que la forma en la que codifican las variables es muy diferente. En Etiquetado, los valores numéricos son obtenidos mediante el análisis manual de los datos seguido de un proceso de prueba y error. En cambio, en Estadística Sal., la codificación se realiza de forma automática. En la figura 4.4 se representa el volumen de llamadas predicho con la codificación Estadística Sal. y el volumen real. Los valores están normalizados, y el intervalo temporal mostrado corresponde al conjunto de pruebas. Se observa como las diferencias más notables se producen en los instantes de mayor actividad del día, estando casi siempre el volumen predicho por encima del real.
44 Capítulo 4. Desempeño de las codificaciones propuestas Tiempo -1 0 1 2 3 Vol. normalizado de llamadas Real Predicción Figura 4.4 Comparación del volumen de llamadas real y predicho. Tabla 4.3 Resultados de las codificaciones en la predicción del volumen de llamadas. CE CP Id. Codificación Tipo de predicción MAPE MAPE 1 Etiquetado Horaria 0.1490 0.1339 2 Etiquetado Natural Horaria 0.2193 0.2302 3 One-hot Horaria 0.2862 0.2988 4 One-cold Horaria 0.2862 0.2988 5 Rank-hot Horaria 0.2944 0.8072 6 Binaria Horaria 0.2242 0.2319 7 Gray Horaria 0.2486 0.2640 8 E. Embeddings 1 Horaria 0.2810 0.2872 9 E. Embeddings 2 Horaria 0.2156 0.1954 10 Estadística Sal. Horaria 0.1432 0.1278 11 Etiquetado Semanal 0.1508 0.1191 12 Etiquetado Natural Semanal 0.1620 0.1323 13 One-hot Semanal 0.2710 0.2237 14 One-cold Semanal 0.2710 0.2237 15 Rank-hot Semanal 0.2536 0.6773 16 Binaria Semanal 0.2267 0.1970 17 Gray Semanal 0.1934 0.1596 18 E. Embeddings 1 Semanal 0.2324 0.2005 19 E. Embeddings 2 Semanal 0.3127 0.2847 20 Estadística Sal. Semanal 0.1532 0.1202
5 Conclusiones El objetivo del trabajo consistía en el estudio de la representación numérica de las variables categóricas mediante técnicas de codificación, con el objetivo de poder extraer la información que estas condensan. Como resultado, se han descrito y probado 13 codificaciones. Es cierto que no hay ninguna que destaque en gran medida sobre las demás, pero entre todas ellas es posible afrontar situaciones de diferente índole: problemas con salida discreta, problemas con salida continua, variables categóricas de gran cardinalidad, variables categóricas con un orden implícito entre sus categorías, variables categóricas cuyos valores se pueden agrupar en conjuntos semejantes, etc. Además, en uno de los problemas tratados, concretamente el de las llamadas del centro de atención al cliente con la codificación basada en estadísticas de la variable de salida, se ha comprobado que el uso de estas técnicas puede igualar el resultado obtenido mediante la sustitución de las categorías por los números resultantes de un tedioso y lento estudio racional de los datos. Lo que significa que esta tarea puede ser sorteada mediante la aplicación de un algoritmo automático, facilitando así el desarrollo y aplicación de modelos de aprendizaje automático en problemas con variables categóricas. Por otro lado, las funciones en MATLAB ® para realizar las codificaciones se han programado para que sean aplicables a cualquier problema, y no únicamente a los tratados en las pruebas. Por tanto, se ha desarrollado una librería de funciones que puede ser aplicada con el único requisito de organizar los datos de una forma concreta. 5.1 Líneas futuras La continuación de este trabajo se puede abordar de diferentes formas. La más inmediata consiste en investigar y estudiar otros métodos de codificación no recogidos en este texto, o poner a prueba los ya presentados con nuevos problemas. Otra alternativa sería el análisis de la combinación de codificaciones. Es decir, en vez de utilizar la misma para todas las variable categóricas del problema, utilizar en cada variable aquella que sea más conveniente. Es más, la determinación de cuál es la codificación más idónea en cada una también puede ser objeto de estudio para continuar el trabajo. 45
46 Capítulo 5. Conclusiones Asimismo, puede ser útil reflexionar sobre técnicas para reducir la dimensionalidad de las codificaciones que provocan un incremento en el tamaño del vector de entrada. De este modo, se mejoraría el tratamiento de las variable categóricas de gran cardinalidad y, por tanto, el desempeño de los modelos predictivos. Por otro lado, podría ser interesante cambiar el enfoque en el tratamiento de las variables categóricas. En este trabajo se ha realizado mediante la codificación de las categorías, pero el estudio de otras alternativas también es atractivo. Por ejemplo, mediante funciones de cálculo de distancia adaptadas a variables categóricas, o mediante la combinación de modelos predictivos para resolver un problema. Esta última idea consiste en usar dos o más modelos, cada uno especializado en un tipo de variable, y combinar la salida de todos para obtener el resultado final del problema.
Anexo A Códigos implementados En este anexo se recogen las funciones implementadas en MATLAB ® para llevar a cabo las codificaciones estudiadas previamente. En la cabecera de cada una se puede consultar una breve descripción de la función y de sus entradas y salidas. Todas las funciones tienen dos entradas principales. La primera se llama datos, y corresponde con una matriz con una fila por cada observación y una columna por cada variable. La variable de la primera columna debe ser un identificador de cada observación, y la última columna, la salida del problema. En esta matriz, los valores de las variables categóricas son representados por números enteros. La segunda entrada principal es features. Esta consiste en una estructura de dimensión igual al número de columnas de la matriz de entrada datos, sin contar la primera con los identificadores. Sus campos son los siguientes: •nombre: Cadena de texto con el nombre de la variable. •tipo:’%f’ si la variable es continua o ’%s’ si la variable es categórica. •columna : Entero que indica la columna de la matriz datos en la que se encuentra la variable en cuestión. •valores : Cell de dimensión igual a la cardinalidad de la variable. En cada componente hay una cadena de texto con la representación textual de cada categoría. Este campo está vacío si la variable es continua. •nValores : Entero con la cardinalidad de la variable. Este campo está vacío si la variable es continua. •frecuencia : Vector de dimensión igual a la cardinalidad de la variable. En cada componente se indica el número de veces que aparece cada categoría. Este campo está vacío si la variable es continua. •frecuenciaRel: Igual que la anterior pero con la frecuencia relativa. •representacion : Vector de dimensión igual a la cardinalidad de la variable. En cada componente se indica el número entero con el que se representa cada categoría en la matriz datos. Este campo está vacío si la variable es continua. 47
48 Anexo A. Códigos implementados A.1 Codificaciones A.1.1 En clasificación y regresión Codificación one-hot Código A.1 onehot.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un archivo de datos ya procesado por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación one hot. 6% 7%Uso: 8% [datosCod,nombresCabecera,cabecera,features]=onehot(datos,features) 9% 10 %Entradas: 11 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 12 % realizado en main_cargarDatos.m 13 % 14 % features: Estructura con la información de cada variable dada por el 15 % preprocesamiento realizado en main_cargarDatos.m 16 % 17 %Salidas: 18 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 19 % como número de valores diferentes haya entre todas las variables 20 % categóricas. Más una columna para los índices. 21 % 22 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 23 % la variable de cada columna de la matriz datosCod. 24 % 25 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 26 % cual cada elemento contiene la tranformación númerica que se le 27 % asigna a cada valor de las variables categóricas. 28 % 29 % features: Sruct [1xN] donde cada elemento corresponde a una 30 % característica. Sus campos son: 31 %−nombre: String con el nombre de la característica. 32 %−tipo: String con el tipo de la característica. 33 %−nValores: Entero con el número de valores diferentes que puede 34 % adoptar cada característica. En el caso de ser continua está 35 % vacío. 36 %−valores: Para las continuas está vacío, pero en las 37 % categóricas es un cell [1 x nValores] que contiene los 38 % diferentes valores que puede adoptar la característica. 39 %−representacion: Para las continuas está vacío, pero en las 40 % categóricas es un vector [1 x nValores] que contiene los 41 % enteros usados para sustituir los diferentes valores que puede 42 % adoptar la característica. 43 %−frecuencias: Para las continuas está vacío, pero en las 44 % categóricas es un vector [1 x nValores] que contiene el número 45 % de veces que aparece cada valor. 46 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 47 %−columna: Vector [1 x nValores] donde los elementos indican la 48 % columna de datosCod que codifica cada valor que puede 49 % adoptar la variable categórica.
A.1 Codificaciones 49 50 % 51 %Notas: 52 % 53 % 54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 55 56 function [datosCod,nombresCabecera,cabecera,features]=onehot(datos,features) 57 % Se extrae la columna de los índices 58 indices=datos(:,1); 59 datos(:,1)=[]; 60 61 % Se determinan las dimensiones de la matriz de entradas 62 [nMuestras,nVariables]=size(datos); 63 64 % Se determinan las columnas que corresponden a cada valor de las variables 65 N=1; 66 for ii=1:nVariables 67 if strcmp(features(ii).tipo,'%s') 68 features(ii).columna=N:(features(ii).nValores+N−1); 69 N=N+features(ii).nValores; 70 elseif strcmp(features(ii).tipo,'%f') 71 features(ii).columna=[]; 72 end 73 end 74 N=N−1; 75 76 % Se iniciliza la matriz de datosCod 77 datosCod=−1*ones(nMuestras,N); 78 79 % Para los valores de cada variable se construye la matriz de datos codificados 80 for ii=1:nVariables 81 % Si la variable es categórica 82 if strcmp(features(ii).tipo,'%s') 83 % Se construye la codificación para dicha variable 84 datosCod(:,features(ii).columna)=full(ind2vec(datos(:,ii)',features(ii).nValores)←- )'; 85 end 86 87 end 88 89 % Se añade el índice de cada muestra como primera columna 90 datosCod=[indices datosCod]; 91 92 % Se actualiza el campo columna de la estructura features debido a la 93 % inclusión del índice 94 for iVariable=1:nVariables 95 features(iVariable).columna=features(iVariable).columna+1; 96 end 97 98 % Se procede a calcular las salidas cabecera y nombresCabecera 99 nombresCabecera=cell(1+N,1); 100 cabecera=cell(1,N+1); 101 102 % La primera posición corresponde al índice 103 nombresCabecera{1}={'1 −−> Índice'}; 104 cabecera{1}='Int'; 105 106 % Se completa el resto de variables 107 for iVariable=1:nVariables 108 if strcmp(features(iVariable).tipo,'%s') 109 for iValor=1:features(iVariable).nValores 110 nombresCabecera{features(iVariable).columna(iValor)}=...
50 Anexo A. Códigos implementados 111 sprintf('%d −−> is_%s',features(iVariable).columna(iValor),features(←- iVariable).valores{iValor}); 112 cabecera{features(iVariable).columna(iValor)}='Bool'; 113 end 114 end 115 end 116 117 end Codificación one-cold Código A.2 onecold.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma una base de conocimiento ya procesada por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación one cold. 6% 7%Uso: 8% [datosCod,nombresCabecera,cabecera,features]=onehot(datos,features) 9% 10 %Entradas: 11 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 12 % realizado en main_cargarDatos.m 13 % 14 % features: Estructura con la información de cada variable dada por el 15 % preprocesamiento realizado en main_cargarDatos.m 16 % 17 %Salidas: 18 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 19 % como número de valores diferentes haya entre todas las variables 20 % categóricas. Más una columna para los índices. 21 % 22 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 23 % la variable de cada columna de la matriz datosCod. 24 % 25 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 26 % cual cada elemento contiene la tranformación númerica que se le 27 % asigna a cada valor de las variables categóricas. 28 % 29 % features: Sruct [1xN] donde cada elemento corresponde a una 30 % característica. Sus campos son: 31 %−nombre: String con el nombre de la característica. 32 %−tipo: String con el tipo de la característica. 33 %−nValores: Entero con el número de valores diferentes que puede 34 % adoptar cada característica. En el caso de ser continua está 35 % vacío. 36 %−valores: Para las continuas está vacío, pero en las 37 % categóricas es un cell [1 x nValores] que contiene los 38 % diferentes valores que puede adoptar la característica. 39 %−representacion: Para las continuas está vacío, pero en las 40 % categóricas es un vector [1 x nValores] que contiene los 41 % enteros usados para sustituir los diferentes valores que puede 42 % adoptar la característica. 43 %−frecuencias: Para las continuas está vacío, pero en las 44 % categóricas es un vector [1 x nValores] que contiene el número 45 % de veces que aparece cada valor. 46 %−frecuenciaRel: Igual que el anterior pero en tanto por cien.
A.1 Codificaciones 51 47 %−columna: Vector [1 x nValores] donde los elementos indican la 48 % columna de datosCod que codifica cada valor que puede 49 % adoptar la variable categórica. 50 % 51 %Notas: 52 % 53 % 54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 55 56 function [datosCod,nombresCabecera,cabecera,features]=onecold(datos,features) 57 % Se extrae la columna de los índices 58 indices=datos(:,1); 59 datos(:,1)=[]; 60 61 % Se determinan las dimensiones de la matriz de entradas 62 [nMuestras,nVariables]=size(datos); 63 64 % Se determinan las columnas que corresponden a cada valor de las variables 65 N=1; 66 for ii=1:nVariables 67 if strcmp(features(ii).tipo,'%s') 68 features(ii).columna=N:(features(ii).nValores+N−1); 69 N=N+features(ii).nValores; 70 elseif strcmp(features(ii).tipo,'%f') 71 features(ii).columna=[]; 72 end 73 end 74 N=N−1; 75 76 % Se iniciliza la matriz de datosCod 77 datosCod=−1*ones(nMuestras,N); 78 79 % Para los valores de cada variable se construye la matriz de datos codificados 80 for ii=1:nVariables 81 % Si la variable es categórica 82 if strcmp(features(ii).tipo,'%s') 83 % Se construye la codificación para dicha variable 84 datosCod(:,features(ii).columna)=abs(full(ind2vec(datos(:,ii)',features(ii).←- nValores))'−1); 85 end 86 87 end 88 89 % Se añade el índice de cada muestra como primera columna 90 datosCod=[indices datosCod]; 91 92 % Se actualiza el campo columna de la estructura features debido a la 93 % inclusión del índice 94 for iVariable=1:nVariables 95 features(iVariable).columna=features(iVariable).columna+1; 96 end 97 98 % Se procede a calcular las salidas cabecera y nombresCabecera 99 nombresCabecera=cell(1+N,1); 100 cabecera=cell(1,N+1); 101 102 % La primera posición corresponde al índice 103 nombresCabecera{1}={'1 −−> Índice'}; 104 cabecera{1}='Int'; 105 106 % Se completa el resto de variables 107 for iVariable=1:nVariables
58 Anexo A. Códigos implementados 45 % categóricas es un vector [1 x nValores] que contiene los 46 % enteros usados para sustituir los diferentes valores que puede 47 % adoptar la característica. 48 %−frecuencias: Para las continuas está vacío, pero en las 49 % categóricas es un vector [1 x nValores] que contiene el número 50 % de veces que aparece cada valor. 51 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 52 %−columna: Vector [1 x ceil(log2(nValores))] donde los elementos 53 % indican las columnas de datosCod que codifican los valores de 54 % la variable en cuestión. 55 %−representacionBinaria: Para las continuas está vacío, pero en las 56 % categóricas es un vector [1 x nValores] que contiene enteros 57 % cuya representación en gray sustituye los diferentes valores 58 % que puede adoptar la característica. 59 % 60 %Notas: 61 % 62 % 63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 64 65 function [datosCod,nombresCabecera,cabecera,features]=gray(datos,features) 66 % Se extrae la columna de los índices 67 indices=datos(:,1); 68 datos(:,1)=[]; 69 70 % Se determinan las dimensiones de la matriz de entradas 71 [nMuestras,nVariables]=size(datos); 72 73 % Se determinan las columnas que corresponden a cada valor de las variables 74 % También se añade la representación en decimal cuya representación binaria 75 % se usará para cada valor de las variables 76 N=1; 77 for ii=1:nVariables 78 if strcmp(features(ii).tipo,'%s') 79 nColumnas=ceil(log2(features(ii).nValores)); 80 features(ii).columna=N:(nColumnas+N−1); 81 N=N+nColumnas; 82 features(ii).representacionBinaria=features(ii).representacion−1; 83 elseif strcmp(features(ii).tipo,'%f') 84 features(ii).columna=[]; 85 features(ii).representacionBinaria=[]; 86 end 87 end 88 N=N−1; 89 90 % Se iniciliza la matriz de datosCod 91 datosCod=−1*ones(nMuestras,N); 92 93 % Con los valores de cada variable se construye la matriz de datos codificados 94 for ii=1:nVariables 95 % Si la variable es categórica 96 if strcmp(features(ii).tipo,'%s') 97 % Se transforman los valores de la variable a binario 98 auxCod=dec2bin(datos(:,ii)−1); 99 % Se transforman los valores binarios a gray 100 auxCod = bin2gray(auxCod); 101 102 % Cada columna de la matriz resultado se contruye con un bit 103 % resultante de la transformación. Se comienza por el más 104 % significativo. 105 for iBit=1:size(auxCod,2) 106 datosCod(:,features(ii).columna(iBit))=auxCod(:,iBit);
A.1 Codificaciones 59 107 end 108 end 109 110 end 111 112 % Se añade el índice de cada muestra como primera columna 113 datosCod=[indices datosCod]; 114 115 % Se actualiza el campo columna de la estructura features debido a la 116 % inclusión del índice 117 for iVariable=1:nVariables 118 features(iVariable).columna=features(iVariable).columna+1; 119 end 120 121 % Se procede a calcular las salidas cabecera y nombresCabecera 122 nombresCabecera=cell(1+N,1); 123 cabecera=cell(1,N+1); 124 125 % La primera posición corresponde al índice 126 nombresCabecera{1}={'1 −−> Índice'}; 127 cabecera{1}='Int'; 128 129 % Se completan cabecera y nombresCabecera para el resto de variables 130 for iVariable=1:nVariables 131 % Si la variable es categórica 132 if strcmp(features(iVariable).tipo,'%s') 133 134 % iBit es un índice del bit en la codificación de cada variable 135 iBit=1; 136 for iCol=features(iVariable).columna 137 % Se completan ambas salidas para cada columna resultante de 138 % la codificación de la variable 139 nombresCabecera{iCol}=sprintf(... 140 '%d −−> %dgray_%s',iCol,iBit,features(iVariable).nombre); 141 cabecera{iCol}='Bool'; 142 143 % Se incrementa el índice para el siguiente bit de esa misma 144 % variable 145 iBit=iBit+1; 146 end 147 148 end 149 end 150 151 end 152 153 function gray = bin2gray(binstr) 154 % Transforma un vector fila de caracteres representando un número 155 % binario a un vector numérico con el mismo número pero en gray 156 % Si la entrada es una matriz de string, considera cada columna como un 157 % bit y cada fila como un número diferente. 158 bin=zeros(size(binstr)); 159 for i=1:size(binstr,2) 160 bin(:,i)=str2num(binstr(:,i)); 161 end 162 gray = bitxor(bin(:,2:end),bin(:,1:(end−1))); 163 gray = [bin(:,1) gray]; 164 165 end
60 Anexo A. Códigos implementados Codificación basada en la frecuencia Código A.6 frequency.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un conjunto de datos ya procesados por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación basada en la frecuencia relativa de cada categoría. 6% 7%Uso: 8% [datosCod,nombresCabecera,cabecera,features]=frequency(datos,features) 9% 10 %Entradas: 11 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 12 % realizado en main_cargarDatos.m 13 % 14 % features: Estructura con la información de cada variable dada por el 15 % preprocesamiento realizado en main_cargarDatos.m 16 % 17 %Salidas: 18 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 19 % como variables categóricas exitan en la matriz de entrada datos. Más 20 % una columna para los índices. 21 % 22 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 23 % la variable de cada columna de la matriz datosCod. 24 % 25 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 26 % cual cada elemento contiene la transformación numérica que se le 27 % asigna a cada valor de las variables categóricas. 28 % 29 % features: Sruct [1xN] donde cada elemento corresponde a una 30 % característica. Sus campos son: 31 %−nombre: String con el nombre de la característica. 32 %−tipo: String con el tipo de la característica. 33 %−nValores: Entero con el número de valores diferentes que puede 34 % adoptar cada característica. En el caso de ser continua está 35 % vacío. 36 %−valores: Para las continuas está vacío, pero en las 37 % categóricas es un cell [1 x nValores] que contiene los 38 % diferentes valores que puede adoptar la característica. 39 %−representacion: Para las continuas está vacío, pero en las 40 % categóricas es un vector [1 x nValores] que contiene los 41 % enteros usados para sustituir los diferentes valores que puede 42 % adoptar la característica. 43 %−frecuencias: Para las continuas está vacío, pero en las 44 % categóricas es un vector [1 x nValores] que contiene el número 45 % de veces que aparece cada valor. 46 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 47 %−columna: Entero que indica la columna de datosCod que contiene la 48 % codificación de la variable categórica. Para las continuas está 49 % vacío. 50 %Notas: 51 % 52 % 53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 54 55 56 function [datosCod,nombresCabecera,cabecera,features]=frequency(datos,features)
A.1 Codificaciones 61 57 % Se extrae la columna de los índices 58 indices=datos(:,1); 59 datos(:,1)=[]; 60 61 % Se determinan las dimensiones de la matriz de entradas 62 [nMuestras,nVariables]=size(datos); 63 64 % Se determina la columna de la matriz de salida datosCod que tendrá la 65 % codificación de cada variable categórica original. 66 N=1; 67 for iVar=1:nVariables 68 if strcmp(features(iVar).tipo,'%s') 69 features(iVar).columna=N; 70 N=N+1; 71 elseif strcmp(features(iVar).tipo,'%f') 72 features(iVar).columna=[]; 73 end 74 end 75 N=N−1; 76 77 % Se iniciliza la matriz de datosCod 78 datosCod=−1*ones(nMuestras,N); 79 80 % Se pasa a realizar el cálculo de la codificación 81 % Para cada una de las variables 82 for iVar=1:nVariables 83 % Si la variable es categórica 84 if strcmp(features(iVar).tipo,'%s') 85 86 % Para cada uno de los valores que puede adoptar la variable 87 for iValor=1:features(iVar).nValores 88 89 % Se determina el índice de las muestras con dicho valor 90 iMuestrasValor=datos(:,iVar)==features(iVar).representacion(iValor); 91 92 % Se atualiza la salida datosCod haciendo uso de la 93 % frecuencia relativa de aparición de dicho valor 94 datosCod(iMuestrasValor,features(iVar).columna)=features(iVar).frecuenciaRel(←- iValor); 95 end 96 97 end 98 end 99 100 % Se añade el índice de cada muestra como primera columna 101 datosCod=[indices datosCod]; 102 103 % Se actualiza el campo columna de la estructura features debido a la 104 % inclusión del índice 105 for iVar=1:nVariables 106 features(iVar).columna=features(iVar).columna+1; 107 end 108 109 % Se procede a calcular las salidas cabecera y nombresCabecera 110 nombresCabecera=cell(1+N,1); 111 cabecera=cell(1,N+1); 112 113 % La primera posición corresponde al índice 114 nombresCabecera{1}={'1 −−> Índice'}; 115 cabecera{1}='Int'; 116 117 % Se completan cabecera y nombresCabecera para el resto de variables
62 Anexo A. Códigos implementados 118 for iVar=1:nVariables 119 % Si la variable es categórica 120 if strcmp(features(iVar).tipo,'%s') 121 % Se inicializa cada componente de la cabecera con un cell de 122 % tamaño igual al número de valores 123 col=features(iVar).columna; 124 cabecera{col}=cell(features(iVar).nValores,1); 125 126 % En cada componente del cell creado se indica la transformación 127 % numérica llevada a cabo 128 for iValor=1:features(iVar).nValores 129 cabecera{col}{iValor}=[features(iVar).valores{iValor} '−−>'num2str(←- features(iVar).frecuenciaRel(iValor))]; 130 end 131 132 % Se completa nombresCabecera con un nombre para cada nueva 133 % variable 134 nombresCabecera{col}=[num2str(col) '−−> freq_'features(iVar).nombre]; 135 136 end 137 end 138 139 140 end A.1.2 En clasificación Codificación mediante entity embeddings Código A.7 entityEmbedding.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un conjunto de datos ya procesados por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación basada en entity embedding 6% 7%Para cada una de las variables, el tamaño de la capa oculta y, por tanto, 8%el de variables resultantes para codificar la original, será indicado en 9%la entrada hiddenLayerSizes. Si esta entrada está vacía, se usará como 10 %tamaño el resultado de redondear la semisuma de 1 más el número de 11 %categorías de la variable. 12 % 13 %Uso: 14 % 15 % [datosCod,nombresCabecera,cabecera,features]=entityEmbedding(datos,features,←- hiddenLayerSizes,idCod) 16 % 17 %Entradas: 18 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 19 % realizado en main_cargarDatos.m 20 % 21 % features: Estructura con la información de cada variable dada por el 22 % preprocesamiento realizado en main_cargarDatos.m 23 % 24 % hiddenLayerSizes: Vector de enteros de tamaño igual al número de 25 % variables categóricas que indica el tamaño de la capa oculta de la
A.1 Codificaciones 63 26 % red usada para codificar cada una de las variables. Si está vacío 27 % se calcula mediante la expresión previa. 28 % 29 %Salidas: 30 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 31 % como neuronas totales se hayan usado en la capa oculta de todas las 32 % variables categóricas. Más una columna para los índices. 33 % 34 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 35 % la variable de cada columna de la matriz datosCod. 36 % 37 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 38 % cual cada elemento contiene la transformación numérica que se le 39 % asigna a cada valor de las variables categóricas. 40 % 41 % features: Sruct [1xN] donde cada elemento corresponde a una 42 % característica. Sus campos son: 43 %−nombre: String con el nombre de la característica. 44 %−tipo: String con el tipo de la característica. 45 %−nValores: Entero con el número de valores diferentes que puede 46 % adoptar cada característica. En el caso de ser continua está 47 % vacío. 48 %−valores: Para las continuas está vacío, pero en las 49 % categóricas es un cell [1 x nValores] que contiene los 50 % diferentes valores que puede adoptar la característica. 51 %−representacion: Para las continuas está vacío, pero en las 52 % categóricas es un vector [1 x nValores] que contiene los 53 % enteros usados para sustituir los diferentes valores que puede 54 % adoptar la característica. 55 %−frecuencias: Para las continuas está vacío, pero en las 56 % categóricas es un vector [1 x nValores] que contiene el número 57 % de veces que aparece cada valor. 58 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 59 %−columna: Entero que indica las columnas de datosCod que contienen la 60 % codificación de la variable categórica. Para las continuas está 61 % vacío. 62 %−codificacion: Para las continuas está vacío, pero en las 63 % categóricas es un cell [1 x length(columna)] que contiene los 64 % números con los que se sustituye cada posible valor de la variable. 65 % 66 %Notas: 67 % 68 % 69 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 70 71 function [datosCod,nombresCabecera,cabecera,features]=entityEmbedding(datos,features,←- hiddenLayerSizes,idCod) 72 73 % Se extrae la columna de los índices 74 indices=datos(:,1); 75 datos(:,1)=[]; 76 77 % Se determinan las dimensiones de la matriz de entradas 78 [nMuestras,nVariables]=size(datos); 79 80 % Se determinan las columnas de la matriz de salida datosCod que tendrán la 81 % codificación de cada variable categórica original. 82 % El número de columnas esta especificado por hiddenLayerSizes. 83 % Si este vector está vacío se usa el número medio entre el tamaño de la 84 % capa de entrada y de salida. 85 N=0; 86 for iVar=1:nVariables
64 Anexo A. Códigos implementados 87 if strcmp(features(iVar).tipo,'%s') 88 if ~isempty(hiddenLayerSizes) 89 tam=hiddenLayerSizes(1); 90 hiddenLayerSizes(1)=[]; 91 else 92 tam=round((1+features(iVar).nValores)/2); 93 end 94 features(iVar).columna=(1:tam)+N; 95 N=features(iVar).columna(end); 96 elseif strcmp(features(iVar).tipo,'%f') 97 features(iVar).columna=[]; 98 end 99 end 100 101 % Se iniciliza la matriz de datosCod 102 datosCod=−1*ones(nMuestras,N); 103 104 % Se realiza la codificación one−hot de la salida 105 t=full(ind2vec(datos(:,end)',features(end).nValores)); 106 107 % Se pasa a realizar el cálculo de la codificación 108 % Para cada una de las variables 109 for iVar=1:nVariables 110 % Si la variable es categórica 111 if strcmp(features(iVar).tipo,'%s') 112 % Se inicializa el campo de codificacion 113 features(iVar).codificacion=cell(1,features(iVar).nValores); 114 115 % Se realiza la codificación one−hot de la variable 116 x=full(ind2vec(datos(:,iVar)',features(iVar).nValores)); 117 118 % Se inicializa la red con el tamaño de la capa oculta adecuado 119 net = patternnet(length(features(iVar).columna), 'trainscg'); 120 121 % Se determina la división de los datos 122 net.divideParam.trainRatio = 70/100; 123 net.divideParam.valRatio = 15/100; 124 net.divideParam.testRatio = 15/100; 125 126 % Se entrena la red 127 net = train(net,x,t); 128 129 % Se recogen los pesos necesarios para la codificación 130 weights=net.IW{1}'; 131 clear net x 132 133 % Para cada una de las categorías de la variable 134 for iValor=1:features(iVar).nValores 135 % Se rellena el campo codificacion de la estructura 136 features(iVar).codificacion{iValor}=weights(iValor,:); 137 138 % Se completa la salida datosCod 139 % Se buscan las muestras de cada categoría y se rellena cada 140 % una de las columnas 141 indices_valor=features(iVar).representacion(iValor)==datos(:,iVar); 142 for iCol=1:length(features(iVar).columna) 143 datosCod(indices_valor,features(iVar).columna(iCol))=features(iVar).←- codificacion{iValor}(iCol); 144 end 145 end 146 147 % Si la variable no es categórica
A.1 Codificaciones 65 148 elseif strcmp(features(iVar).tipo,'%f') 149 % El campo codificación estará vacío 150 features(iVar).codificacion=[]; 151 end 152 end 153 154 % Se libera memoria 155 clear indices_valor weights t datos 156 157 % Se añade el índice de cada muestra como primera columna 158 datosCod=[indices datosCod]; 159 clear indices 160 161 % Se actualiza el campo columna de la estructura features debido a la 162 % inclusión del índice 163 for iVar=1:nVariables 164 features(iVar).columna=features(iVar).columna+1; 165 end 166 167 % Se procede a calcular las salidas cabecera y nombresCabecera 168 nombresCabecera=cell(1+N,1); 169 cabecera=cell(1,N+1); 170 171 % La primera posición corresponde al índice 172 nombresCabecera{1}={'1 −−> Índice'}; 173 cabecera{1}='Int'; 174 175 % Se completan cabecera y nombresCabecera para el resto de variables 176 for iVar=1:nVariables 177 % Si la variable es categórica 178 if strcmp(features(iVar).tipo,'%s') 179 % Para cada columna de la matriz datosCod 180 for iCol=1:length(features(iVar).columna) 181 col=features(iVar).columna(iCol); 182 cabecera{col}=cell(features(iVar).nValores,1); 183 184 % Se describe el valor que adopta cada categoría 185 for iValor=1:features(iVar).nValores 186 cabecera{col}{iValor}=[features(iVar).valores{iValor} '−−>'num2str(←- features(iVar).codificacion{iValor}(iCol))]; 187 end 188 189 % Se completa nombresCabecera con un nombre para cada nueva 190 % variable 191 nombresCabecera{col}=[num2str(col) '−−> ee' num2str(idCod) '_'num2str(iCol)←- 'w_'features(iVar).nombre]; 192 end 193 194 end 195 end 196 197 198 end
66 Anexo A. Códigos implementados Codificación basada en estadísticas de la variable de salida dicotómica Código A.8 probabilityBinaryTarget.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un conjunto de datos ya procesados por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación basada en la probabilidad a priori y posteriori de la salida 6%condicionada por el valor de la variable categórica. 7% 8%Uso: 9% [datosCod,nombresCabecera,cabecera,features]=probabilityBinaryTarget(datos,features,←- salida1) 10 % 11 %Entradas: 12 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 13 % realizado en main_cargarDatos.m 14 % 15 % features: Estructura con la información de cada variable dada por el 16 % preprocesamiento realizado en main_cargarDatos.m 17 % 18 % salida1: Valor de la variable de salida que se considera para el 19 % cálculo de las probabilidades. 20 % 21 % lambda: Función anónima que se usará para el cálculo de los pesos de 22 % las probabilidades a priori y a posteriori. Su única variable de 23 % entrada es el número de veces que aparece un determinado valor de 24 % una variable categórica. 25 % 26 % idCod: Entero usado para ser añadido en nombresCabecera ya que la 27 % codificación no es única (depende de la función lambda). 28 % 29 %Salidas: 30 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 31 % como variables categóricas exitan en la matriz de entrada datos. Más 32 % una columna para los índices. 33 % 34 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 35 % la variable de cada columna de la matriz datosCod. 36 % 37 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 38 % cual cada elemento contiene la tranformación númerica que se le 39 % asigna a cada valor de las variables categóricas. 40 % 41 % features: Sruct [1xN] donde cada elemento corresponde a una 42 % característica. Sus campos son: 43 %−nombre: String con el nombre de la característica. 44 %−tipo: String con el tipo de la característica. 45 %−nValores: Entero con el número de valores diferentes que puede 46 % adoptar cada característica. En el caso de ser continua está 47 % vacío. 48 %−valores: Para las continuas está vacío, pero en las 49 % categóricas es un cell [1 x nValores] que contiene los 50 % diferentes valores que puede adoptar la característica. 51 %−representacion: Para las continuas está vacío, pero en las 52 % categóricas es un vector [1 x nValores] que contiene los 53 % enteros usados para sustituir los diferentes valores que puede 54 % adoptar la característica. 55 %−frecuencias: Para las continuas está vacío, pero en las
A.1 Codificaciones 67 56 % categóricas es un vector [1 x nValores] que contiene el número 57 % de veces que aparece cada valor. 58 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 59 %−columna: Entero que indica la columna de datosCod que contiene la 60 % codificación de la variable categórica. Para las continuas está 61 % vacío. 62 %−codificacion: Para las continuas está vacío, pero en las 63 % categóricas es un vector [1 x nValores] que contiene el número 64 % con el que se sustituye cada posible valor de la variable. 65 % 66 %Notas: 67 % 68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 69 70 function [datosCod,nombresCabecera,cabecera,features]=probabilityBinaryTarget(datos,←- features,salida1,lambda,idCod) 71 % Se extrae la columna de los índices 72 indices=datos(:,1); 73 datos(:,1)=[]; 74 75 % Se determinan las dimensiones de la matriz de entradas 76 [nMuestras,nVariables]=size(datos); 77 78 % Se determina la columna de la matriz de salida datosCod que tendrá la 79 % codificación de cada variable categórica original. 80 N=1; 81 for iVar=1:nVariables 82 if strcmp(features(iVar).tipo,'%s') 83 features(iVar).columna=N; 84 N=N+1; 85 elseif strcmp(features(iVar).tipo,'%f') 86 features(iVar).columna=[]; 87 end 88 end 89 N=N−1; 90 91 % Se iniciliza la matriz de datosCod 92 datosCod=−1*ones(nMuestras,N); 93 94 % Se determinan los índices de las muestras con salida igual a salida1 95 iSalida1=datos(:,end)==salida1; 96 97 % Se determina la probabilidad a priori 98 prioriP=sum(iSalida1)/nMuestras; 99 100 % Se pasa a realizar el cálculo de la codificación 101 % Para cada una de las variables 102 for iVar=1:nVariables 103 % Si la variable es categórica 104 if strcmp(features(iVar).tipo,'%s') 105 % Se inicializa el campo de codificacion 106 features(iVar).codificacion=−1*ones(1,features(iVar).nValores); 107 108 % Para cada uno de los valores que puede adoptar la variable 109 for iValor=1:features(iVar).nValores 110 valor=features(iVar).representacion(iValor); 111 112 % Se determina el índice de las muestras con dicho valor 113 iMuestrasValor=datos(:,iVar)==valor; 114 % Se calcula la probabilidad a posteriori 115 posteriorP=sum(and(iMuestrasValor,iSalida1))/sum(iMuestrasValor); 116 % Se calcula la codificación mediante el uso de las dos
74 Anexo A. Códigos implementados 117 cod=Goods/Bads; 118 119 % Se actualiza el campo codificacion para dicho valor 120 features(iVar).codificacion(iValor)=cod; 121 122 % Se atualiza la salida datosCod haciendo uso de la 123 % codificación calculada 124 datosCod(iMuestrasValor,features(iVar).columna)=cod; 125 end 126 127 % Si la variable no es categórica 128 elseif strcmp(features(iVar).tipo,'%f') 129 % El campo codificación estará vacío 130 features(iVar).codificacion=[]; 131 end 132 133 end 134 135 % Se añade el índice de cada muestra como primera columna 136 datosCod=[indices datosCod]; 137 138 % Se actualiza el campo columna de la estructura features debido a la 139 % inclusión del índice 140 for iVar=1:nVariables 141 features(iVar).columna=features(iVar).columna+1; 142 end 143 144 % Se procede a calcular las salidas cabecera y nombresCabecera 145 nombresCabecera=cell(1+N,1); 146 cabecera=cell(1,N+1); 147 148 % La primera posición corresponde al índice 149 nombresCabecera{1}={'1 −−> Índice'}; 150 cabecera{1}='Int'; 151 152 % Se completan cabecera y nombresCabecera para el resto de variables 153 for iVar=1:nVariables 154 % Si la variable es categórica 155 if strcmp(features(iVar).tipo,'%s') 156 % Se inicializa cada componente de la cabecera con un cell de 157 % tamaño igual al número de valores 158 col=features(iVar).columna; 159 cabecera{col}=cell(features(iVar).nValores,1); 160 161 % En cada componente del cell creado se indica la transformación 162 % numérica llevada a cabo 163 for iValor=1:features(iVar).nValores 164 cabecera{col}{iValor}=[features(iVar).valores{iValor} '−−>'num2str(←- features(iVar).codificacion(iValor))]; 165 end 166 167 % Se completa nombresCabecera con un nombre para cada nueva 168 % variable 169 nombresCabecera{col}=[num2str(col) '−−> pr_'features(iVar).nombre]; 170 171 end 172 173 end 174 175 176 end
A.1 Codificaciones 75 Codificación por similitud Código A.11 similarity.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma una base de conocimiento ya procesada por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación similarity. 6% 7%Uso: 8% [datosCod,nombresCabecera,cabecera,features]=similarity(datos,features) 9% 10 %Entradas: 11 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 12 % realizado en main_cargarDatos.m 13 % 14 % features: Estructura con la información de cada variable dada por el 15 % preprocesamiento realizado en main_cargarDatos.m 16 % 17 %Salidas: 18 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 19 % como número de valores diferentes haya entre todas las variables 20 % categóricas. Más una columna para los índices. 21 % 22 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 23 % la variable de cada columna de la matriz datosCod. 24 % 25 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 26 % cual cada elemento contiene la tranformación númerica que se le 27 % asigna a cada valor de las variables categóricas. 28 % 29 % features: Sruct [1xN] donde cada elemento corresponde a una 30 % característica. Sus campos son: 31 %−nombre: String con el nombre de la característica. 32 %−tipo: String con el tipo de la característica. 33 %−nValores: Entero con el número de valores diferentes que puede 34 % adoptar cada característica. En el caso de ser continua está 35 % vacío. 36 %−valores: Para las continuas está vacío, pero en las 37 % categóricas es un cell [1 x nValores] que contiene los 38 % diferentes valores que puede adoptar la característica. 39 %−representacion: Para las continuas está vacío, pero en las 40 % categóricas es un vector [1 x nValores] que contiene los 41 % enteros usados para sustituir los diferentes valores que puede 42 % adoptar la característica. 43 %−frecuencia: Para las continuas está vacío, pero en las 44 % categóricas es un vector [1 x nValores] que contiene el número 45 % de veces que aparece cada valor. 46 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 47 %−columna: Vector [1 x nValores] donde los elementos indican la 48 % columna de datosCod que codifica cada valor que puede 49 % adoptar la variable categórica. 50 % 51 %Notas: 52 % 53 % 54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 55 56 function [datosCod,nombresCabecera,cabecera,features]=similarity(datos,features)
76 Anexo A. Códigos implementados 57 % Se extrae la columna de los índices 58 indices=datos(:,1); 59 datos(:,1)=[]; 60 61 % Se determinan las dimensiones de la matriz de entradas 62 [nMuestras,nVariables]=size(datos); 63 64 % Se determinan las columnas que corresponden a cada valor de las variables 65 N=1; 66 for ii=1:nVariables 67 if strcmp(features(ii).tipo,'%s') 68 features(ii).columna=N:(features(ii).nValores+N−1); 69 N=N+features(ii).nValores; 70 elseif strcmp(features(ii).tipo,'%f') 71 features(ii).columna=[]; 72 end 73 end 74 N=N−1; 75 76 % Se iniciliza la matriz de datosCod 77 datosCod=−1*ones(nMuestras,N); 78 79 % Para los valores de cada variable se construye la matriz de datos codificados 80 for ii=1:nVariables 81 % Si la variable es categórica 82 if strcmp(features(ii).tipo,'%s') 83 % Se construye la codificación para dicha variable 84 auxCod = zeros(nMuestras,features(ii).nValores); 85 86 vecSim = zeros(1,features(ii).nValores); 87 for iValMuestra = features(ii).representacion 88 for iValSim = features(ii).representacion 89 if iValMuestra == iValSim 90 vecSim(iValSim) = 1; 91 else 92 vecSim(iValSim) = 1./(1 + log(nMuestras./features(ii).frecuencia(←- iValMuestra))*log(nMuestras./features(ii).frecuencia(iValSim))); 93 end 94 end 95 auxCod(datos(:,ii)==iValMuestra,:) =... 96 repmat(vecSim,features(ii).frecuencia(iValMuestra),1); 97 end 98 99 datosCod(:,features(ii).columna)= auxCod; 100 end 101 102 end 103 104 % Se añade el índice de cada muestra como primera columna 105 datosCod=[indices datosCod]; 106 107 % Se actualiza el campo columna de la estructura features debido a la 108 % inclusión del índice 109 for iVariable=1:nVariables 110 features(iVariable).columna=features(iVariable).columna+1; 111 end 112 113 % Se procede a calcular las salidas cabecera y nombresCabecera 114 nombresCabecera=cell(1+N,1); 115 cabecera=cell(1,N+1); 116 117 % La primera posición corresponde al índice
A.1 Codificaciones 77 118 nombresCabecera{1}={'1 −−> Índice'}; 119 cabecera{1}='Int'; 120 121 % Se completa el resto de variables 122 for iVariable=1:nVariables 123 if strcmp(features(iVariable).tipo,'%s') 124 for iValor=1:features(iVariable).nValores 125 nombresCabecera{features(iVariable).columna(iValor)}=... 126 sprintf('%d −−> sim_%s',features(iVariable).columna(iValor),features(←- iVariable).valores{iValor}); 127 cabecera{features(iVariable).columna(iValor)}='Float'; 128 end 129 end 130 end 131 132 end A.1.3 En regresión Codificación mediante entity embeddings Código A.12 entityEmbedding.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un conjunto de datos ya procesados por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación basada en entity embedding 6% 7%Para cada una de las variables, el tamaño de la capa oculta y, por tanto, 8%el de variables resultantes para codificar la original, será indicado en 9%la entrada hiddenLayerSizes. Si esta entrada está vacía, se usará como 10 %tamaño el resultado de redondear la semisuma de 1 más el número de 11 %categorías de la variable. 12 % 13 %Uso: 14 % 15 % [datosCod,nombresCabecera,cabecera,features]=entityEmbedding(datos,features,salida,←- hiddenLayerSizes,idCod) 16 % 17 %Entradas: 18 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 19 % realizado en main_cargarDatos.m 20 % 21 % features: Estructura con la información de cada variable dada por el 22 % preprocesamiento realizado en main_cargarDatos.m 23 % 24 % salida: Vector columna con la variable de salida para cada una de las muestras 25 % en datos. Si no se conoce una componente debe ser NaN. 26 % 27 % hiddenLayerSizes: Vector de enteros de tamaño igual al número de 28 % variables categóricas que indica el tamaño de la capa oculta de la 29 % red usada para codificar cada una de las variables. Si está vacío 30 % se calcula mediante la expresión previa. 31 % 32 % idCod: Entero usado para ser añadido en nombresCabecera ya que la 33 % codificación no es única (depende de hiddenLayerSizes).
78 Anexo A. Códigos implementados 34 % 35 %Salidas: 36 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 37 % como neuronas totales se hayan usado en la capa oculta de todas las 38 % variables categóricas. Más una columna para los índices. 39 % 40 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 41 % la variable de cada columna de la matriz datosCod. 42 % 43 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 44 % cual cada elemento contiene la transformación numérica que se le 45 % asigna a cada valor de las variables categóricas. 46 % 47 % features: Sruct [1xN] donde cada elemento corresponde a una 48 % característica. Sus campos son: 49 %−nombre: String con el nombre de la característica. 50 %−tipo: String con el tipo de la característica. 51 %−nValores: Entero con el número de valores diferentes que puede 52 % adoptar cada característica. En el caso de ser continua está 53 % vacío. 54 %−valores: Para las continuas está vacío, pero en las 55 % categóricas es un cell [1 x nValores] que contiene los 56 % diferentes valores que puede adoptar la característica. 57 %−representacion: Para las continuas está vacío, pero en las 58 % categóricas es un vector [1 x nValores] que contiene los 59 % enteros usados para sustituir los diferentes valores que puede 60 % adoptar la característica. 61 %−frecuencias: Para las continuas está vacío, pero en las 62 % categóricas es un vector [1 x nValores] que contiene el número 63 % de veces que aparece cada valor. 64 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 65 %−columna: Entero que indica las columnas de datosCod que contienen la 66 % codificación de la variable categórica. Para las continuas está 67 % vacío. 68 %−codificacion: Para las continuas está vacío, pero en las 69 % categóricas es un cell [1 x length(columna)] que contiene los 70 % números con los que se sustituye cada posible valor de la variable. 71 % 72 %Notas: 73 % 74 % 75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 76 77 function [datosCod,nombresCabecera,cabecera,features]=entityEmbedding(datos,features,←- salida,hiddenLayerSizes,idCod) 78 79 % Se extrae la columna de los índices 80 indices=datos(:,1); 81 datos(:,1)=[]; 82 83 % Se determinan las dimensiones de la matriz de entradas 84 [nMuestras,nVariables]=size(datos); 85 86 % Se determinan las columnas de la matriz de salida datosCod que tendrán la 87 % codificación de cada variable categórica original. 88 % El número de columnas esta especificado por hiddenLayerSizes. 89 % Si este vector está vacío se usa el número medio entre el tamaño de la 90 % capa de entrada y de salida. 91 N=0; 92 for iVar=1:nVariables 93 if strcmp(features(iVar).tipo,'%s') 94 if ~isempty(hiddenLayerSizes)
A.1 Codificaciones 79 95 tam=hiddenLayerSizes(1); 96 hiddenLayerSizes(1)=[]; 97 else 98 tam=round((1+features(iVar).nValores)/2); 99 end 100 features(iVar).columna=(1:tam)+N; 101 N=features(iVar).columna(end); 102 elseif strcmp(features(iVar).tipo,'%f') 103 features(iVar).columna=[]; 104 end 105 end 106 107 % Se iniciliza la matriz de datosCod 108 datosCod=−1*ones(nMuestras,N); 109 110 % Se pasa a realizar el cálculo de la codificación 111 % Para cada una de las variables 112 for iVar=1:nVariables 113 % Si la variable es categórica 114 if strcmp(features(iVar).tipo,'%s') 115 % Se inicializa el campo de codificacion 116 features(iVar).codificacion=cell(1,features(iVar).nValores); 117 118 % Se realiza la codificación one−hot de la variable 119 x=full(ind2vec(datos(:,iVar)',features(iVar).nValores)); 120 121 % Se inicializa la red con el tamaño de la capa oculta adecuado 122 net = fitnet(length(features(iVar).columna), 'trainscg'); 123 124 % Se determina la división de los datos 125 net.divideParam.trainRatio = 70/100; 126 net.divideParam.valRatio = 15/100; 127 net.divideParam.testRatio = 15/100; 128 129 % Se entrena la red con las muestras con salida conocida 130 net = train(net,x(:,~isnan(salida)),salida(~isnan(salida))'); 131 132 % Se recogen los pesos necesarios para la codificación 133 weights=net.IW{1}'; 134 clear net x 135 136 % Para cada una de las categorías de la variable 137 for iValor=1:features(iVar).nValores 138 % Se rellena el campo codificacion de la estructura 139 features(iVar).codificacion{iValor}=weights(iValor,:); 140 141 % Se completa la salida datosCod 142 % Se buscan las muestras de cada categoría y se rellena cada 143 % una de las columnas 144 indices_valor=features(iVar).representacion(iValor)==datos(:,iVar); 145 for iCol=1:length(features(iVar).columna) 146 datosCod(indices_valor,features(iVar).columna(iCol))=features(iVar).←- codificacion{iValor}(iCol); 147 end 148 end 149 150 % Si la variable no es categórica 151 elseif strcmp(features(iVar).tipo,'%f') 152 % El campo codificación estará vacío 153 features(iVar).codificacion=[]; 154 end 155 end
80 Anexo A. Códigos implementados 156 157 % Se libera memoria 158 clear indices_valor weights t datos 159 160 % Se añade el índice de cada muestra como primera columna 161 datosCod=[indices datosCod]; 162 clear indices 163 164 % Se actualiza el campo columna de la estructura features debido a la 165 % inclusión del índice 166 for iVar=1:nVariables 167 features(iVar).columna=features(iVar).columna+1; 168 end 169 170 % Se procede a calcular las salidas cabecera y nombresCabecera 171 nombresCabecera=cell(1+N,1); 172 cabecera=cell(1,N+1); 173 174 % La primera posición corresponde al índice 175 nombresCabecera{1}={'1 −−> Índice'}; 176 cabecera{1}='Int'; 177 178 % Se completan cabecera y nombresCabecera para el resto de variables 179 for iVar=1:nVariables 180 % Si la variable es categórica 181 if strcmp(features(iVar).tipo,'%s') 182 % Para cada columna de la matriz datosCod 183 for iCol=1:length(features(iVar).columna) 184 col=features(iVar).columna(iCol); 185 cabecera{col}=cell(features(iVar).nValores,1); 186 187 % Se describe el valor que adopta cada categoría 188 for iValor=1:features(iVar).nValores 189 cabecera{col}{iValor}=[features(iVar).valores{iValor} '−−>'num2str(←- features(iVar).codificacion{iValor}(iCol))]; 190 end 191 192 % Se completa nombresCabecera con un nombre para cada nueva 193 % variable 194 nombresCabecera{col}=[num2str(col) '−−> ee' num2str(idCod) '_'num2str(iCol)←- 'w_'features(iVar).nombre]; 195 end 196 197 end 198 end 199 200 201 end Codificación basada en estadísticas de la variable de salida continua Código A.13 probabilityContinuousTarget.m. 1%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2% 3%Este script transforma un conjunto de datos ya procesados por main_cargarDatos 4%de tal forma que solo incluye las variables categóricas y les realiza una 5%codificación basada en la probabilidad a priori y posteriori de la salida 6%condicionada por el valor de la variable categórica.
A.1 Codificaciones 81 7% 8%Uso: 9% [datosCod,nombresCabecera,cabecera,features]=probabilityContinuousTarget(datos,←- features,salida,lambda,idCod) 10 % 11 %Entradas: 12 % datos: Matriz de datos de las muestras dadas por el preprocesamiento 13 % realizado en main_cargarDatos.m 14 % 15 % features: Estructura con la información de cada variable dada por el 16 % preprocesamiento realizado en main_cargarDatos.m 17 % 18 % salida: Vector columna con la variable de salida para cada una de las muestras 19 % en datos. Si no se conoce una componente debe ser NaN. 20 % 21 % lambda: Función anónima que se usará para el cálculo de los pesos de 22 % las probabilidades a priori y a posteriori. Su única variable de 23 % entrada es el número de veces que aparece un determinado valor de 24 % una variable categórica. 25 % 26 % idCod: Entero usado para ser añadido en nombresCabecera ya que la 27 % codificación no es única (depende de la función lambda). 28 % 29 %Salidas: 30 % datosCod: Matriz [MxN'] con tantas filas como muestras y tantas columnas 31 % como variables categóricas exitan en la matriz de entrada datos. Más 32 % una columna para los índices. 33 % 34 % nombresCabecera: Cell [N'x1] donde cada elemento indica el nombre de 35 % la variable de cada columna de la matriz datosCod. 36 % 37 % cabecera: Cell[1xN'] donde cada elemento contiene otro cell en el 38 % cual cada elemento contiene la tranformación númerica que se le 39 % asigna a cada valor de las variables categóricas. 40 % 41 % features: Sruct [1xN] donde cada elemento corresponde a una 42 % característica. Sus campos son: 43 %−nombre: String con el nombre de la característica. 44 %−tipo: String con el tipo de la característica. 45 %−nValores: Entero con el número de valores diferentes que puede 46 % adoptar cada característica. En el caso de ser continua está 47 % vacío. 48 %−valores: Para las continuas está vacío, pero en las 49 % categóricas es un cell [1 x nValores] que contiene los 50 % diferentes valores que puede adoptar la característica. 51 %−representacion: Para las continuas está vacío, pero en las 52 % categóricas es un vector [1 x nValores] que contiene los 53 % enteros usados para sustituir los diferentes valores que puede 54 % adoptar la característica. 55 %−frecuencias: Para las continuas está vacío, pero en las 56 % categóricas es un vector [1 x nValores] que contiene el número 57 % de veces que aparece cada valor. 58 %−frecuenciaRel: Igual que el anterior pero en tanto por cien. 59 %−columna: Entero que indica la columna de datosCod que contiene la 60 % codificación de la variable categórica. Para las continuas está 61 % vacío. 62 %−codificacion: Para las continuas está vacío, pero en las 63 % categóricas es un vector [1 x nValores] que contiene el número 64 % con el que se sustituye cada posible valor de la variable. 65 % 66 %Notas: 67 %
82 Anexo A. Códigos implementados 68 % 69 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 70 71 function [datosCod,nombresCabecera,cabecera,features]=probabilityContinuousTarget(datos←- ,features,salida,lambda,idCod) 72 % Se extrae la columna de los índices 73 indices=datos(:,1); 74 datos(:,1)=[]; 75 76 % Se determinan las dimensiones de la matriz de entradas 77 [nMuestras,nVariables]=size(datos); 78 79 % Se determina la columna de la matriz de salida datosCod que tendrá la 80 % codificación de cada variable categórica original. 81 N=1; 82 for iVar=1:nVariables 83 if strcmp(features(iVar).tipo,'%s') 84 features(iVar).columna=N; 85 N=N+1; 86 elseif strcmp(features(iVar).tipo,'%f') 87 features(iVar).columna=[]; 88 end 89 end 90 N=N−1; 91 92 % Se iniciliza la matriz de datosCod 93 datosCod=−1*ones(nMuestras,N); 94 95 % Se determina la probabilidad a priori 96 prioriP=mean(salida,'omitnan'); 97 98 % Se pasa a realizar el cálculo de la codificación 99 % Para cada una de las variables 100 for iVar=1:nVariables 101 % Si la variable es categórica 102 if strcmp(features(iVar).tipo,'%s') 103 % Se inicializa el campo de codificacion 104 features(iVar).codificacion=−1*ones(1,features(iVar).nValores); 105 106 % Para cada uno de los valores que puede adoptar la variable 107 for iValor=1:features(iVar).nValores 108 valor=features(iVar).representacion(iValor); 109 110 % Se determina el índice de las muestras con dicho valor 111 iMuestrasValor=datos(:,iVar)==valor; 112 % Se calcula la probabilidad a posteriori 113 posteriorP=mean(salida(iMuestrasValor),'omitnan'); 114 % Se calcula la codificación mediante el uso de las dos 115 % probabilidades y la función lambda 116 cod=posteriorP*lambda(sum(iMuestrasValor)) + prioriP*(1−lambda(sum(←- iMuestrasValor))); 117 % Se actualiza el campo codificacion para dicho valor 118 features(iVar).codificacion(iValor)=cod; 119 120 % Se atualiza la salida datosCod haciendo uso de la 121 % codificación calculada 122 datosCod(iMuestrasValor,features(iVar).columna)=cod; 123 end 124 125 % Si la variable no es categórica 126 elseif strcmp(features(iVar).tipo,'%f') 127 % El campo codificación estará vacío
A.1 Codificaciones 83 128 features(iVar).codificacion=[]; 129 end 130 end 131 132 % Se añade el índice de cada muestra como primera columna 133 datosCod=[indices datosCod]; 134 135 % Se actualiza el campo columna de la estructura features debido a la 136 % inclusión del índice 137 for iVar=1:nVariables 138 features(iVar).columna=features(iVar).columna+1; 139 end 140 141 % Se procede a calcular las salidas cabecera y nombresCabecera 142 nombresCabecera=cell(1+N,1); 143 cabecera=cell(1,N+1); 144 145 % La primera posición corresponde al índice 146 nombresCabecera{1}={'1 −−> Índice'}; 147 cabecera{1}='Int'; 148 149 % Se completan cabecera y nombresCabecera para el resto de variables 150 for iVar=1:nVariables 151 % Si la variable es categórica 152 if strcmp(features(iVar).tipo,'%s') 153 % Se inicializa cada componente de la cabecera con un cell de 154 % tamaño igual al número de valores 155 col=features(iVar).columna; 156 cabecera{col}=cell(features(iVar).nValores,1); 157 158 % En cada componente del cell creado se indica la transformación 159 % numérica llevada a cabo 160 for iValor=1:features(iVar).nValores 161 cabecera{col}{iValor}=[features(iVar).valores{iValor} '−−>'num2str(←- features(iVar).codificacion(iValor))]; 162 end 163 164 % Se completa nombresCabecera con un nombre para cada nueva 165 % variable 166 nombresCabecera{col}=[num2str(col) '−−> pct' num2str(idCod) '_'features(iVar).←- nombre]; 167 168 end 169 end 170 171 172 end