martes, 29 de septiembre de 2020

Power Query: Añadir Subtotales a una consulta

Recientemente hablaba con un cliente de la posibilidad de añadir subtotales dentro de una consulta de Power Query...
Es sabido por todos que no es una opción desde la herramienta de Subtotales (al margen de lo poco práctico para el trabajo)... pero la cuestión quedó en el aire y me puse a investigar y probar, y aquí estamos.
Power Query: Añadir Subtotales a una consulta

Increible!!... con algunos pasos y funciones M de una consulta en el editor de Power Query, sí es posible añadir subtotales!!!.
¿Que funciones M debemos conocer?:
1. Table.SelectRows(table as table, condition as function)
que nos devuelve una tabla de filas de table, que coincide con la selección condition; o sea, aplicar un filtro con una condición o varias.
2. Table.Group(table as table, key as any, aggregatedColumns as list, optional groupKind as nullable number, optional comparer as nullable function)
muchos argumentos para esta función que nos permite agrupar.. pero muchos opcionales que no suelen emplearse.
La conoceras por que frecuentemente llegamos a ella desde el botón de Agrupar por
3. Table.Combine(tables as list, optional columns as any)
Sirve para Anexar tablas (una debajo por otra, verticalmente).
4. Última relevante, al margen de otras que también emplearemos: #table(columns as any, rows as any)
Crea un valor de tabla a partir de las columnas y filas donde cada elemento de la lista es una lista interna que contiene los valores de columna de una sola fila.
El argumento columns puede ser una lista de nombres de columna, un tipo de tabla, un número de columnas o NULL.
MUY importante esta función, ya que es ésta la que va a añadir esos subtotales!!

Comencemos...
Partimos de la tabla de regiones de Europa, paises, zonas, comerciales, unidades de nuestra hoja de cálculo. Tabla que cargaremos al editor de consultas de Power Query como siempre (desde la ficha Datos > grupo Obtener y transformar datos > botón Desde tabla o rango)
Con la tabla cargada, y ya en el editor, accedemos al Editor Avanzado para incluir el siguiente código M:
let
    Origen = Excel.CurrentWorkbook(){[Name="Tabla1"]}[Content],   
    //
    AgrupaPAIS = 
        Table.SelectRows(
            Table.Group(Origen, {"País"}, {"temporal", each _ 
                & #table(
                    {"País", "UDS"}, 
                        {{
                            "Subtotal " & [País]{0},
                            List.Sum([UDS])
                        }}
                    )}),
        each [País] <> null and [País] <> ""),
    CombinacionPAIS = Table.Combine(AgrupaPAIS[temporal]),
    //
    DuplicadaEU = Table.DuplicateColumn(CombinacionPAIS, "Europa", "Europa_"),
    RellenaEU = Table.FillDown(DuplicadaEU,{"Europa_"}),
    //
    AgrupaEU = 
        Table.SelectRows(
            Table.Group(RellenaEU, {"Europa_"}, {"temporal_2", each _ 
                & #table(
                    {"Europa", "UDS"}, 
                        {{
                            "Total " & [Europa_]{0} ,
                            List.Sum([UDS])/2
                        }}
                    )}),
        each [Europa_] <> null and [Europa_] <> ""),
    //
    CombinacionEU = Table.Combine(AgrupaEU[temporal_2]),
    ColumnasQuitadas = Table.RemoveColumns(CombinacionEU,{"Europa_"})
in
    ColumnasQuitadas
Power Query: Añadir Subtotales a una consulta
Explicamos paso a paso...
El primer paso:
Origen = Excel.CurrentWorkbook(){[Name="Tabla1"]}[Content],   

ha cargado la tabla a la consulta...

El segundo paso:
AgrupaPAIS = 
        Table.SelectRows(
            Table.Group(Origen, {"País"}, {"temporal", each _ 
                & #table(
                    {"País", "UDS"}, 
                        {{
                            "Subtotal " & [País]{0},
                            List.Sum([UDS])
                        }}
                    )}),
        each [País] <> null and [País] <> ""),

Aquí está la clave.. OJO!!!.
Agrupamos por País, añadiendo un campo 'temporal', donde en lugar de realizar alguna acumulación u operación agregada, añade o anexa al detalle de elementos correspondientes a cada 'País' una 'nueva tabla' de dos columnas 'País' y 'UDS' con un solo registro:
Para 'País': Subtotal y el nombre del País agrupado (por ejemplo, 'Subtotal ES')
Para 'UDS': la Suma Acumulada de dicho País
Algo como esto...
Power Query: Añadir Subtotales a una consulta

Sobre el resultado de esa tabla 'temporal' aplicamos un filtro, con Table.SelectRows con un filtro aplicado sobre la columna 'País' para descartar valores vacíos o nulos (each [País] <> null and [País] <> "")

El tercer paso es simple:
CombinacionPAIS = Table.Combine(AgrupaPAIS[temporal]),   

Aquí anexamos cada tabla obtenida, de cada País, con el paso anterior, una a continuación de la otra.
Power Query: Añadir Subtotales a una consulta

En los dos siguientes pasos:
 DuplicadaEU = Table.DuplicateColumn(CombinacionPAIS, "Europa", "Europa_"),
 RellenaEU = Table.FillDown(DuplicadaEU,{"Europa_"}),

