top of page

COSAS VEREDES

  • fjroar
  • 24 mar 2024
  • 5 Min. de lectura

Cansado un poco de toda esta vorágine donde a todo se le llama Inteligencia Artificial con el apelativo guay de generativa, ... vuelvo a mis orígenes para lo cual estoy comenzando a trabajar una librería para uso astronómico gracias a los conocimientos que he podido ir adquiriendo como socio de la Agrupación Astronómica de Madrid y en especial al área del grupo de la Luna:



Así que, aprovechando que mañana 25 – Abril – 2024 es Lunes Santo y curiosamente, para la primera Luna llena tras el equinoccio de primavera, tenemos simultáneamente un eclipse prenumbral (que no podrá verse muy bien en España y además habría que madrugar …), aprovecho para presentaros algunas evoluciones que espero ir mejorando sobre la librería que estoy construyendo con R con funciones para el análisis y predicción de los principales eventos lunares.


ree


Recuerdo que su instalación para quien tenga Rstudio y Windows resulta muy sencilla y basta tan sólo teclear:

 

               library(devtools)

install_github("FJROAR/RMoon", force = T)

 

En concreto la función que he introducido es la denominada: MoonEclipses() y que lo que hace es que dada una fecha, determina cuál es la siguiente Luna Llena y si habrá o no eclipse de Luna, así pues si se teclea:


options(digits=11)

options(scipen=999)

                          library(Rmoon)

                          MoonEclipses("2024-03-24")

 

Os dará una serie de salidas que detallo más adelante, aunque están explicadas en la ayuda de la función.


No obstante, antes de empezar si alguno se instaló la librería con anterioridad, recomiendo que la actualice (y que lo haga de vez en cuando ya que la voy mejorando y corrigiendo errores que me voy encontrando), por lo que debería dar los siguientes pasos:


(1)   remove.packages("RMoon")

(2)   Volver a aplicar a ejecutar:


library(devtools)

install_github(“FJROAR/RMoon”, force = T)

 

Pues bien, aunque hay muchas cuestiones a tratar en esto de los Eclipses, me voy a centrar en una parte de la función MoonEclipses() basada por supuesto en los algortimos del astrónomo Jean Meeus. En concreto, para saber si una determinada fecha en el futuro será o no eclipse, hay que medir si la próxima Luna llena esté suficientemente cercana a uno de sus nodos y para esto, dentro del algoritmo destaco la siguiente fórmula que adapté a R y que explico aquí a los pocos que hayáis llegado a leer hasta este punto y que no os hayáis cansado, ya que como veis no hablo de Machine Learning ni de flipadas por el estilo que sé que os gusta más:


Eclipse <- abs(F2 %% 360)

Eclipse <- min(abs(Eclipse - 0), abs(Eclipse - 180), abs(Eclipse - 360))

    isEclipse[i] <- ifelse(Eclipse < 13.9, "Yes", "No")

    isEclipse[i] <- ifelse((Eclipse >= 13.9 & Eclipse <= 21), "Indetermined", isEclipse[i])


Como puede observarse hay una franja de imprecisión, donde arroja el mensaje Indetermined, lo que obliga ver algunos de los parámetros que ofrece la función para saber si efectivamente ese día habrá o no eclipse de Luna.


Así pues si se ejecuta (tras instalar y llamar a la librería) MoonEclipses("2024-03-24") se obtiene lo siguiente:


ree

Cuyo significado paso a describir:


         [[1]]       Día juliano en el que tendrá lugar el eclipse

         [[2]]       Fecha y hora

         [[3]]       ¿Será eclipse?  Yes, No, Indetermined

         [[4]]       Nodo más cercano

[[5]]       Gamma o distancia mínima desde el centro de la luna al eje de la sombra terrestre en unidades del radio ecuatorial terrestre

[[6]]       Rho o radio de la penumbra si ésta tuviera lugar

[[7]]       Sigma o radio de la umbra si ésta tuviera lugar

