Artículo
· 27 sep, 2023 Lectura de 6 min

Diversión con IntegratedML Time Series

Recientemente @Anastasia Dyubaylo publico un post (este) en el que mostraba una nueva funcionalidad de IntegratedML para predicciones de series temporales que ya nos presentó @Thomas Dyar en el Global Summit 2023 y me entró la curiosidad de verlo funcionar en acción así que ¡qué mejor forma de verlo en funcionamiento que montando un pequeño workshop!

Introducción

Para este workshop hemos elegido como temática la predicción de usuarios mensuales del Metro de Valencia línea por línea. Para ello contamos con los datos mensuales desglosados por líneas desde 2022 así como los datos anuales por líneas desde 2017 que extrapolaremos mensualmente.

Una vez recopilada de la afluencia de usuarios mensual nos permitirá disponer de un fichero CSV tal que así:

Year,Month,Passengers,Line
2017,1,549007,1
2017,1,515947,2
2017,1,770564,3
2017,1,621256,5
2017,1,429640,7
2017,1,520692,9
2017,1,322392,4
2017,1,95473,6
2017,1,18188,8
2017,1,0,10

En nuestro archivo csv hemos incluido una línea final ",,," que nos permitirá saber cuando ha concluido el fichero.

Mapeando archivos CSV

A pesar de que en el proyecto incluido en este artículo ya viene realizado el mapeo, vamos a detallarlo para aquellos de vosotros que no estéis familiarizados con Record Mapper.

Como podéis ver en la imagen anterior, la primera fila será la cabecera del fichero y disponemos de los datos de año, mes, afluencia de pasajeros y línea. Para registrar dicho CSV en nuestra base de datos de IRIS usaremos la funcionalidad de Record Mapper, a la cual accederemos desde el Portal de Gestión desde las opciones del menú Interoperability -> Build -> CSV Record Wizard. Una vez que hayamos accedido rellenaremos los campos como indicamos:

Tened en cuenta que nuestro fichero CSV cuenta de cabeceras, por lo que hemos marcado la opción de Sample has header row. Con esa información IRIS nos generará automáticamente el mapeo del CSV, mostrándose a continuación la siguiente pantalla:

No será necesario que cambiemos nada y pulsando en Generate el sistema creará automáticamente todas las clases necesarias para el mapeo de CSV desde nuestras producciones.

Configuración de la Producción

Echemos un vistazo a la producción que se encuentra configurada y en funcionamiento en nuestro proyecto:

Como vemos, nuestra producción la forman 3 Business Components veamos un poco más en detalle que hace cada uno:

CSVIn

Este Business Service es de la clase EnsLib.RecordMap.Service.FileService y nos servirá para capturar los ficheros CSV que se encuentren en una ruta indicada en su configuración. Por cada línea del fichero CSV que capture creará un object del tipo MLTEST.PassengersMap.Record definido por el RecordMap que tenga configurado, en nuestro caso PassengersMap y lo enviará al Business Component indicado en TargetConfigNames.

PredictionModelGeneration

Este Business Process viene definido por el BPL MLTEST.BP.PopulateTable desde el que transformaremos los datos en crudo recibidos al formato que más nos interesa para entrenar nuestro modelo. Echemos un vistazo al BPL:

Entremos un poco más en detalle en el diagrama:

Primeramente comprobaremos si el objeto recibido corresponde a un dato o es el indicador del final del fichero que hemos definido previamente.

Objeto NO final de fichero:

En caso de que no sea el final del fichero procederemos a transformar el dato en crudo recibido por algo más entendible para nuestro modelo y para ello hemos definido un pequeño DTL MLTEST.DT.ParseDate

Este DTL únicamente nos generará un objeto de tipo MLTEST.Data.PassengersInfo con una fecha de tipo DateTime formada con los datos procedentes del CSV. Una vez concluida la tranformación el BPL se limitará a grabar dicho objeto en nuestra base de datos que se encuentra en la variable de contexto PassengersInfo.

 set sc = context.PassengersInfo.%Save()
 if ($$$ISERR(sc))
 {
   set context.Result = "Error"
   set context.ErrorMessage = sc
 }
 else
 {
   set context.Result = "Success"
 }

Objeto final de fichero:

Si hemos terminado de leer el fichero deberemos proceder a formatear los datos para que el modelo de Time Series los comprenda. ¡¡ATENCIÓN!! debemos tener en consideración los siguientes dos puntos (y que yo no tuve en cuenta 🙄):

  1. Es necesario disponer de un campo del tipo DateTime que se usará para continuar la serie temporal, no bastará con un tipo Date.
  2. Los registros deberán estar ordenados del registro más antiguo al más actual para que el entrenamiento funcione correctamente.

En el caso de que ya tenemos el modelo generado nos encargaremos de borrarlo así como los datos en la tabla de entrenamiento. El proceso continuará del siguiente modo:

 