Donde empleando los asistentes hemos duplicado la columna 'Europa' y renombrado como 'Europa_', y a continuación hemos Rellenado hacía abajo (clic derecho sobre el nuevo campo, opción Rellenar > Abajo), con el siguiente resultado.
Power Query: Añadir Subtotales a una consulta

Este paso, donde añadimos una nueva columna de 'Europa' es fundamental para poder añadir el siguiente Subtotal!!

Vamos al siguiente paso, donde al igual que hicimos para 'País', replicaremos el mismo proceso:
AgrupaEU = 
        Table.SelectRows(
            Table.Group(RellenaEU, {"Europa_"}, {"temporal_2", each _ 
                & #table(
                    {"Europa", "UDS"}, 
                        {{
                            "Total " & [Europa_]{0} ,
                            List.Sum([UDS])/2
                        }}
                    )}),
        each [Europa_] <> null and [Europa_] <> ""),

Observa que los pasos son idénticos, excepto en elemento añadido en el campo 'UDS' de la tabla 'temporal_2' que hemos forzado la Suma Acumulada de las 'UDS' de la región de 'Europa_' dividida entre dos para evitar el subtotal anterior por País !!.
Para cada Región de 'Europa_' tendremos los registros de dicha región y al final el añadido con '#table:
Para 'Europa_': Total y el nombre de la región de Europa_ agrupado (por ejemplo, 'Total CENTRO')
Para 'UDS': la Suma Acumulada de dicha región divida por dos (List.Sum([UDS])/2)
NO OLVIDES que hemos aplicado un filtro sobre lo anterior para descartar los valores vacíos o nulos!!!
Algo como esto...
Power Query: Añadir Subtotales a una consulta


El siguiente paso obvio, es Anexar esas regiones con sus subtotales:
CombinacionEU = Table.Combine(AgrupaEU[temporal_2]),
Power Query: Añadir Subtotales a una consulta


Último paso, eliminamos la columna 'Europa_' que ya no necesitamos:
ColumnasQuitadas = Table.RemoveColumns(CombinacionEU,{"Europa_"})

LLegando al resultado esperado!!.
Ya podemos Cerrar y Cargar en... sobre la hoja de cálculo.
Por estética final, y ya en la tabla de la hoja de cálculo, aplicamos un par de reglas de formato condicional sobre los campos Europa y País para dar 'algo de color' a las filas de los parciales:
Power Query: Añadir Subtotales a una consulta

Y para rematar, incorporamos a la Tabla de la hoja de cálculo la Fila de totales, personalizando la fórmula del campo 'UDS' con:
=SUMAR.SI([Europa];"Total*";[UDS])
Power Query: Añadir Subtotales a una consulta


Increible trabajo, y laborioso... ;-)

jueves, 24 de septiembre de 2020

Power Query: #shared-Listado de funciones M

Algún buen amigo me ha 'reprochado' que mis explicaciones, en bastantes ocasiones son de temas demasiados complejos, y que no suelo escribir sobre 'asuntos más normales'... No es que esté del todo de acuerdo, pero para sentirme tranquilo, hoy explciaré un buen truco para acceder a la biblioteca de funciones M dentro del editor de Power Query.
Supongamos que solo me interesa llegar a ese listado... así pues accedo, dentro de Excel, a la ficha Datos > grupo Obtener y transformar > desplegable Obtener datos > Desde otras fuentes > Consulta en blanco
Power Query: #shared-Listado de funciones M

Esto abrirá el editor de Power Query con una consulta vacía, sin pasos...
En la barra de fórmulas de la consulta escribiremos:
= #shared
Power Query: #shared-Listado de funciones M

Tras presionar Enter veremos el resultado de la consulta creada, con un listado de todas las funciones M disponibles en Power Query...
Power Query: #shared-Listado de funciones M
Con un funcionalidad adicional, en el listado de funciones tenemos dos columnas, siendo la segunda ('Record') un link a una ventana de ayuda de esa función!!
Power Query: #shared-Listado de funciones M

Basta hacer clic sobre cualquiera de ellas para que se abra una ventana con una breve descripción de esa función, ejemplos, etc...
OJO, por que añade a nuestra consulta un paso con esa función elegida...

De igual forma, podemos realizar búsquedas aplicando filtros sobre la primera columna, buscando por el nombre de la función

Un buen truco, y rápido, si no tuvieramos acceso a internet en ese momento ;-)

martes, 22 de septiembre de 2020

Power Query: El tamaño de los ficheros importa

Toca en el día de hoy analizar el impacto de trabajar con Power Query sobre el tamaño de nuestros ficheros (y su consecuente ralentización).
Partiremos de un fichero .csv con datos inventados de diferentes países por meses, con un total de 380.160 registros.
El peso inicial de este archivo .csv: 29.442 Kb.

Así pues, nuestro primer paso será abrir un libro de Excel (.xlsx) y cargar los datos a una consulta de Power Query.
Desde la Ficha Datos > grupo Obtener y transformar > Obtener datos > Desde un archivo > Desde texto/csv
Power Query: El tamaño de los ficheros importa

Indicaremos la ruta de nuestro fichero .csv y en un primer término simplemente Cargaremos y cerraremos en una Tabla
Power Query: El tamaño de los ficheros importa