[[8]]       Magnitud de la umbra (si fuese negativo, significa que no habría eclipse en la umbra)

[[9]]       Magnitud de la penumbra

[[10]]    Semi-duración de la umbra parcial en minutos si tuviese lugar

[[11]]    Semi-duración de la umbra en minutos en minutos si tuviese lugar

[[12]]    Semi-duración de la penumbra en minutos si tuviese lugar

 

Pero ¿Qué ocurriría si se ejecutase MoonEclipses("2024-03-26")? En este caso, la salida sería bastante diferente, ya que se indicaría cuando sería la próxima Luna Llena de Abril pero la salida [[3]] aparece como Indeterminated tal y como se puede observar:


ree
ree


En este caso obsérverse que no habrá eclipse porque de hecho, no hay semi-duraciones [[10]], [[11]] y [[12]] y las magnitudes de las umbras y pre-umbras son negativas [[8]] – [[9]], sin embargo, para tener una idea de la complejidad del problema, cabe considerar el eclipse de Junio de 1973 (que propone Jean Meeus en su libro), que podemos analizar con esta sencilla función con tan sólo escribir MoonEclipses("1973-06-01") obteniéndose:


ree
ree


Con lo que aquí sí se puede considerar eclipse, de hecho, sería sólo de tipo penumbral y por tanto tiene magnitud positiva y semiduración de la penumbra como se observa en [[9]] y [[12]].


Finalmente y para no extenderme demasiado algunas anotaciones resumen sombre esta entrega de la librería RMoon:


  • Actualmente no se puede habla de eclipses sin hacer referencia a la web de Mr. Eclipse que se pueden encontrar en: https://www.mreclipse.com/Special/LEnext.html Se puede comprobar que muchos de los términos que extrae este programa se corresponde con los resultados que allí se publica con un alto grado de similitud, así pues para mañana, Mr Eclipse arroja los siguientes resultados (con apenas algunos segundos de desviación de la hora prevista:


ree

  • Las funciones construidas están vectorizadas, de modo que a diferencia de otras  implementaciones, si se introduce un vector, RMoon devolverá o un vector o una lista de vectores.

  • Salvo que se tome material muy especializado, se frecuente ver en muchas web e incluso en aparatos de seguimiento actuales, ciertos errores que sin embargo, a nivel de los algoritmos de esta librería, quedan bastante corregidos.

  • Si se ha notado más arriba, se exige a R el uso de una elevada precisión numérica, es quizás este uno de los aspectos más apasionantes que me han inducido a hacer este trabajo y es que no solamente es interesante por los resultados, si no por el cuidado y el “mimo” que hay que poner en la programación para no caer en problemas de propagación de errores.

  • Por si alguien quiere observar el eclipse desde España serán las 8.14h y por tanto habrá demasiada luz diurna, dicho eclipse empieza como 2 horas antes aproximadamente por lo que hay que madrugar como indiqué al principio, así que entre eso y la de nubes que se nos viene encima, como que no lo veo claro.

  • Y ya por acabar, claramente hay algoritmos más precisos y mejores que incluso el propio Meeus reconoce sin embargo, las funciones aquí utilizadas resultan bastante asequibles y sencillas (para lo que hay) y el grado de aproximación es bastante elevado. Por el momento, parece funcionar bien en el rango de 1951 a 2050 que indica el Meeus y personalmente creo que se puede ir bastante atrás en el tiempo pero si se baja de 1600, se necesitaría mejorar el algoritmo considerado, por tanto cabe otorgarle una validez desde 1900 hasta el 2100.

 
 
 

En esta última parte de la medición del valor añadido, quiero ofrecer un acercamiento práctico a través de una función R que dejo en mi git para el interesado en usarla:

 

 

Con lo tratado hasta aquí podemos medir el beneficio en modelos de admisión de riesgos enfocados a compras aplazadas y préstamos fundamentalmente, siendo algo más complejo el tema de tarjetas de crédito que en función del tipo de tarjeta y de sus opciones convendría analizarlo con un poco más de detalle en algunos casos.


