Para programadores
Calculo dinámico de Scoring
Esta entrada sale fuera de lo común en este blog, pero como es mi blog y puedo escribir lo que me guste, pues en esta oportunidad hago una excepción para escribir dos entradas que no son comunes, y sin ufanarme, quiero dejar este grano de arena como ayuda a quienes puedan encontrar algún problema similar.
En mi trabajo como programador, he hecho muchas cosas, la mayoría repetitivas, pero algunas poco comunes, y en esta oportunidad, por segunda vez, me encontré con un problema cuya solución requirió no solo del conocimiento del lenguaje, sino de algo de imaginación, mucho de abstracción y de lectura para completar la tarea que me fue asignada.
Un Scoring en palabras simples, es un número que indica, financieramente, que tanto riesgo implica prestar dinero. El Scoring se aplica a personas o empresas, se basa en muchas variables como ingresos, egresos, sexo, estado civil, ocupación, educación y un largo (realmente largo) etc. Entre mas variables se incluyan mas acertado o mas preciso es el resultado.
Hacer el cálculo no es mas que una simple suma, es tomar cada variable y aplicar una fórmula matemática simple para obtener un resultado. Cada variable tiene una valoración, digamos de 0 a 10, así que el resultado estará de 0 a 10, o dicho de otra manera, el scoring estará de 0 a 10. Cuanto mas se acerque a 10 el Scoring, menor será el riesgo, cuanto mas se acerque a 0 mayor será el riesgo. ¿Y esto que signica? Si el Scoring está cerca de 10, cualquier entidad que preste dinero querrá hacer el préstamo, si se acerca a 0 será lo contrario. Las empresas que operan en campo financiero utilizan este método (entre otros) para determinar si aprobar o no un crédito, o para determinar el cupo asignado a una tarjeta de crédito.
Ahora bien, el cálculo como tal para una persona no representa ningún problema, conociendo las variables fácilmente se puede hacer el cálculo, el problema se presenta cuando se quiere automatizar el proceso, y es allí donde entro yo como programador, porque el problema que me plantearon no era solo hacer el cálculo, sino hacer un programa que haga el cálculo, sin conocer de antemano qué variables, ni cuantas variables se van a utilizar, y para hacerlo mas interesante, me pidieron que fuese dinámico, es decir, que el mismo programa permitiera agregar o eliminar variables durante la ejecución.
Así que fue necesario aplicar un poco de imaginación
La solución que encontré y desarrollé, no es 100% dinámica, pero para los efectos del problema planteado, funciona perfectamente.
Hay varias premisas tomadas del problema mismo:
- Todas las variables deben tener un valor en un rango específico (puede ser 0 a 10, 0 a 100, 0 a 1000 o 0 a 1)
- Cada variable tiene un peso en el cálculo
- Las variables pueden agruparse y cada grupo tiene un peso en el cálculo
- No hay límites a la cantidad de variables
- No hay límites a la cantidad de grupos de variables
- La suma de los pesos de todos los grupos debe ser 100
- La suma de los pesos de todas las variables de un grupo debe ser 100
Para entender esto hagamos un ejemplo. Supongamos que tomamos 5 variables, cada variable tiene un valor de 0 a 10, y las ponemos en dos grupos:
Grupo A
- Edad
- Nivel de estudio
- Vivienda
Grupo B
- Sexo
- Cantidad de hijos
Ahora, el Grupo A de variables tiene un peso de 60, es decir, representa el 60% del scoring, y el Grupo B tiene un peso de 40, así la suma de los pesos de los grupos suma 100
Edad tiene un peso de 30, Nivel de estudio de 50 y Vivienda de 20.
Sexo tiene un peso de 40 y cantidad de hijos 60.
Ya teniendo los pesos, faltan los valores:
Edad
- 18 a 30 años vale 5
- 30 a 40 años vale 9
- 40 a 50 años vale 7
- 50 o mas vale 2
Nivel estudio
- Ninguno vale 0
- Primaria vale 2
- Secundaria vale 3
- Tecnico vale 4
- Universitario vale 5
- Maestria vale 6
- Doctorado vale 7
- PHD vale 10
Vivienda
- Propia vale 10
- Arrendada vale 5
- De un familiar vale 7
Sexo
- Femenino 10
- Masculino 8
Cantidad de hijos
- Ninguno vale 3
- Un hijo vale 8
- Dos hijos vale 9
- Tres o mas hijos 4
En base a estos datos, ¿cómo se calcula el scoring de una persona? Conociendo sus datos, podemos hacer el cálculo. Supondremos una persona de 35 años, universitario, que vive arrendado, hombre y sin hijos
35 años -> 9
Universitario -> 5
Arrendado -> 5
Hombre ->; 8
Sin hijos -> 3
El scoring se calcula tomando el valor de cada variable, multiplicarlo por su peso y sumarlo con todas las variables del mismo grupo, el resultado multiplicarlo por el peso del grupo, repetir para cada grupo, y sumar el resultado de cada grupo.
El scoring para nuestro ejemplo sería: (9*30% + 5*50% + 5*20%)*40% + (8*40% + 3*60%)*40% = 6,2*60% + 2*40% = 5,72
Así el scoring para este ejemplo es 5,72.
Noten que cada variable tiene un valor (que esta entre 0 y 10) y tiene un peso, y cada grupo otro peso.
Ahora el problema de automatizar esto, sin conocer de antemano que variables, pesos y grupos tenemos. Este fue el problema planteado en su inicio.
Bien, al no conocer la cantidad, tipo, valores y pesos de las variables y grupos, lo primero que se me ocurrió fue crear una plantilla genérica. En esta plantilla, se especificarían (por la persona que tiene el conocimiento) las variables, valores y pesos. La plantilla debe ser flexible al permitir la creación de grupos y variables, pero rígida exigiendo todo lo necesario para el cálculo de scoring, y luego un algoritmo que lea esa plantilla, solicite los valores de cada variable, guarde el resultado y calcule el scoring finalmente.
Para ello se ideó utilizar una plantilla con formato XML, este XML tiene la siguiente estructura:
< template id="tem1" >
< group id="gr1" weigth="" >
< item id="ite1" weigth="" ></ item >
</ group >
</ template >
Donde < template > representa una plantilla, que puede contener uno o mas < group >.
Cada < group > debe tener un atributo que indica el peso (weigth) y puede contener uno o mas < item > . Un < group > representa un grupo de variables.
Cada < item > representa una variable, y debe tener un peso. Cada < item > es un componente que debe capturarse para obtener el valor adecuado.
Ahora, hay < item > que no tienen un valor determinado por la captura, sino que su valor depende de los valores de otros < item >, esto ocurre por ejemplo, cuando se desea determinar en el scoring el nivel de endeudamiento que puede tener una persona, esto se sabe por esta fórmula:
endeudamiento = egresos/ingresos*100
Así que, el < item > que corresponde a endeudamiento, no toma su valor de una captura, sino de otros dos < item >. Para representar esto, lo hacemos así (asumiendo que los item están en un template con id="tem1"):
< item id="egresos" weigth="10" ></ item >
< item id="ingresos" weigth="15" ></ item >
< item id="endeudamiento" weigth="15" value="tem1.egresos/tem1.ingresos*100" >
Como ven, el atributo value es necesario en un item, para los casos donde es necesario calcular el valor basado en el valor de otros item.
Una vez resuelto eso, se crea otro problema, basado en el value del item endeudamiento, ¿cómo podemos calcular ese valor?. Bien el algoritmo para tomar una ecuación y obtener su resultado, será objeto de otro post, por su complejidad, pero comienza por saber cómo debe guardarse la información obtenida, para identificar cada item, group y template y realizar el cálculo. Nosotros creamos una tabla donde se almacenan:
- nombre del archivo XML
- id del group
- peso del group
- id del item
- peso del item
- valor del item
Con esta tabla, una vez capturados los valores de cada item (o calculado, según el caso) se almacenan, y finalmente se realiza el algoritmo que calcula el scoring:
score = 0;
while (hay registros) {
score = score + item.value * (item.weigth/100) * (group.weigth/100)
}
De esta manera, para que sea dinámico, solo se necesita crear un XML para cada caso, donde se tengan todos los item que se desean en el cálculo del scoring.
Nosotros agregamos mas atributos para los item, esto para crear de cada item un input de HTML, así el algoritmo que lee cada item convierte a éste en su tag HTML equivalente para mostrarse en pantalla. Para ello agregamos:
atributo type: indica si es input, radio, select, hidden, etc.
atributto class: para indicar la clase del CSS que le da aspecto
atributo order: para indicar el orden en que se muestran
atributo name: para indicar el nombre del componente
atributos maxLength, readonly, visible, size, etc. para darles comportamiento, y con su equivalente en HTML
Tambien agregamos algunos otros componentes al XML, como por ejemplo los OPTION, en caso de que un item sea select o radio, se necesita conocer datos en referencia a lo que ese item puede tener como valores, el caso típico es el sexo:
< item id="ite1" name="sexo" type="radio" >
< option value="1" defaultValue="true" >Femenino</ option >
< option value="2" >Masculino</ option >
</ item >
De esta forma, cuando el algoritmo lee el item ite1, lo convierte en:
< input type="radio" name="sexo" value="1" selected="selected" >Femenino
< input type="radio" name="sexo" value="2" >Masculino
Y aprovechando el algoritmo que ya habíamos creado, convertimos los < group > en tag HTML tambien, de modo que cada < group > representa un < div >, cada < template > un < form >, y cada < item > lo colocamos dentro de un < div > también, con eso logramos darle mas dinamismo, y permitimos que un < item > sea visible o no, de acuerdo al valor de otro < item >, usando JQuery. Les dejo uno de los ejemplos que usamos:
< ?xml version="1.0" encoding="UTF-8"? >
< template id="T1" title="Primer Template" action="/Prueba" next="template2" >
< script >
$ ( "#IT3" ) . change( function ( ) {
if ( $ ( "#IT3" ) . val ( ) = = "DOS" ) {
$ ( "#div-IT4" ) . show( );
} else {
$ ( "#div-IT4" ) . hide ( );
}
} ) ;
</ script >
< item weight="0" id="H1" type="hidden" value="100" name="SCR" >
< group weight="60" id="G1" order="1" name="Grupo1" groupClass="uno" itemClass="dos" >
< text class="otra" >Grupo1</ text >
< item weight="30" id="IT1" type="text" order="1" maxlength="10" size="30" required="true" class="prueba" readonly="false" name="IT1" >
< text >Item1G1</ text >
</ item >
< item weight="30" id="IT2" type="radio" order="2" name="IT2" >
< text >Item2G1</ text >
< option value="10" defaultValue="true" >Diez</ option >
< option value="120" >Ciento Veinte</ option >
</ item >
< item weight="30" id="IT3" order="4" type="select" name="IT3" >
< text >Item3G1</ text >
< option value="10" defaultValue="true" >UNO</ option >
< option value="5" >DOS</ option >
</ item >
< item weight="10" id="IT4" type="text" order="3" name="IT4" visible="false" >
< text >Item4G1</ text >
</ item >
< item weight="10" id="IT5" type="text" order="3" name="IT5" >
< text >Viajes</ text >
</ item >
</ group >
</ template >
El HTML equivalente, es este (modifique un poco para evitar errores en la muestra):
< form method="POST" action="Prueba" id="T1" >
< input type="hidden" value="100" name="SCR" id="H1" >
< div id="G1" class="uno" > < !-- G1 -- >
< div >
< spam class="otra">Grupo1</ spam >
< /div >
< div class="dos" >
< div class="prueba" id="div-IT1" >Item1G1
< input type="text" class="prueba" required="required" name="IT1" id="IT1" >
</ div >
< div id="div-IT2" >Item2G1
< input type="radio" value="10" checked="checked" name="IT2" id="IT2" >Diez
< input type="radio" value="120" name="IT2" id="IT2" >Ciento Veinte
</ div >
< div id="div-IT4" >Item4G1
< input type="text" name="IT4" id="IT4" >
< /div >
< div id="div-IT5" >Viajes
< input type="text" name="IT5" id="IT5" >
< /div >
< div id="div-IT3" >Item3G1
< select name="IT3" id="IT3" >
< option value="10" selected="selected" >UNO</ option >
< option value="5">DOS</ option >
</ select >
< /div >
</ div >
</ div > < !-- Fin de G1 -- >
< button type="submit" >Siguiente</ button >
</ form >
< !-- SCRIPTS JQUERY -- >
< script type="text/javascript" >
$ ( document ) . ready ( function ( e ) {
$ ( " # IT3 " ) . change( function () {
if ( $ ( "#IT3" ).val() = = "DOS" ) {
$ ( "#div-IT4" ) . show();
} else {
$ ( "#div-IT4" ) . hide();
}
});
$ ( "#div-IT4" ) . hide ( );
document . title = 'Primer Template';
});
</ script >
< !-- FIN SCRIPTS JQUERY -- >"
Espero sea útil.
Gracias por leerme.
Comentarios