Es decir, en este primer experimento no hay tratamiento o transformación de datos.
Tras esperar a la actualización y comprobar que se han cargado las 380.160 filas, procedemos a guardar nuestro libro y comprobar el peso de este... que es de 29.512 Kb.
Unos bytes por encima del peso original del fichero .csv.
Poco esperanzador :-(

Vamos a por una alternativa... Cargaremos y cerraremos en modo Tabla dinámica
Power Query: El tamaño de los ficheros importa

Esperamos a que la consulta actualice sus 380.160 filas y guardamos el fichero.
Ahora su tamaño ha bajado a 10.613 Kb (más o menos un tercio del tamaño original!!) :O
En este caso, al devolver la info a través de un informe de tabla dinámica, y por tanto, no tener la info 'repetida' en la vista de hoja de cálulo, el peso del fichero es sustancialmente menor.

Otra variante asombrosa sería Cargar y cerrar en Solo conexión...
Dejando en esta situación, solo conexión, un peso del fichero de 16 Kb !!!!.
Disponible para combinar con otras posibles consultas si fuera necesario.

En estos casos descritos no ha habido transformación alguna de columnas o filas.. ¿pero que pasaría si Quitamos columnas innecesarias de nuestra consulta??.
Veamos que ocurre si eliminamos algunas columnas (hasta 9 de las 15 existentes en el .csv original).
Tras eliminar dichas columnas volvemos a Cargar y cerrar en modo Tabla, y verificar que se han actualizado las 380.160 filas, guardamos de nuevo el libro de trabajo y comprobamos que su peso es de 10.430 kB
Recuerda que el peso del fichero, con todas sus columnas y filas cargadas y mostradas en una Tabla (primera prueba realizada) era de 29.512 Kb; es decir, eliminando columnas reducimos peso... en mi ejemplo de 29.512 a 10.430.

Si en lugar de cargar y cerrar en una tabla, lo mostramos en un informe de tabla dinámica con las columnas ya eliminadas, comprobaremos que el peso queda en unos 3.827 Kb.
Con todas sus columnas, el equivalente, tenía 10.613 kb. Una bajada de +/- 7 Mb

Continuamos ahora rebajando el número de filas aplicando algún filtro sobre un campo de años
Asi pues, con todas sus columnas de origen y mostrada la consulta sobre una Tabla en la hoja de cálculo, aplicamos un criterio sobre la columna de año para mostrar solo años posteriores a 1975 (los datos originales eran de 1900 a 2019).
Se nos quedan por tanto las 15 columnas originales pero solo con 142.560 filas...
y un tamaño del fichero (frente a los 29.512 Kb) de 11.046 Kb... de nuevo casi un tercio.
Si quitamos columnas, además del filtro de años aplicado, el peso sería de 3.969 Kb.

Puedes comprobar cuantas combinaciones se te ocurran... quitar columnas o aplicar filtros mostrando los datos como Tabla, Informe de tabla dinámica o Solo conexión, pero la conclusión será siempre la misma:
Lo primero que debemos hacer con nuestras consultas es eliminar columnas innecesarias y aplicar los filtros oportunos que muestren información realmente relevante.
Solo una vez hecho esto, podremos decidir en qué forma (Tabla, Tabla dinámica o Conexión solo) mostrar dicha información... según nuestras necesidades.

Una última variable, nada despreciable, aparece con Power Pivot y su Modelo de datos.
Cuando nuestra meta es claramente trabajar con un informe de tablas dinámicas, lo recomendable será realizar todo tipos de cálculos, modificaciones, transformaciones (como las ya comentadas, entre otras) con el Editor de Power Query, para una vez concluido el proceso Cargar y cerrar como Solo conexión pero Agregándolo al modelo de datos
Power Query: El tamaño de los ficheros importa

Posteriormente deberemos entrar en el Administrador de Power Pivot e insertar una Tabla dinámica desde el modelo de datos...
Esto reducirá aún más el peso del fichero, y además agilizará los procesos de cálculo de nuestro libro.

Otras recomendaciones variadas... (adicionales a las anteriores):
- Si trabajas con el Modelo de datos de Power Pivot crea Medidas mejor que Columnas calculadas
- Siempre mejor, si es inevitable, calcular columnas en Power Query que en Power Pivot
/ - Mejor Campo calculado en la tabla dinámica que como Medida en el modelo
- Si existen columnas del tipo DateTime mejor separalas en dos columnas, una solo Date, otra con Time
- Cuando sea posible redondea las columnas (evita decimales 'interminables')
Todos estos puntos, y alguno más, facilitarán que tus procesos sobre grandes bases de datos sean agiles y rápidos en sus cálculos...
Espero algo de lo expuesto te sea de utilidad...

jueves, 17 de septiembre de 2020

VBA: el cuadrado mágico de Leonhard Euler

Hoy toca un poco de programación en VBA para Excel.
Aprovechando un tema siempre interesante: el cuadrado mágico de Euler que reune dos temas apasionantes.. secuencias matemáticas y ajedrez, veremos algunas rutinas de macros interesantes.

Gráfica de un polígono de hasta 20 lados.


En qué consiste este cuadrado 'semimágico' de Euler...
/ Este gran matemático (el del número e) fue capaz de montar la secuencia natural de números del 1 al 64 sobre un tablera de ajedrez (8 x 8 = 64 escaques) distribuidos siguiendo la secuencia de movimientos de un caballo (la figura del ajedrez)!!!.
No solo eso, además cada fila y columna de sus secuencia distribuida sumarán siempre 260... pero encima, si dividimos en cuatro cuadrados de 4 x 4 nuestro tablero, cada parte suma igualmente una misma cantidad: 520 (si, el doble de 260)... increible!!.
Solo le falto que las diagonales también sumaran esos 260 para conseguir el perfecto cuadrado mágico... no se puede tener todo, verdad?.

Los valores de este cuadrado:
VBA: el cuadrado mágico de Leonhard Euler
Nuestra experiencia de hoy será replicar el camino que seguiría ese caballo por el tablero, siguiendo el orden natural 1,2,3,4,..., 64. Mostrando con flechas y colores esos movimientos.
Para ello emplearemos un truco que simula una matriz de constantes con los ocho movimientos posibles de un caballo; y por otro lado usaremos el método Shapes.Addline para añadir con flechas dichos movimientos...
Insertamos un módulo en nuestro proyecto, y en el añadimos nuestro procedimiento auxiliar que permite añadir las flechas:

Sub AñadimosFlecha(FromCell As Range, ToCell As Range)

iniX = FromCell.Left + (FromCell.Width / 2)
iniY = FromCell.Top + (FromCell.Height / 2)
finX = (ToCell.Left + (ToCell.Width / 2))
finY = (ToCell.Top + (ToCell.Height / 2))

'expresión. AddLine (BeginX, BeginY, EndX, Endy)
Set flecha = FromCell.Parent.Shapes.AddLine(iniX, iniY, finX, finY)

'seleccionamos la linea
flecha.Select
'y añadimos la terminación en forma de flecha
Selection.ShapeRange.Line.EndArrowheadStyle = msoArrowheadTriangle

End Sub

Por otra parte, en el mismo módulo, añadimos el procedimiento Sub principal:

Sub Euler()
'dejamos celdas sin color
Range("ndEuler").Interior.Color = xlNone
'y eliiminamos las formas...
Dim shp As Shape
For Each shp In ActiveSheet.Shapes
   shp.Delete
Next shp

'limites filas y columnas del cuadro
fila1 = Range("ndEuler").Row
fila8 = fila1 + 8 - 1
col1 = Range("ndEuler").Column
col8 = col1 + 8 - 1

'situación inicio
Range("ndEuler").Cells(1, 1).Select
Selection.Interior.Color = vbCyan
Application.Wait (Now + TimeValue("0:00:02") / 3)
Selection.Interior.Color = vbYellow

ValorActual = Selection.Value

'nos movemos, y probamos encontrar el valor siguiente al Actual
'con el movimiento del caballo!!

'definimos la matriz de constantes de ocho movimientos del caballo posibles...
arrMovs = [{1, -1, 2, 2, 1, -1, -2, -2; 2, 2, 1, -1, -2, -2, 1, -1}]

Dim horizontal As Integer, vertical As Integer, x As Integer, i As Integer
'forzamos los 64 escaques posibles
For i = 1 To 64
    For x = 1 To UBound(arrMovs, 2)
        fA = Selection.Row
        cA = Selection.Column
        fB = arrMovs(1, x)
        cB = arrMovs(2, x)
        
        'control dentro cuadro
        If fA + fB >= fila1 And fA + fB <= fila8 And _
            cA + cB >= col1 And cA + cB <= col8 Then
            'si el movimiento cae dentro del cuador
            If Selection.Value + 1 = Selection.Offset(fB, cB).Value Then
            'y además el valor encontrado es el siguiente...
                'y añadimos los conectores
                Call AñadimosFlecha(Cells(fA, cA), Cells(fA, cA).Offset(fB, cB))
                Cells(fA, cA).Select
                
                'marcamos la celda final
                Selection.Offset(fB, cB).Select
                Selection.Interior.Color = vbCyan
                'damos un retraso de tiempo para resaltar el cambio
                Application.Wait (Now + TimeValue("0:00:02") / 3)
                Selection.Interior.Color = vbYellow
                
                'seleccionamos celda última con valor
                Cells(fA, cA).Offset(fB, cB).Select
            End If
        End If
    Next x
Next i

End Sub

Notemos que previamente he asignado al cuadrado o rango de celdas B2:I9 el nombre definido: 'ndEuler'.

De especial interes cómo hemos definido la matriz de movimientos:
'definimos la matriz de constantes de ocho movimientos del caballo posibles... arrMovs = [{1, -1, 2, 2, 1, -1, -2, -2; 2, 2, 1, -1, -2, -2, 1, -1}]
con dos filas y ocho columnas, desde la que posteriormente poder recuperar sus pares de valores con la notación:
fB = arrMovs(1, x)
cB = arrMovs(2, x)
El resto del código no parece tener especial dificultad, ya que solo son bucles y condicionales que permiten seleccionar las celdas e ir coloreándolas...
De otra parte, para insertar las líneas, empleamos el método:
'expresión. AddLine (BeginX, BeginY, EndX, Endy)
donde definimos el inicio y fin con las propiedades .Top, .Left, .Width y .Height de Range.
El resultado se puede ver, en el video al inicio del post.
Espero te haya resultado interesante ;-)