Por tanto, teniendo en cuenta lo anterior, nos centramos en la última parte del siguiente esquema:


ree

Figura 1: Esquema genérico de un sistema de estimación de beneficios de un modelo en una cartera de clientes


Antes de llegar a dicha última parte indicar que dado que en general se toman datos mensuales, sólo interesan unas pocas variables, los datos finales que van a trabajarse con la función R que explico a continuación no van a suponer un problema de volumen de información y por tanto esta función puede ser perfectamente trabajada en local.

Esta función emplea a su vez algunos elementos de unas de mis librerías favoritas como es la scorecardModelUtils, con lo que esta versión está restringuida para modelos de Credit Scoring, pero no sería muy complicado extenderla para modelos Machine Lerning en general, aunque para estos caso algunas cosas que nos simplifica la anterior librería deberán ser programadas a mano.


Así pues los parámetros de entrada de la función a considerar aquí serían los siguientes:


ree
ree

 







Figura 2: Parámetros y dependencias principales


En principio el último parámetro denominado perc no sería necesario si se trabaja con los rechazados y se tiene además, para dichos rechazados por el modelo los valores de exposición y de fee correspondientes tal y como se comentó en el anterior post, pero por simplificar la exposición y porque no siempre en todas las situaciones los valores anteriores son factibles, lo que hace esta función es que trabaja sólo sobre los aceptados y se estima, en función del porcentaje de rechazo lo siguiente:

 

  • Estima el número de clientes rechazados en esta parte del código:


ree

  • Se estima la exposición media de dichos clientes, calculando primero la media de las exposiciones en los clientes conocidos y extrapolando este valor al cada uno de los anteriores clientes

  • Se estima el ingreso (fee) medio de dichos clientes, calculando primero la media de los fees en los clientes conocidos y extrapolando este valor al cada uno de los anteriores clientes. Los dos pasos anteriores se hacen justo aquí:


ree

Y se añade al dataset agregado df_model_sum con:

 

                               df_model_sum <- rbind(df0, df_model_sum)


Nótese que para llegar a lo anterior se han creado las siguientes tablas básicas:

 

  • Variables internas y selección de las fechas de los datos:

ree

  • Generación de la tabla gini mediante el uso de la librería scorecardModelUtils y su función gini_table() además se estima uno de los parámetros de salida de la función como será el gini y dentro del dataset df_model_prev está la variable Total que se usa para el cálculo del número total de individuos según la inferencia realizada en el apartado anterior:


ree

El dataset df_model_prev intenta ser de unos 100 registros (percentiles) que en ocasiones, si el modelo no es muy granular no van a conseguir o si hay puntos de acumulación. La función gini_table() preserva la integridad de los puntos score que emplea de modo que no asocia a distintos tramos clientes con la misma puntuación, lo que distorciona todos los cálculos, de ahí que sea tan recomendable scorecarModelUtils frente a una mera división en percentiles con n_tile() de dplyr 

que puede generar muchos problemas

 

  • Otro elemento a destacar de la anterior tabla es que genera columnas con los puntos de corte, lo que nos permite aplicalos nuestro dataset original para hacer posteriormente las segmentaciones por importes (exposiciones y exposiciones en default) que es lo que nos interesa, ya que por desgracia, scorecardModelUtils sólo agrupa por número, no por importes. Esto se hace precisamente aquí:


ree

Nótese que df_model_sum tiene ya casi todo lo necesario para aplicar la metodología del anterior post

 

  • La inferencia del riesgo se hace mediante el siguiente modelo de regresión lineal de orden 2. En aplicaciones más avanzadas se pueden dejar distitas modelizaciones y que se tome la de menor R-cuadrado, pero en esta ocasión me declino por esta por dar unos resultados en general coherentes. Nótese que como variable explicaa va el procentaje (sin acumular) de la tasa de riesgo en importes (no en número) y como variable explicativa de grado 1 y 2 el porcentaje de exposición acumulado pero considerando la inferencia realizada en el paso anterior (que es la que nos interesa estimar):


