martes, 11 de mayo de 2021

Power Query: Obtener último valor condicionado

Hoy veremos diferentes alternativas con Power Query para recuperar los últimos valores registrados, de acuerdo a distintas condiciones.
A partir de nuestra tabla de datos, deseamos recuperar aquellos registros únicos que respondan a un criterio múltiple (País y Producto), pero que hayan sido los últimos en registrarse según un campo de orden, fecha, etc...
Power Query: Obtener último valor condicionado

En concreto veremos tres técnicas que, con algunas diferencias, dan respuesta a nuestro problema.
En la imagen anterior, he señalado en color rojo los registros esperados, que responden a los últimos registros ocurridos para esa dupla: país+producto.

Cargamos la tabla al editor de Power Query.
Y duplicamos en dos ocasiones la consulta creada (hasta tener tres 'queries' iguales.

Las renombraremos como:
-TblORDEN_Opcion1
-TblORDEN_Opcion2
-TblORDEN_Opcion3

En la primera de ellas 'TblORDEN_Opcion1' usaremos la técnica de Quitar duplicados.
Para esto deberemos ordenar adecuadamente nuestros registros...
Por ejemplo ordenando por el campos: 'Orden' (en Descendente!!!, para que los más recientes queden en la parte superior)
Luego Quitaríamos duplicados (en el Editor de Power Query - botón derecho y Quitar duplicados sobre los campos: País & Producto)
La secuencia grabada sería:
let
    Origen = Excel.CurrentWorkbook(){[Name="TblORDEN"]}[Content],
    Tipo_Cambiado = Table.TransformColumnTypes(Origen,{{"Orden", Int64.Type}, {"País", type text}, {"Producto", type text}, {"Precio", type number}}),
    
    Filas_Ordenadas = Table.Buffer(Table.Sort(Tipo_Cambiado,{{"Orden", Order.Descending}})),
    Duplicados_Quitados = Table.Distinct(Filas_Ordenadas, {"País", "Producto"}),
    Filas_Ordenadas1 = Table.Sort(Duplicados_Quitados,{{"País", Order.Ascending}, {"Producto", Order.Ascending}})
in
    Filas_Ordenadas1
O algo más breve:
let
    Origen = Excel.CurrentWorkbook(){[Name="TblORDEN"]}[Content],
    Tipo_Cambiado = Table.TransformColumnTypes(Origen,{{"Orden", Int64.Type}, {"País", type text}, {"Producto", type text}, {"Precio", type number}}),
    
    Filas_Ordenadas = Table.Buffer(Table.Sort(Tipo_Cambiado,{{"País", Order.Ascending}, {"Producto", Order.Ascending}})),
    Duplicados_Quitados = Table.Distinct(Filas_Ordenadas, {"País", "Producto"})
in
    Duplicados_Quitados

En ambos casos el resultado es el correcto:
Power Query: Obtener último valor condicionado
Fundamental en este caso el empleo de Table.Buffer.
En una entrada posterior explicaremos el por qué.

Otra posible solución la damos en la Opción 2.
Donde los pasos dados serían:
1- Agrupar por 'País' y 'Producto'
2- Modificar algo el código obtenido en Table.Group para conseguir ver el valor máximo del campo Orden
3- Acabar filtrando según las coincidencias obtenidas.

El código completo:
let
    Origen = Excel.CurrentWorkbook(){[Name="TblORDEN"]}[Content],
    TipoCambiado = Table.TransformColumnTypes(Origen,{{"Orden", Int64.Type}, {"País", type text}, {"Producto", type text}, {"Precio", type number}}),

    //agrupamos por País y Producto
    //Y definimos que el cálculo a ver sea el dato mayor del campo Orden (List.Max([Orden])
    FilasAgrupadas = Table.Group(TipoCambiado, {"País", "Producto"}, {{"Último", each List.Max([Orden]), type number}, {"ListPrecios", each _, type table [Orden=nullable number, País=nullable text, Producto=nullable text, Precio=nullable number]}}, GroupKind.Local),

	//expandimos la agrupación, y recuperamos de nuevo los campos Orden y Precio
    SeExpandió_ListPrecios = Table.ExpandTableColumn(FilasAgrupadas, "ListPrecios", {"Orden","Precio"}, {"Orden","Precio"}),
    
    //para finalmente, en el conjunto de todas las combinaciones posibles, 
    //quedarnos con aquellos registros donde coincidan Orden y Ultimo
    Filas_Filtradas = Table.SelectRows(SeExpandió_ListPrecios, each ([Orden] = [Último])),
    //reordenamos las columnas
    Columnas_Reordenadas = Table.ReorderColumns(Filas_Filtradas,{"Orden", "País", "Producto", "Precio", "Último"})
in
    Columnas_Reordenadas

el resultado es el mismo... solo que este método requiere de más tiempo en su ejecución. Especialmente notable con bases de datos grandes...

La opción 3 es una variante de la anterior, pero con un poco de lenguaje M avanzado.
Vemos el código picado:
let
    Origen = Excel.CurrentWorkbook(){[Name="TblORDEN"]}[Content],
    TipoCambiado = Table.TransformColumnTypes(Origen,{{"Orden", Int64.Type}, {"País", type text}, {"Producto", type text}, {"Precio", type number}}),

    //agrupamos por Pais y Producto
    //sobre los campos resultantes de una tabla filtrada por los registros correspondientes al valor de orden más alto
    //se consigue ordenando por Descendente el campo Orden, y asignandole un Indice 1, y negativos al posible resto de registros
    // filtrando solo con aquellos cuyo Indice =1, esto es, el mayor valor
    FilasAgrupadas = Table.Group(
        TipoCambiado,
        {"País", "Producto"},
        {
            {"Último", each  Table.SelectRows(
                [tbl=Table.AddIndexColumn( Table.Sort(_,{{"Orden", Order.Descending}}), "Index", 1, -2, Int64.Type)][tbl] , 
                            each ([Index] = 1))}
        }
    ),
    
    //expandimos la tabla resultante de un solo registro
    Expande_ListPrecios = Table.ExpandTableColumn(FilasAgrupadas, "Último", {"Orden","Precio"}, {"Orden","Precio"}),

    //y reordenamos las columnas
    ColumnasReordenadas = Table.ReorderColumns(Expande_ListPrecios,{"Orden", "País", "Producto", "Precio"})
in
    ColumnasReordenadas


Tres técnicas interesantes a aplicar según el caso y el volumen de registros...

No hay comentarios:

Publicar un comentario

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