martes, 15 de septiembre de 2020

Power Query: Seleccionado origen de datos

Recientemente me encontraba con una situación algo diferente, me parecía, donde debía resolver una consulta de Power Query eligiendo un origen entre distintas posibilidades...
Veamos el planteamiento en la imagen siguiente:
Power Query: Seleccionado origen de datos
La situación concreta es la siguiente: Disponemos de tres orígenes de información (tres tablas: Tabla1, Tabla2 y Tabla3). Deseamos construir una única consulta final (que llamaré en su momento 'qryVariable') que se alimente a mi elección, de acuerdo a la celda I2... combinándose con un cuarto origen de datos 'TblColor'.
Además cada uno de las tres fuentes tiene una denominación de campos distintos, así como un número diferente de ellos... aunque sí tienen tres conceptos en común, que serán los que necesitamos: Fecha, País, Unidades.
Es decir, por si aún no he conseguido explicar qué vamos a hacer... Vamos a construir una consulta que permite seleccionar el origen de datos desde una celda validada tipo lista.

Nuestro primer paso será cargar las tres tablas fuentes (Tabla1, Tabla2 y Tabla3) solo como conexión.
En el editor trabajaremos cada una de las tablas cargadas para renombrar las columnas necesarias de igual forma, reordenarlas y eliminar, si fuera el caso, columnas sobrantes.
Pongamos de ejemplo los pasos de la Tabla3 cargada
Power Query: Seleccionado origen de datos