ree

El anterior fragmento de código es equivalente a plantear el siguiente gráfico ya

mostrado en el anterior post:

 

ree

Generándose la estimación de la tasa de riesgo deseada del siguiente modo:

 

                                                 rate = as.numeric((model$coefficients[1] +

                                                 model$coefficients[2] * perc +

                                                 model$coefficients[3] perc*2))


  • Una vez que se obtiene rate se completa el dato que se necesita en el segmento de inferencia del dataset df_model_sum, nótese que lo que nos va a interesar realmente e la exposición at default en el tramo inferido, este valor puede ser interpretado como el Coste de Riesgo evitado por el modelo y por tanto si este Coste de Riesgo > Fees rechazados, entonces la aplicación del modelo genera beneficios que será de lo que se trata después.

 

df_model_sum$risk[1] = rate

                 df_model_sum$exposure_at_default[1] = df_model_sum$total_exposure[1] * rate


  • El último paso de la función es generar el resto de los indicadores y ofrecerlos en forma de una lista de valores:


ree


Con todo lo anterior cabe ya generar la siguiente tabla mostrada también en el anterior post:


ree

Figura 3: Tabla de seguimiento de la eficiencia de una cartera de pago aplazado a 3 meses con algunas columnas relevantes (ac significa after cutoff y bc before cutoff)


Nota: (1) Hay borrones y eliminación de datos a propósito en la anterior figura

            (2) Nótese que Global Profits sería Profits without model y Profits ac sería Profits with model

 

Donde Add value = Total_ProfitsMod – Total_Profits pudiéndose por tanto con esta función y con un poco de desarrollo adicional tenerse automatizado el cálculo sistemático de beneficios o de valor añadido aportado por un modelo que en general va a ser una función dependiente de los siguientes elementos principales:


ree

Figura 4: Nivel conceptual de las dependencias principales de una función de beneficios en modelos de admisión de riesgo de crédito genérica