Veamos en detalle cada paso:

  1. Realizamos una inserción masiva con los datos transformados.
    INSERT INTO MLTEST_Data.PassengersLine (DateOfData, Line1, Line2, Line3, Line4, Line5, Line6, Line7, Line8, Line9, Line10) 
    SELECT DateOfData, 
    (SELECT MAX(s1.Passengers) FROM MLTEST_Data.PassengersInfo s1 WHERE s1.Line = 1 AND s1.DateOfData = s.DateOfData) as Line1, 
    (SELECT MAX(s2.Passengers) FROM MLTEST_Data.PassengersInfo s2 WHERE s2.Line = 2 AND s2.DateOfData = s.DateOfData) as Line2, 
    (SELECT MAX(s3.Passengers) FROM MLTEST_Data.PassengersInfo s3 WHERE s3.Line = 3 AND s3.DateOfData = s.DateOfData) as Line3, 
    (SELECT MAX(s4.Passengers) FROM MLTEST_Data.PassengersInfo s4 WHERE s4.Line = 4 AND s4.DateOfData = s.DateOfData) as Line4, 
    (SELECT MAX(s5.Passengers) FROM MLTEST_Data.PassengersInfo s5 WHERE s5.Line = 5 AND s5.DateOfData = s.DateOfData) as Line5,
    (SELECT MAX(s6.Passengers) FROM MLTEST_Data.PassengersInfo s6 WHERE s6.Line = 6 AND s6.DateOfData = s.DateOfData) as Line6, 
    (SELECT MAX(s7.Passengers) FROM MLTEST_Data.PassengersInfo s7 WHERE s7.Line = 7 AND s7.DateOfData = s.DateOfData) as Line7, 
    (SELECT MAX(s8.Passengers) FROM MLTEST_Data.PassengersInfo s8 WHERE s8.Line = 8 AND s8.DateOfData = s.DateOfData) as Line8, 
    (SELECT MAX(s9.Passengers) FROM MLTEST_Data.PassengersInfo s9 WHERE s9.Line = 9 AND s9.DateOfData = s.DateOfData) as Line9, 
    (SELECT MAX(s10.Passengers) FROM MLTEST_Data.PassengersInfo s10 WHERE s10.Line = 10 AND s10.DateOfData = s.DateOfData) as Line10 
    FROM MLTEST_Data.PassengersInfo s GROUP BY DateOfData
  2. Creamos nuestro modelo de tipo TIME SERIES, indicando los valores a predecir y la columna que contiene la serie temporal del tipo DateTime (DateOfData):
    CREATE TIME SERIES MODEL PassengersPrediction 
    PREDICTING (Line1,Line2,Line3,Line4,Line5,Line6,Line7,Line8,Line9,Line10) 
    BY (DateOfData) 
    FROM MLTEST_Data.PassengersLine USING {"Forward":3}
    
  3. Entrenamos nuestro modelo:
    TRAIN MODEL PassengersPrediction
  4. Y finalmente llamamos al Business Operation PredictionToCSV que nos generará la predicción y la registrará en un CSV.

PredictionToCSV

Este Business Operation es extremadamente sencillo y todo gracias al proyecto de @Evgeny Shvarov csvgen que nos permite exportar directamente los resultados de una consulta SQL a un archivo CSV de una forma directa. Aquí podéis ver el código de dicho BO:

Class MLTEST.BO.PredictionToCSV Extends Ens.BusinessOperation
{

Parameter INVOCATION = "Queue";
Method ExportPrediction(pRequest As Ens.Request, pResponse As Ens.Response) As %Status
{
    set query="SELECT WITH PREDICTIONS (PassengersPrediction) * FROM MLTEST_Data.PassengersLine"
    w ##class(community.csvgen).SQLToCSV(",",1,"/shared/prediction.csv",query)

    Quit $$$OK
}

XData MessageMap
{
<MapItems>
  <MapItem MessageType="Ens.Request">
    <Method>ExportPrediction</Method>
  </MapItem>
</MapItems>
}

}

Como véis la consulta encargada de obtener la predicción no es nada complicada.

SELECT WITH PREDICTIONS (PassengersPrediction) * FROM MLTEST_Data.PassengersLine

Veamos cual es el resultado generado en el archivo /shared/prediction.csv

Conclusiones

Como véis, la creación de los modelos de predicción de series temporales es relativamente sencilla, únicamente deberéis aseguraros de dispones de una columna de tipo DateTime ordenada correctamente para que el entrenamiento sea el más adecuado.

Si tenéis alguna sugerencia o comentario no dudéis de dejarla en la sección de comentarios. ¡Muchas gracias por vuestro tiempo!

Comentarios (0)1
Inicie sesión o regístrese para continuar