En las tres tablas habrá que realizar un trabajo similar hasta llegar a mostrar únicamente tres columnas: Fecha, Pais, Uds
Igualmente, solo como conexión, la TblColor... en este caso sin transformación alguna.

En la celda I2 añadimos una validación de datos tipo Lista con tres elementos permitidos: Tabla1, Tabla2 y Tabla3.
No deberías tener problema para esta primera etapa... si fuera el caso revisa la etiqueta del blog Power.
En el segundo paso cargaremos la celda I2, a la que previamente hemos asignado u nnombre definido ('ndOrigen'), solo como conexión y la pasaremos como parámetro.
Desde la ficha Datos > grupo Obtener y transformar > Desde una tabla o rango, y una vez cargada desde el editor de consultas de Power Query, haciendo clic derecho sobre la 'casilla' presionar 'Rastrear desagrupando datos'
Power Query: Seleccionado origen de datos

Es importante pasar como parámetro este valor de la celda I2 para gestionar la futura elección del origen...

Vamos a por el paso importante. Añadiremos desde el editor de consultas una consulta en blanco, desde la ficha Inicio > grupo Nueva consultas > Nuevo origen > Otros orígenes > Consulta en blanco, y desde la vista de Editor avanzado escribiremos una sentencia condicional... pero no para trabajar entre columnas, sino para trabajar entre orígenes o consultas cargadas!!.
Aquí está la fuerza de este ejercicio ;-)
En la ventana del editor avanzado de nuestra consulta en blanco escribiremos:
let
    fuente = if ndOrigen="Tabla1" then Tabla1 else 
            if ndOrigen="Tabla2" then Tabla2 else 
            Tabla3
in
    fuente

Observamos una estructura múltiple condicional if condición 1 then salida 1 else if...then...else ...
La condición consiste en comparar nuestro parámetro 'ndOrigen' con alguno de las opciones disponibles (Tabla1, Tabla2 o Tabla3), para así dirigir la respuesta a alguno de los orígenes cargados.
Al aceptar veremos:
Power Query: Seleccionado origen de datos

Sobre esta consulta, ya desde la vista normal completaremos los pasos siguientes:
1-Combinar con la consulta de colores
2-Expandir el campo color
Power Query: Seleccionado origen de datos

let
    fuente = if ndOrigen="Tabla1" then Tabla1 else 
            if ndOrigen="Tabla2" then Tabla2 else 
            Tabla3,
    #"Consultas combinadas" = Table.NestedJoin(fuente, {"Fecha"}, TblColor, {"Año"}, "TblColor", JoinKind.LeftOuter),
    #"Se expandió TblColor" = Table.ExpandTableColumn(#"Consultas combinadas", "TblColor", {"Color"}, {"Color"})

in 
#"Se expandió TblColor"
Power Query: Seleccionado origen de datos

Y listo, ya podemos Cargar y cerrar en... y devolver la consulta a nuestra hoja de cálculo. Daríamos el trabajo por concluido. Cada vez que seleccionemos un origen diferente en I2 y actualicemos... nuestra query devolverá los datos correspondientes :O

jueves, 10 de septiembre de 2020

MEDIA GEOMÉTRICA versus MEDIA ARITMÉTICA

Hoy veremos cómo y cuándo aplicar a nuestros cálculos la MEDIA GEOMÉTRICA (la función de Excel MEDIA.GEOM) con sintáxis:
=MEDIA.GEOM(num1;num2;....)