Conviene indicar que aunque la acción en sí de medir los beneficios que aporta un modelo parece no aportar realmente valor a una empresa lo cierto, es que tener su sistemática creada permitiría lo siguiente:

 

  • Valorar el valor que aportan las áreas analíticas cuando se les encomienda la construcción de un modelo para un determinado segmento de negocio

 

  • Valorar pruebas de concepto cuando vienen los “vendedores de humo” o “alfombras” a colocarnos sus maravillosos productos. A veces son buenos e incluso necesarios, pero podría citar varios casos de algunas patatas que se han colado por no hacer mediciones precisas

 

  • Conocer el límite real de mejora de los modelos y analizar cuándo es necesario articular variaciones bien en la política de riesgos, bien en la de pricing para alcanzar los objetivos de la empresa

 

  • Valorar si merece la pena un cambio de modelo frente a otro por unos cuantos puntos de Gini (tener en cuenta que el Gini, la ROC, … son variables aleatorias, las variaciones de +- 5 puntos arriba o abajo pueden deberse a fluctuaciones muestrales y por tanto modelo de un 44 de Gini puede generar similar beneficio a uno de un 48, eso sí cuando la diferencia es de +-10 puntos, la cosa es bastante distinta

 

 

Por tanto no hay ninguna excusa, salvo la “vagancia” o el desconocimiento de no querer llevar a cabo un programa de medición sistemática del beneficio aportado por los modelos, máxime sabiendo que si se hace con cuidado, esta acción se puede automatizar en un alto grado sin generar un coste de mantenimiento posterior elevado. Por supuesto que no todos los modelos se miden igual y por tanto no todos van a aportar el mismo valor, no es lo mismo un modelo de riesgos que uno de on-boarding, de fuga o de series temporales, cada uno tiene su metodología y en cada uno hay que combinar por un lado técnica estadística y por otro conocimiento de negocio para saber si estamos ganando pasta o tirando el dinero en interminables proyectos analíticos que mucho prometen pero que poco impactan en las cuentas de resultados a la hora de la verdad.

 
 
 

Como prometí voy con la segunda parte parte del anterior post, dejando en una tercera, como con R, sería factible hacer todo esto, eso sí, siempre y cuando se tenga la información preparada que por lo general (por no decir nunca, lo está).


Pues bien, el método anteriormente descrito, nos daba de un modo bastante prudente una estimación de los beneficios sin demasiada “inferencia estadística”, pero debido a sus limitaciones y a que consume, bastante información de la disponible, resulta preferible este segundo método que hace uso del concepto de curva de riesgos que va a permitir inferir la cantidad de información necesaria a partir de la existente:




ree

Figura 1: Distribución de las tasas de riesgo en importe frente a la población acumulada

Nota: Hay borrones y eliminación de datos a propósito en la anterior figura

 

Con un poco más de dificultad, se puede trabajar con los rechazados por el modelo y por tanto cabe deducir una barra adicional justo delante de la primera donde se tiene:


  • El número de transacciones (o clientes) rechazados por score

  • La exposición de las transacciones

  • E incluso el tipo de interés a nivel de transacción aplicado (si éste depende del producto, merchant, condiciones del cliente, etc)


El único dato que no se conoce va a resultar ser la tasa de riesgo en importe, es decir, al ser los anteriores clientes rechazados por el modelo, no se conoce si dichos clientes finalmente acaban pagando o no y cuánto dejarían de pagar exactamente, por esta última razón, para cada uno de los buckets observados el ratio que se calcula es:


ree

A diferencia de lo mostrado en la figura anterior, el número buckets para este propósito suele ser mayor y no tienen por qué tener tasas de población similares (sobre todo si el modelo no es muy granular), por ese motivo, es conveniente definir un eje horizontal donde se tengan la tasa de exposición acumulada y un eje vertical con las tasas de riesgo (o risk rates). Además la curva anterior debe construirse recalculando todo el eje horizontal para tener en cuenta a los rechazados, por lo que se obtendrá el porcentaje de rechazo. En este caso, se estima la curva de riesgos sobre los buckets que se tienen datos y que dará una expresión del tipo:


ree

Así pues se tendría inferido el dato final necesario que permitiría inferir el importe impagado en la zona desconocida.

Nótese que lo que se ha hecho ha sido una construcción similar a lo comentado en el anterior post pero desde otro punto de vista.


ree

Figura 2 Esquema gráfico análogo al del anterior post

 

En este caso, se tiene el cutoff aplicado a la población mediante el modelo puesto en producción pero ¡Ojo! Yo siempre digo que: “los modelos no producen dinero, sino que lo producen quienes lo aplican”, en un modelo de riesgo, la producción de dinero, cuando el modelo es bueno, viene de su aplicación natural mediante el rechazo activo de los clientes que probablemente impagen.


Además del cutoff, se conoce toda la exposición antes y después de éste ya que en el momento de rechazo se ha intentado adquirir el producto financiero con su coste y en general (si no habría que estimarlo en función de lo conocido) se conoce también el ingreso que se habría obtenido de haberse contratado. Finalmente mediante la inferencia se va tener también el valor del impago que se habría producido de no haber aplicado el modelo por lo comentado anteriormente, por tanto se tiene toda la información necesaria de lo que se ha denominado Detection Zone que ahora cabría denominarse Rejection Zone. Así pues se tienen las siguientes cantidades:

 


Profits without Model = (Inferred Income + Income) – (Inferred Exposure at Default + Exposure at Default)

    

Profits with Model = Income – Exposure at Default

 

El valor por tanto que aportaría el modelo sería:

 

Model Add value = Profits with Model – Profits without Model = Inferred Income – Inferred Exposure at Default


Nótese que esta metodología, no es mucho más complicada que la anterior y tan sólo hay que añadir la estimación de la curva de riesgos y cabe seguirla periódicamente. Además al igual que en el anterior caso, también permitiría la comparación de modelos y también se observa que por lo general a mejores Ginis (o ROCs) para una misma cartera, mayor valor añadido por parte de los modelos utilizados.


Varios comentarios a todo esto:

 

  • Model Add value no es más que la diferencia entre el ingreso que se habría obtenido por comisiones o fees al cliente (que pueden conocerse en muchos casos con precisión pero que en otras ocasiones deberá ser estimado) menos el riesgo que se habría evitado, este último número será mayor cuanto “más empinada” sea la curva, además es una función que dependerá de los siguientes elementos:

 

El nivel de demanda del producto (o número de transacciones)

La calidad en riesgo de la comentada demanda

El cutoff elegido

La política de precios

   El Gini o capacidad discriminante del modelo

 

  • Nótese que si se sube el precio o las comisiones, manteniéndose todo lo demás constante, el Model Add value disminuiría y tiene su lógica porque las comisiones irían cubriendo los posibles riesgos que tendrían lugar y entonces o se consigue un modelo más eficiente o dicho valor cae (esto tiene bastantes connotaciones adicionales que no voy a comentar aquí)

 

  • En esta metodología no están incluidos los siguientes costes:

 

Costes financieros: el principal es el tipo de interés del dinero que al menos va a ser el nivel euribor y en el caso de las financieras cabe esperar que sea ligeramente superior

   Costes operativos: en general hay reglas y variables externas que tienen un precio y que se paga por exposición aceptada e incluso rechazada

Costes de estructura: donde cabe al menos considerar el coste del personal implicado en generar y mantener el producto financiero

Todo lo anterior indica que si por ejemplo me compro un ordenador a plazos en Amazon por 1000 € a pagar durante un año ¿Cuánto me debe cobrar Amazon en términos de financiación de esta compra sabiendo que su tasa de impago es de un 3%? Pues por lo pronto tendrá que tomar dinero prestado para financiar el producto seguramente al 4% si consigue un “precio amigo” dado que actualmente el euribor está al 3.5%, aparte hará consultas a bureau de crédito y otros agentes que podrá suponer en el mejor de los casos unos 10€ lo que añade un 1% (o algo más) a todo y finalmente el coste de mantener a un personal que haga la gestión, que gestione el dato, que plantee un plan de recobro en caso de impago, etc. Por tanto al menos

entre un 8% - 10% para no incurrir en pérdidas. Actualmente, para este tipo de

compras la TAE de Amazon ronda el 13% como se observa a continuación:


ree

  • Lo anterior hace que, aunque en general es rentable tener modelos, hay que seguir su evolución a nivel de poder discriminante y de valor añadido con períodos de tiempo lo más cortos pero a la vez, lo más representativos posible


ree

Figura 3: Tabla de seguimiento de la eficiencia de una cartera de pago aplazado a 3 meses con algunas columnas relevantes (ac significa after cutoff y bc before cutoff)

Nota: (1) Hay borrones y eliminación de datos a propósito en la anterior figura

            (2) Nótese que Global Profits sería Profits without model y Profits ac sería Profits with model

 

Para finalizar, cabe comentar que mientras el método del post anterior es útil para analizar variaciones en la política de riesgo al crear escenarios por variación del cutoff, este método es menos restrictivo si bien es cierto que la inferencia realizada con la curva de riesgos será muy imprecisa en modelos malos y en estos casos, donde no se sea capaz de discriminar adecuadamente el riesgo, las tasas de la región de rechazo serán por lo general las tasas medias, lo que también tiene sentido que sea así, ya que para esos casos se tendría una curva plana.

 

 
 
 

© 2021 by Francisco J. Rodríguez Aragón. Proudly created with Wix.com

bottom of page