martes, 5 de marzo de 2019

Power Query: List.Transform y List.Accumulate alternativas a List.Sum

Tiempo atrás escribí sobre cómo obtener un 'Running total' (acumulado creciente) sobre una tabla (ver aquí), donde empleamos una función M de Power Query: List.Sum.

Hoy nos adentraremos un poco más en este lenguaje y descubriremos algunas 'joyas' de esta programación:
List.Buffer(lista as list)
Carga la lista en la memoria. El resultado de esta llamada es una lista estable, lo que significa que tendrá un recuento determinado y un orden de los elementos.

List.Transform(lista as list, transformación as function)
Aplica la función/transformación en cada elemento de la lista y devuelve una nueva lista transformada.

List.Accumulate(lista as list, valor_incial as any, acumulador as function)
Acumula el resultado a partir de los valores de la lista dada; i.e., a partir del valor inicial, esta función aplica la función del acumulador y devuelve el resultado final acumulado.


En su momento empleamos la función List.Sum efectiva y válida cuando hay 'pocos' elementos sobre los que trabajar.. pero en ocasiones nuestras tablas contienen un número elevado de registros y List.Sum se hace 'pesado' y 'lento'.
Será en estos casos cuando podremos emplear las alternativas comentadas...


Partiremos de una tabla sencilla con cuatro campos: Fecha / Concepto / Debe / Haber

Power Query: List.Transform y List.Accumulate alternativas a List.Sum



Trabajaremos de dos maneras.
Primero usando List.Transform y List.Buffer.

Cargaremos la Tabla al Editor de Power Query y en el editor avanzado añadiremos el siguiente código comentado:
let
    //carga los datos de la Tabla 'TblMov'
    Origen = Excel.CurrentWorkbook(){[Name="TblMov"]}[Content],
    //obtenemos el cálculo del saldo como diferencia de Debe-haber
    colSaldo = Table.AddColumn(Origen, "Saldo", each (if [Debe] = "" then 0 else [Debe]) - (if [Haber] = "" then 0 else [Haber])),
    //añadimos un columna de índice
    colIndice = Table.AddIndexColumn(colSaldo,"TempIndex"), 
    
    //recuperamos(duplicamos el dato de la columna de Saldo
    Input = colIndice[Saldo],
    //generamos una lista nueva para cada registro de la tabla, 
    //calculada como la suma del saldo del registro más el dato del registro anterior del campo que estamos creando
    //y la cargamos en memoria con List.Buffer
    Output = List.Buffer(List.Transform({0..List.Count(Input)-1}, each Input{_} + (if _ = 0 then 0 else @Output{_-1}))),

    //recuperamos de la memoria el dato acumulado
    colAcumulado = Table.AddColumn(colIndice, "Acumulado", each Output{[TempIndex]})
in
    colAcumulado


Power Query: List.Transform y List.Accumulate alternativas a List.Sum


Con lo que obtendremos es siguiente resultado esperado...

Power Query: List.Transform y List.Accumulate alternativas a List.Sum



Segunda forma usando List.Accumulate.
Añadiremos una consulta en blanco y desde el editor avanzado añadiremos el siguiente código comentado:
let
    //cargamos la tabla desde la hoja de cálculo
    Origen = Excel.CurrentWorkbook(){[Name="TblMov"]}[Content],
    //calculamos el Saldo para cada registro como diferencia de Debe-Haber
    colSaldo = Table.AddColumn(Origen, "Saldo", each (if [Debe] = "" then 0 else [Debe]) - (if [Haber] = "" then 0 else [Haber])),
    //añadimos una columna de índice
    colIndice = Table.AddIndexColumn(colSaldo,"TempIndex"), 
    
    //añadimos una columna personalizada con el cálculo Acumulado requerido
    //List.Accumulate acumula el rango variable obtenido por List.Range
    Fin = Table.AddColumn(colIndice, "Acum", 
                    each List.Accumulate(
                        List.Range(colIndice[Saldo],0,[TempIndex]+1),
                        0,
                        (state, current) => state + current
                    ))
in
    Fin


Power Query: List.Transform y List.Accumulate alternativas a List.Sum


A partir de lo que obtenemos...

Power Query: List.Transform y List.Accumulate alternativas a List.Sum



Consiguiendo nuestra meta por dos nuevos caminos...

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.