OJO, por que si alguno de los puntos de datos ≤ 0, media devolverá el #NUM! o #VALOR!.
La media geométrica (MEDIA.GEOM) de un conjunto de números estrictamente positivos (X1, X2,…,XN) es el resultado de aplicar la raíz N-ésima del producto de los N elementos.
Lo importante es el cúando emplearla... La misma ayuda de Excel nos da una pista: [sic]puede usar MEDIA.GEOM para calcular la tasa de crecimiento promedio, dado un interés compuesto por tasas variables.[sic].
En definitiva, la media geométrica es útil para calcular medias de porcentajes, tantos por uno, puntuaciones o índices. Con la ventaja de que no ser tan sensible a los valores atípicos o extremos como la media aritmética (PROMEDIO en Excel).

Veamos un ejemplo con unas previsiones de ventas para los siguientes cinco años, con tasas de variación de un año respecto al anterior:
MEDIA GEOMÉTRICA versus MEDIA ARITMÉTICA

Partimos de unas ventas reales en el 2020 de 1.000 euros, y estimamos unas variaciones para los siguientes años (respecto al anterior) de: 2%, -15%, 7%, 4%, -1%
Esto significaría, haciendo el cálculo manual, que en el 2025 tendríamos unas ventas de:
=1.000 x (1+2%) x (1+15%) x (1+7%) x (1+4%) x (1-1%) = 955,15 euros.

Esto es fácil de conseguir en nuestra hoja de cálculo, bien aplicando la fórmula anterior, o bien empleando alguna forma matricial, por ejemplo en la celda G7:
=B4*PRODUCTO(1+C4:G4) (recuerda ejecutarla matricialmente para mayor seguridad, i.e., presionando Ctrl+Mayus+Enter).
MEDIA GEOMÉTRICA versus MEDIA ARITMÉTICA

Obteniendo idéntico resultado: 955,15.

Otra cuestión relevante sería conocer cuál es la media para cada uno de esos cinco años de variación interanual... y qué tipo de media deberíamos aplicar (geométrica o aritmética).
Calculemos ambas...
Comenzemos buscando la media geométrica.
En nuestro ejemplo, como se observa, tenemos variaciones negativas, por lo que directamente NO sería posible aplicar la función MEDIA.GEOM (no admite valores negativos o ceros)... pero si reajustamos las variaciones sobre base 100, esto es, sobre los valores acumulados (sumando 1 a cada variación), los valores de cálculo serían:
102%, 85%, 107%, 104% y 99%
donde si podemos aplicar la función... Vemos en F10 la fórmula matricial:
=MEDIA.GEOM(1+C4:G4)-1
MEDIA GEOMÉTRICA versus MEDIA ARITMÉTICA

Dando como resultado, para nuestro ejemplo, -0,914% de media para cada uno de los cinco años.

'Curiosamente' si aplicamos esta media acumulada para cada uno de los cinco años, en G10 la fórmula matricial:
=B4*(MEDIA.GEOM(1+C4:G4))^5
o también sobre F10
=B4*(1+F10)^5
conseguimos el mismo resultado de 955,15 que ya habíamos logrado con el cálculo manual, año por año... Interesante ;-)
Es decir, con el cálculo de la media geométrica llegamos al mismo resultado equivalente, lo que indica que es una media correcta!.

Repetimos los pasos para la media aritmética (PROMEDIO).
Introducimos en F13 la matricial:
=PROMEDIO(1+C4:G4)-1
MEDIA GEOMÉTRICA versus MEDIA ARITMÉTICA

Ya podemos comparar con el cálculo de la media geométrica (-0,914%) frente a la media aritmética (-0,600%) ...
Si aplicamos el acumulado para los cinco años en G13 con la matricial:
=B4*(1+PROMEDIO(C4:G4))^5
o también
=B4*(1+F13)^5
resulta un valor de 970,36, valor superior al previo calculado, y algo lejos del resultado exacto.

Esto podría demostrar que la media geométrica resulta más representativa, trabajando con variaciones porcentuales, que la media aritmética.

martes, 8 de septiembre de 2020

Power Query: Text.Combine para unir datos de distintas filas

Avanzando con el potencial de Power Query, hoy veremos una de sus funciones M más curiosas: Text.Combine con su sintáxis:
Text.Combine(texts as list, optional separator as nullable text) as text
La cual nos devuelve el resultado de combinar una lista de valores de texto, como una única cadena de texto; además podemos especificar un separador opcional entre los elementos de esa lista.

Nuestro objetivo hoy, para emplear esta función, es pasar de una tabla de información con registros repartidos entre distintas filas, a un nuevo listado final con la información agregada y combinada de esas filas diferentes.
Podemos verlo en la imagen siguiente:
Power Query: Text.Combine para unir datos de distintas filas


Veamos los pasos a seguir...
En primer lugar, como siempre, desde la ficha Datos > grupo Obtener y transformar > botón Desde Tabla o rango cargaremos los datos en una consulta del Editor de Power Query.
Power Query: Text.Combine para unir datos de distintas filas

En el segundo paso, por estética y gusto personal, transformaré el campo de 'Unidades' a texto con un formato de separador de miles sin decimales...
Para lo cual emplearé otra función M interesante:
Number.ToText(number as nullable number, optional format as nullable text, optional culture as nullable text)
siendo de especial interés los formatos permitidos:
- "D" o "d": (Decimal) Da formato al resultado como dígitos enteros. El especificador de precisión controla el número de dígitos en la salida.
-"E" o "e": (Exponencial/científico) Notación exponencial. El especificador de precisión controla el número máximo de dígitos decimales (el valor predeterminado es 6).
-"F" o "f": (Fijo, de punto fijo) Dígitos integrales y decimales.
-"G" o "g": (General) Forma más compacta de punto fijo o científico.
-"N" o "n": (Número) Dígitos integrales y decimales con separadores de grupos y un separador decimal.
-"P" o "p": (Porcentaje) Número multiplicado por 100 y mostrado con un símbolo de porcentaje.
-"R" o "r": (Recorrido de ida y vuelta) Valor de texto que puede realizar un recorrido de ida y vuelta por un número idéntico. El especificador de precisión se omite.
-"X" o "x": (Hexadecimal) Un valor de texto hexadecimal.
Estos códigos estarán seguidos de un valor numérico que opcionalmente irá seguido de un especificador de precisión numérica. en mi ejemplo emplearé el formato 'N0'; lo que mostrará un número con separador de miles (la N) y con 0 decimales (el 0).
El tercer argumento de la función, opcional, puede marcar el aspecto de la configuración del país, por ejemplo, es-ES, o us-US.

De acuerdo a lo anterior, volviendo a nuestra consulta, añadiremos una Columna personalizada con la siguiente expresión:
=Number.ToText([Unds],"N0")
Power Query: Text.Combine para unir datos de distintas filas

Y vemos el resultado de las unidades reflejado como texto...
Power Query: Text.Combine para unir datos de distintas filas

Podemos Quitar la columna previa de 'Unds', para lo que basta presionar sobre ella botón derecho y Quitar columna.

En el siguiente paso uniremos/concatenaremos las dos columnas informativas: Paises y Unidades.
Solo tendremos que seleccionar ambas y presionar botón derecho sobre ellas y seleccionar la opción Combinar columnas
Power Query: Text.Combine para unir datos de distintas filas

Y configuramos en la ventana correspondiente qué separador queremos emplear en la concatenación de ambas columnas... en mi ejemplo el guión medio -
Power Query: Text.Combine para unir datos de distintas filas

En un penúltimo paso, preparando el siguiente, Rellenaremos las filas vacías de los primeros campos: Vendedor y Año.
Así pues seleccionaremos estos dos campos y desde el menú Transformar > grupo Cualquier columna > botón Rellenar abajo, obteniendo el siguiente listado en nuestra consulta...
Power Query: Text.Combine para unir datos de distintas filas

Y, por fin, llegamos al paso final... debemos pasar de múltiples filas, además variables, ya que nunca es el mismo número de países los implicados en estos repartos, a una única fila por Vendedor y País, con el detalle agrupado.

Tiempo atrás ya publiqué un post en el blog sobre otra técnica para lograr este mismo tipo de agrupaciones (ver aquí).
Hoy veremos una alternativa con el uso de Text.Combine algo más simple... diría yo ;-)

Seleccionamos las columnas Vendedor y País y presionamos la opción de Agrupar por (la encontrarás en el menú Inicio > grupo Transformar > botón Agrupar por). Lo que abrirá la ventana de configuración y estableceremos la Agregación de Suma por el campo 'País+Unds'
Power Query: Text.Combine para unir datos de distintas filas

Comprobaremos como tras Aceptar nuestra consulta devuelve un error!!
Power Query: Text.Combine para unir datos de distintas filas

Lo curioso e interesante es que al arreglar este error daremos al tiempo con la solución buscada!!.
En la barra de fórmula de este paso vemos:
Table.Group(#"Rellenar hacia abajo", {"Vendedor", "Año"}, {{"País+Unidades", each List.Sum([#"Pais-Unds"]), type text}})
El problema es que trata de sumar una columna de texto... El truco es cambiar:
List.Sum([#"Pais-Unds"])
por
Text.Combine([#"Pais-Unds"],"|")
quedando el último paso de nuestra consulta:
Table.Group(#"Rellenar hacia abajo", {"Vendedor", "Año"}, {{"País+Unidades", each Text.Combine([#"Pais-Unds"],"|"), type text}})
Power Query: Text.Combine para unir datos de distintas filas
Listos... Cargamos y cerramos y vemos el resultado en la hoja de cálculo tal como queríamos!

jueves, 3 de septiembre de 2020

Power Query: Importar datos desde un pdf

En una reciente actualización de nuestro Excel (al menos para suscriptores de Microsoft 365) nos ofrece una nueva, e importante, posibilidad: Importar datos desde pdf empleando para ello Power Query (como no).
La herramienta es muy potente y sobre todo fiable... ya que no se trata de un simple conversor OCR.

Pongamos a prueba la funcionalidad.
Para ello disponemos de uu archivo pdf generado a partir de un .docx donde se han insertado dos tablas de datos:
1-una de ellas como tabla
2-la segunda como imagen!!

Power Query: Importar datos desde un pdf

Para comenzar el proceso de importación iremos a la ficha Datos > grupo Obtener y transformar > desplegable Obtener datos externos > Desde un archivo > Desde pdf
Power Query: Importar datos desde un pdf

Esto nos abrirá el asistente de importación donde indicaremos la ruta del documento pdf.
En el siguiente paso veremos la siguiente ventana:
Power Query: Importar datos desde un pdf

Al interesarnos ambas tablas tendremos cuidado de marcar la opción Seleccionar varios elementos; e igualmente buscaremos entre los distintos items desplegados las tablas deseadas.
Como punto curioso observamos que el asistente identifica en qué páginas del documento pdf se encuentran nuestras tablas.
Verificamos en la vista previa que los datos se visualizan correctamente y accederemos, presionando el botón de Transformar datos al editor de Power Query.
Power Query: Importar datos desde un pdf
Comprobamos que tenemos dos consultas creadas, una por cada tabla importada desde nuestro pdf.
Un pequeño inconveniente, no podemos tener todo en un solo paso :'( , es que no ha identificado correctamente los encabezados de las tablas. Cosa que lograremos rápidamente usando la primera fila como encabezado
Power Query: Importar datos desde un pdf
Esto inserta un paso en nuestra consulta... como era de esperar.

Repetimos el paso para la segunda consulta y estaremos listos para Cargar y cerrar, disponiendo de las tablas vinculadas a dicho documento pdf original!!.
Power Query: Importar datos desde un pdf


Alternativamente, en casos extremos, si el reconocimiento de las tablas de datos fallara, podremos trabajar directamente sobre las hojas del documento, dentro del Editor de Power Query, seleccionando directamente las hojas como elementos a importar.
Power Query: Importar datos desde un pdf
Obviamente el trabajo de Transformar datos en este supuesto se hace más laborioso...

martes, 1 de septiembre de 2020

Power Query: Actualización en segundo plano

Con la expansión de las consultas de Power Query en nuestras hojas de cálculo aparecen nuevas cuestiones que debemos solventar, una muy interesante es el efecto que pueden tener sobe nuestros informes de tablas dinámicas.
Hablo en particular del efecto de Refescar/Actualizar todo (Ctrl+Alt+F5).

Muy probablemente habrás notado en muchas ocasiones que tras refrescar nuestras consultas de Power Query, nuestras tablas dinámicas no se ven actualizadas con la nueva información... obligando a un segundo refresco de las conexiones, con el tiempo que esto conlleva.
La solución a esta situación pasa por Deshabilitar la actualización en segundo plano en las propiedades de las consultas...
Power Query: Actualización en segundo plano
Esta característica tan importante se configura para cada consulta en su opción de 'Propiedades', a la que se puede acceder de diferentes formas. Una podría ser desde la cinta de opciones, en las herramientas de la Consulta > grupo Editar > botón Propiedades
Power Query: Actualización en segundo plano

Lo que abrirá la ventana de propiedades (ver primera imagen del post), donde podremos Habilitar / Deshabilitar la actualización en segundo plano.
Igualemente recomendado es mantener marcada la opción de 'Actualizar esta conexión en Actualizar Todo'.

Veamos el efecto sobre nuestras tablas dinámicas de esta actualización en segundo plano.
Tenemos una sencilla tabla origen, de la cual hemos creado dos consultas en Power Query (sin ninguna transformación para visualizar mejor el efecto de estudio).
La primera consulta (llamada 'pqTabla1') tiene Habilitada la actualización en segundo plano.
Mientras la segunda (llamada 'pqTabla2') tiene Deshabilitada la actualización en segundo plano.

Sobre cada una de estas consultas hemos creado un informe de tabla dinámica... como vemos en la imagen siguiente:
Power Query: Actualización en segundo plano
Inicialmente no se ve diferencia alguna... pero lo curioso saldría a la luz cuando existan modificaciones en el Origen.
Supongamos añadimos un par de registros en el Origen y presionemos Actualizar Todo
Power Query: Actualización en segundo plano

Comprobemos qué ha ocurrido...
En la primera de las consultas (con la Actualización en segundo plano habilitada) verificamos que los nuevos registros se muestran en la consulta... pero no se ven reflejadas en la tabla dinámica.
El motivo es que mientras se actualizaba la consulta 'por detrás' (en segundo plano), simultáneamente se refrescaba la Tabla dinámica... CON los datos existentes en ese instante, esto es, aún sin actualizar!!.
El resultado es una tabla dinámica inconsistente con los datos.

Por otra parte, en la segunda de las consultas (con la Actualización en segundo plano deshabilitada) comprobamos igualmente que ha actualizado los nuevos datos, y que SÍ ha mostrado el informe de tabla dinámica igualmente actualizado con los nuevos datos.
El motivo de este comportamiento es que al deshabilitar la actualización en segundo plano ha forzado en primer lugar el refresco de la query, y hasta que no se ha completado dicha actualización no ha continuado con el refresco de su tabla dinámica asociada!!. Tal como deseabamos ;-)

Otro dato interesante de las actualizaciones de las consultas es el orden en el que se ejecutan... especialmente cuando tenemos combinaciones, anexados o consultas 'auxiliares' en nuestro Editor.
Siempre nos queda la duda si habrá actualizado en el orden correcto y estará aplicando los datos refrescados...
Lo que debemos saber es que es el propio Power Query quien, automáticamente aplica el orden correcto, teniendo presente las dependencias, si las hubiera, entre consultas. Lo cual evita nuestro cuidado.
Un punto positivo para Power Query :D