jueves, 3 de diciembre de 2020

Contar en rangos NO adyacentes

Hoy toca algo de formulación que nos ayudará a realizar conteos sobre rangos No adyacentes
Contar en rangos NO adyacentes

El objetivo será lograr contar coincidencias sobre rango no continuos tal y como se ve en la imagen anterior... El asunto no es tan simple como emplear funciones ya conocidas como CONTAR.SI o CONTAR.SI.CONJUNTO, y para dar solución mostraré alternativas con funciones como SUMAPRODUCTO, INDIRECTO o ELEGIR.

En primer lugar haremos uso de una fórmula ya empleada en este post, donde incluimos o asignamos la fórmula siguiente en un nombre definido 'ndProblemas':
ndProblemas = =LET(
rangos;(Tabla1[problema1];Tabla1[problema2];Tabla1[problema3]);
Areas;AREAS(rangos);
filas;FILAS(Tabla1[problema1]);
TotalFilas;SECUENCIA(Areas*filas);
NumArea;MULTIPLO.SUPERIOR.MAT(SECUENCIA(Areas*filas);filas)/filas;
Fila;TotalFilas-(NumArea-1)*filas;
INDICE(rangos;Fila;1;NumArea))

Sobre ese vector construido matricialmente podemos realizar la siguiente operación en C14:
=SUMAPRODUCTO((ndProblemas=$B14)*1)
donde evaluamos los VERDADEROS o las coincidencias del vector (ndProblemas) con el valor de B14, al que multiplicamos por 1 para convertirlo en valor y poder sumarlo...
Igualmente válido (y más 'profesional') sería la alternativa:
=SUMAPRODUCTO(--(ndProblemas=$B14))
esto es, empleando el doble menos.

Luego podremos arrastrar hacia abajo para conocer las coincidencias para B15 y B16.

Una segunda manera de lograr nuestro conteo será empleando la combinación de funciones clásicas como: SUMA, CONTAR.SI o INDIRECTO.
Veamos como... en C20 insertamos:
=SUMA(CONTAR.SI(INDIRECTO({"Tabla1[problema1]";"Tabla1[problema2]";"Tabla1[problema3]"});B20))
/ donde INDIRECTO transforma la matriz de constantes {"Tabla1[problema1]";"Tabla1[problema2]";"Tabla1[problema3]"} en los rangos sobre los que contar.. cosa que hará CONTAR.SI, obteniendo por tanto tres conteos!: {3;2;0} (para mi primer caso).
Finalmente sumaremos esos tres valores devueltos matricialmente con SUMA.
Contar en rangos NO adyacentes

Vemos aquí una solución con un razonamiento de aplicación bastante clásico y fácil de comprender...

Vamos con una tercera alternativa.
En C26 insertamos una anidación de ELEGIR en SUMAPRODUCTO:
=SUMAPRODUCTO(((ELEGIR({1\2\3};Tabla1[problema1];Tabla1[problema2];Tabla1[problema3]))=B26)*1)
Donde la peculiaridad la aporta el uso matricial de ELEGIR que nos permite construir un rango o matriz continuo de tres columnas (en nuestro ejemplo) al llamar a la matriz de constantes {1\2\3}... para una vez montada dicha matriz proceder a compararlo con el valor de la ceda B26 y siguientes...
Si ejecutamos ELEGIR({1\2\3};Tabla1[problema1];Tabla1[problema2];Tabla1[problema3]) de manera separada tendríamos la matriz de 3 columnas por 10 filas:
{"ccc"\0\0;"aaa"\"aaa"\0;0\0\0;"ccc"\"aaa"\"bbb";"aaa"\"ccc"\0;0\0\0;"aaa"\0\"aaa";0\0\0;"bbb"\"ccc"\0;0\0\0}
Tras la comparativa aplicaremos SUMAPRODUCTO para obtener el conteo de coincidencias o VERDADEROS.
Contar en rangos NO adyacentes


Adicionalmente a los cálculos previos, podemos obtener el conteo de celdas vacías en esos rango no adyacentes con alguna de las siguientes fórmulas:
=SUMAPRODUCTO((ndProblemas="")*1) (o también =SUMAPRODUCTO(--(ndProblemas="")))
otra manera
=SUMA(CONTAR.SI(INDIRECTO({"Tabla1[problema1]";"Tabla1[problema2]";"Tabla1[problema3]"});""))
o bien
=SUMAPRODUCTO((ELEGIR({1\2\3};Tabla1[problema1];Tabla1[problema2];Tabla1[problema3])=0)*1)
Algunas de estas funciones se deberán ejecutar, según la versión, matricialmente... esto es, presionando Ctrl+Mayusc+Enter OJO!!.

Como comentario final, vistas algunas de las alternativas, decir que la potencia de las fórmulas descritas está en que los rangos podrían estar incluso en hojas diferentes o ser de tamaños distintos... es decir, potencia en estado puro ;-)

martes, 1 de diciembre de 2020

Calendarios en Excel. Fórmulas desbordadas

Cada vez es más frecuente encontrar por estas fechas publicaciones de todo tipo ofertando o simplemente mostrando como elaborar un calendario laboral para el próximo año...
En este mismo blog puedes encontrar ejemplos de calendarios perpetuos:
- con macros
- con fórmulas
- con Power Query

El día de hoy daremos una vuelta al asunto empleando las fórmulas desbordadas (de momento solo válidas para Microsoft 365).
Con nuestras fórmulas y un poco de formato condicional y formato personalizado llegaremos a:
Calendarios en Excel. Fórmulas desbordadas


Comenzaremos montado la formulación para enero, y posteriormente poder copiar y pegar para el resto de meses...
En primer lugar en B1 añadimos el año, en el ejemplo 2020, pero servirá cualquier año que quieras...
En B2 añadimos un 1 que representará el mes del año (ya sabes, 1 - enero, 2 - febrero, ..., 12 - diciembre).
En B4 la fórmula desbordada que mostrará la fechas del mes...
=SI(MES(SECUENCIA(6;7;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1))<>B2;
"";
SECUENCIA(6;7;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1))

La clave de la fórmula reside en la matriz secuencial devuelta por:
SECUENCIA(6;7;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1)
Calendarios en Excel. Fórmulas desbordadas

con el que obtenemos una secuencia de fechas empezando con la fecha del lunes anterior al primer día del mes:
FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1
Matriz de seis filas (una por cada posible semana del mes) por siete columnas (una por cada día de la semana).
Acabamos nuestra función SECUENCIA indicando que el incremento o paso será de +1.

Con el condicional conseguimos 'dejar fuera de la ecuación', esto es, no mostrar las fechas que no correspondan al mes desplegado:
SI(MES(SECUENCIA(6;7;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1))<>B2;"";...)
Calendarios en Excel. Fórmulas desbordadas

Incorporamos un encabezado en B3:H3 con los días de la semana (lunes, martes, miércoles, ...)

En C2 añadimos la función
=TEXTO(C6;"mmmm-aa")
para visualizar el mes de trabajo...

Y estariamos listos para perfilar el aspecto.
Primero aplicaremos al rango desbordado B4:H9 un formato personalizado de número tipo 'd'
Calendarios en Excel. Fórmulas desbordadas

Copiamos el rango trabajado hasta completar la distribución 'típica' de los doce meses
Calendarios en Excel. Fórmulas desbordadas

Ahora aplicaremos algo de formato condicional básico para resaltar los fines de semana, así como el día corriente y los días festivos (si disponemos de un listado de ellos!!)
Calendarios en Excel. Fórmulas desbordadas
Son tres reglas de formato condicional con fórmulas:
- B4=HOY() para marcar el día corriente
- DIASEM(B4;2)>=6 para resaltar los sábados y domingos
- COINCIDIR(B4;ndFestivos;0) para subrayar los días festivos

Para mi ejemplo previamente he creado una tabla con un nombre definido asignado 'ndFestivos'
Calendarios en Excel. Fórmulas desbordadas

Le quitamos las líneas de cuadrícula de las celdas y listo!!

Una variante de este calendario perpetuo sería mostrarlo en vertical
Calendarios en Excel. Fórmulas desbordadas

El montaje y formulación es casi idéntico (no podría ser de otra manera).
En B1 añadimos el año.
En B2 la función =SECUENCIA(1;12)
En A2:A45 la fórmula =TEXTO(SI(MAX(B4:M4)=0;"";MAX(B4:M4));"dddd")
que genera una secuencia de los días de la semana acorde a las fechas mostradas...
Y finalmente en B4
=SI(MES(SECUENCIA(42;1;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1))<>B2;
"";
SECUENCIA(42;1;FECHA($B$1;B2;1)-DIASEM(FECHA($B$1;B2;1);2)+1;1))

fórmula que copiaremos para el resto de meses en C4, D4, ..., M4
La diferencia con el primer calendario es el tamaño del rango desbordado que devuelve SECUENCIA:
SECUENCIA(6;7) para el primer ejemplo
SECUENCIA(42;1) para este último

Aplicaremos los mismos formatos personalizados y formatos condicionales que en el caso anterior, y ya tenemos un calendario perpetuo 'vertical'.

jueves, 26 de noviembre de 2020

Power Query: List.First - Recuperando primera o última coincidencia

Al trabajar sobre tablas es muy frecuente operar sobre los campos numéricos o incluso de tipo fecha para obtener acumulados, promedios o su valor máximo o mínimo... pero en ocasiones nos interesa recuperar información de campos de tipo texto.
Por ejemplo, ¿cuál es la primera o última descripción que corresponde a tal o cual código?
Power Query: List.First - Recuperando primera o última coincidencia

Hoy mostraré cómo recuperar la primera descripción correspondiente a cada país desde nuestra tabla de información (List.First).
Igualmente recuperaremos la última descripción correspondiente a cada país (List.Last).
Y por último un concatenado de las cuatro primeras descripciones asociadas por país (List.FirstN, además de la ya conocida (Text.Combine)).

Comenzaremos cargando nuestra tabla 'Tabla1' al editor de consultas desde la ficha Datos > grupo Obtener y transformar > Desde Tabla o Rango.
Ya en el editor de Power Query y desde el menú Inicio > grupo Transformar > Agrupar por con la columna 'Código' seleccionada.
En la ventana de configuración aplicaremos las opciones avanzadas, ya que vamos a incluir dos operaciones:
1- una para obtener el valor máximo de 'Ventas' por cada páis
2- una operación cualquiera, que a continuación modificaremos, para recuperar la primera aparición de la descripción
Power Query: List.First - Recuperando primera o última coincidencia

Del código M generado para ese paso:
= Table.Group(#"Tipo cambiado", {"Código"}, {{"1st_Descrip", each Table.RowCount(_), Int64.Type}, {"Max_Ventas", each List.Max([Ventas]), type nullable number}})

cambiaremos el que corresponde a la '1st_Descrip' para que quede como sigue:
= Table.Group(#"Tipo cambiado", {"Código"}, {{"1st_Descrip", each List.First([Descripción]), type nullable text},{"Max_Ventas", each List.Max([Ventas]), type nullable number}})


Se observa que hemos llamado a la función M de
List.First(list as list, optional defaultValue as any) as any
que nos devolverá el primer elemento de la lista indicada. En nuestro ejemplo de la lista correspondiente por cada país que genera nuestra agrupación (Table.Group)
Power Query: List.First - Recuperando primera o última coincidencia

De igual forma podríamos obtener la última descripción que hubiera para cada país.
Mismos pasos que los ya comentados, pero esta vez haciendo uso de la función M:
List.Last(list as list, optional defaultValue as any) as any
pero esta vez devuelve el último elemento de la lista, que para nosotros es la lista de descripciones.
La línea de la agrupación debe quedar como sigue:
= Table.Group(#"Tipo cambiado", {"Código"}, {{"Última_Descrip", each List.Last([Descripción]), type nullable text},{"Max_Ventas", each List.Max([Ventas]), type nullable number}})

Power Query: List.First - Recuperando primera o última coincidencia


Finalmente, veremos un último ejemplo que nos permitirá recuperar en una 'celda', concatenadas, las cuatro primeras 'Descripciones' de cada país.
Emplearemos una función ya conocida que nos permite esa concatenación de elementos de una lista:
Text.Combine(texts as list, optional separator as nullable text) as text
Devuelve el resultado de combinar la lista de valores de texto como un solo valor de texto.
Y por otro lado, para obtener la lista de los cuatro primeros elementos emplearemos:
List.FirstN(list as list, countOrCondition as any) as any
sabiendo que:
-si se especifica un número, se devuelve esa cantidad máxima de elementos.
-y que si se especifica una condición, se devuelven todos los elementos que cumplen inicialmente la condición. (OJO!!: Si un elemento no cumple la condición, no se tienen en cuenta más elementos).
Así pues procederemos de igual forma que en los dos casos previos, esto es, agrupando por la columna 'Código' y modificando la línea del código de ese paso hasta que nos quede como sigue:
= Table.Group(#"Tipo cambiado", {"Código"}, {{"Nrd_Descrip", each Text.Combine(List.FirstN([Descripción],4),"-"), type nullable text},{"Max_Ventas", each List.Max([Ventas]), type nullable number}})

Power Query: List.First - Recuperando primera o última coincidencia


List.FirstN devuelve una lista de hasta cuatro elementos, que luego combinamos en una sola cadena de texto con Text.Combine

Sí, existe una función List.LastN...

martes, 24 de noviembre de 2020

Power Query: Alternativas a BUSCARV aproximado

Probablemente un clásico de las relaciones entre tablas sea la búsqueda aproximada, entendiendo por tal la busqueda de un valor por cercania o pertenencia a un intervalo.
Veamos la imagen siguiente:
Power Query: Alternativas a BUSCARV aproximado
Digamos que vamos a replicar el comportamiento de la función BUSCARV en su forma aproximada (cuarto arumento como VERDADERO).
Se trata de identificar el descuento a aplicar de acuerdo al número de unidades vendidas...
De tal forma que si he vendido, por ejemplo, 74 unidades... según mi tabla de intervalo de unidades (Desde 60 hasta 89), le correspondería un descuento del 8%.

Por desgracia en Power Query no existe un tipo de combinación entre tablas que replique tal cual este tipo de relación... por lo que, como en tantas ocasiones, tendremos que ser imaginativos ;-)

Veremos tres maneras altenativas, cada una con sus ventajas e inconvenientes, pero siempre válidas.

Como siempre comenzaremos cargando las dos tablas al editor de Power Query... ya sabes, desde la ficha Datos > grupo Obtener y transformar > Desde Tabla o Rango, solo con conexión.
Importante, sobre la primera Tabla 'TblVENTAS' hemos añadido una Columna Índice desde 1
Power Query: Alternativas a BUSCARV aproximado

Y sobre la segunda tabla 'TblDCTOS' agregamos una Columna personalizada con un texto cualquiera... en mi caso 'salto'
Power Query: Alternativas a BUSCARV aproximado

Igual de importante es cambiar el nombre de la columna 'Desde' por el nombre 'Unidades' (el mismo nombre de la TblVENTAS para ese campo)!!.
Son columnas y cambios que en alguno de los métodos posteriores necesitaremos...

Primer método: Agregar una columna condicional
En el editor de consultas iremos al menú Agregar columna > grupo General > Columna condicional Desde la configuración identificaremos manualmente los diferentes intervalos
Power Query: Alternativas a BUSCARV aproximado

Se observa como hemos ido insertando condiciones simples con el operador 'menor que' para cada intervalo:
Desde Hasta % Descuento
0 14 1%
15 34 3%
35 59 5%
60 89 8%
90 10%
La fórmula añadida a la consulta, desde el asistente, ha sido:
if [Unidades] < 15 then 0.01 else if [Unidades] < 35 then 0.03 else if [Unidades] < 60 then 0.05 else if [Unidades] < 90 then 0.08 else 0.10
que verifica y se ajusta a los descuentos correspondientes...
Un método muy simple de aplicar, sin duda, pero con la limitació obvia que, ante futuros cambios, esto es, nuevos intervalos o variación en los porcentajes... deberemos entrar en la consulta y cambiar manualmente esta configuración.

Segundo método: Trabajar con ambas tablas sin combinar.
Este caso requiere de un uso intermedio del lenguaje M, ya que deberemos picar algo de código... dejando el uso de los asistentes para aspectos residuales... Nuestro código a insertar en una consulta en blanco será:
let
    AddTblVentas=TblVENTAS,
    AddTblDCTO=Table.AddColumn(AddTblVentas, "descuentos", each TblDCTO),
    
    ExpandeDescuentos = Table.ExpandTableColumn(AddTblDCTO, "descuentos", {"Unidades", "Hasta", "% Descuento", "Salto"}, {"Desde", "Hasta", "% Descuento", "Salto"}),

    Reemplazo = Table.ReplaceValue(ExpandeDescuentos,null,Record.Field(Table.Max(Table.SelectColumns(ExpandeDescuentos,"Unidades"),"Unidades"),"Unidades"),Replacer.ReplaceValue,{"Hasta"}),

    AddCondicional = Table.AddColumn(Reemplazo, "Descuentos", each if [Unidades]>=[Desde] and [Unidades]<=[Hasta] then [#"% Descuento"] else 0),
    #"Filtro<>0" = Table.SelectRows(AddCondicional, each [Descuentos] <> 0),
    Columnas_Quitadas = Table.RemoveColumns(#"Filtro<>0",{"Índice", "Desde", "Hasta", "Salto", "Descuentos"})
in
    Columnas_Quitadas

Power Query: Alternativas a BUSCARV aproximado

La esencia de este método es que al juntar en la misma consulta dos tablas (TblVENTAS y TblDCTO) se produce un listado de todas las posibles combinaciones entre los registros de ambas tablas:
Power Query: Alternativas a BUSCARV aproximado

Esto nos permite comparar, con una columna personalizada, con una condición comparativa entre columnas, determinar a qué intervalo pertenecen las unidades buscadas:
if [Unidades]>=[Unidades.1] and [Unidades]<=[Hasta] then [#"% Descuento"] else 0
obteniendo dato solo para el porcentaje buscado, y cero para los demás casos...
El siguiente paso de la consulta es claro, con un filtro sencillo, dejo fuera de la consulta esas filas con valor cero, esto es, aquellas filas que no han cruzado con ningún intervalo!.
Como apunte interesante, y para evitar errores al aplicar en anterior condicional, se reemplazó el valor 'null' del último intervalo de descuento (Desde 90 Hasta 'vacío') con el valor máximo de entre las unidades vendidas:
Reemplazo = Table.ReplaceValue(ExpandeDescuentos,null,Record.Field(Table.Max(Table.SelectColumns(ExpandeDescuentos,"Unidades"),"Unidades"),"Unidades"),Replacer.ReplaceValue,{"Hasta"})
Nos quedamos con el valor máximo (Tabla.Max) de la columna necesaria 'Unidades' (Table.SelecColumns), recuperando el dato con 'Record.Value'.
Lo interesante de este método es que está abierto a cualquier tipo de cambio en la tabla de descuentos... y como inconveniente, el tipo de unión empleada, que podrá generar fallos en la consulta cuando el número de filas en las tablas sea elevado.

Y vamos a por el tercer método: Anexando ambas tablas.
Muy interesante el empleo de esta herramienta 'Anexar consultas' en vez de lo que nos dice el sentido común 'Combinar consultas'.
Comenzaremos desde la consulta de la 'TblVENTAS' para Anexar consultas para crear una nueva, donde seleccionaremos para anexar la TblVENTAS y la TblDCTO con los cambios ya efectuadas al inicio del proceso!!.
Power Query: Alternativas a BUSCARV aproximado

Vemos que el resultado ha anexado, en el campo 'Unidades' en primer lugar los datos de TblVENTAS y a continuación los datos de TblDCTO; los correspondientes al campo 'Desde' que cambiamos de nombre por 'Unidades'.
El sentido del cambio era este... al llamarse ambos campos/columnas iguales, el anexado los une sobre la misma columna.
El resto de campos al no coincidir entre sí los anexa en columnas nuevas...
Power Query: Alternativas a BUSCARV aproximado

En el siguiente paso, muy sencillo, ordenaremos en Ascendente precisamente por la columna 'Unidades'; para seguidamente forzar sobre la columna del '% descuento' la acción de Rellenar Hacía abajo
Power Query: Alternativas a BUSCARV aproximado

Observa como al ordenar por 'Unidades' se han intercalado nuestros intervalos, partiendo de alguna manera nuestras filas entre esos valores... para finalmente rellenar con los porcentajes de descuentos a aplicar para todas esas unidades entre los intervalos necesarios.
Terminamos aplicando un Filtro sobre la columna 'Salto' para eliminar esas filas donde aparezca el texto 'salto' (recuerda que venían de la TblDCTO tratada)... esto es, quitamos las filas de las tablas no necesarias.
Ordenamos por la columna Índice para recuperar el orden original de los registros de ventas.
Y quitamos columnas no necesarias... para llegar a nuestra consulta final
Power Query: Alternativas a BUSCARV aproximado

El código generado:
let
    Origen = Table.Combine({TblVENTAS, TblDCTO}),
    #"Filas ordenadas" = Table.Sort(Origen,{{"Unidades", Order.Ascending}}),
    RellenarHaciaAbajo = Table.FillDown(#"Filas ordenadas",{"% Descuento"}),
    FiltroQuita_salto = Table.SelectRows(RellenarHaciaAbajo, each ([Salto] = null)),
    Ordena_Inicio = Table.Sort(FiltroQuita_salto,{{"Índice", Order.Ascending}}),
    ColumnasQuitadas = Table.RemoveColumns(Ordena_Inicio,{"Hasta", "Salto","Índice"})
in
    ColumnasQuitadas
Un último método sencillo, que no requiere de conocimientos específicos del lenguaje M, y que nos permite la flexibilidad que necesitamos ante modificaciones en nuestras tablas originales...

jueves, 19 de noviembre de 2020

Power Query: Separar por filas múltiples columnas

Recientemente un cliente me solicitaba la generación de una macro (en VBA para Excel) que transformara un listado de registros con ciertos campos concatenados en un listado de elementos únicos...
Tal y como se ve en la imagen siguiente:
Power Query: Separar por filas múltiples columnas

La idea, como se observa en la imagen anterior, es separar en varias filas cada registro, de acuerdo al número de elementos concatenados en diferentes columnas... por ejemplo:
24/05/2020 ES;DE;FR 267 1544;1020;1915
está compuesto de tres registros, con valores correspondientes entre los tres paises y los tres importes de ventas:
ES - 1544
DE - 1020
FR - 1912
El resto de campos debería repetirse en cada columna final mostrada...
Adicionalmente pretendemos hacer un reparto equitativo de las unidades para cada fila generada!!.

Obviamente desarrollé la programación.. pero me quedé pensando si era posible evitar la programación y generar una consulta en Power Query que realizara el mismo proceso... y aquí está ;-)
Veamos como separar múltiples columnas por filas.

Partimos de nuestra fuente de datos (una tabla llamada 'TblDatos'), la cual cargaremos como siempre desde la ficha Datos > grupo Obtener y transformar > botón Desde Tabla o rango.
Una vez en el editor de consultas Agregaremos una columna personalizada nueva con la siguiente fórmula:
= [Unidades vendidas]/List.Count(Text.Split([Paises],";"))
Power Query: Separar por filas múltiples columnas

Con esta fórmula conseguimos el reparto por unidades entre cada fila futura a generar...
Clave para este cálculo la función M:
Text.Split(text as text, separator as text) as list
que generar una lista a partir de una cadena de texto...
Por ejemplo, de la cadena de texto: ES;DE;FR generará una lista {ES DE FR}.

Sobre esta lista (que volveremos a utilizar posteriormente) aplicamos otra función M:
List.Count(list as list) as number
que nos devolverá el número de elementos en dicha lista... 3 en mi ejemplo anterior.
Finalemente forzamos el cociente, esto es, el reparto de 'Unidades vendidas' entre ese conteo de elementos...

Power Query: Separar por filas múltiples columnas
Con el cálculo de unidades repartidas pasamos al siguiente paso...

Con la función M:
Table.FromColumns(lists as list, optional columns as any) as table
crearemos una tabla para cada registro compuesta por el desglose de cada columna.
ES - 1544
DE - 1020
FR - 1912
Power Query: Separar por filas múltiples columnas

Tabla que posteriormente expandiremos...
Para incorporar nuestra fórmula Agregaremos una columna personalizada nueva con la fórmula:
= Table.FromColumns({Text.Split([Paises],";"),Text.Split([Ventas],";")},{"Pais","Ventas"})
Power Query: Separar por filas múltiples columnas

Table.FromColumns genera una tabla de dos columnas (llamadas 'Pais' y 'Ventas') a partir de las listas obtenidas con Text.Split de los campos originales [Paises] y [Ventas], indicado en el primer argumento:
{Text.Split([Paises],";"),Text.Split([Ventas],";")}

Con la 'tabla' creada para cada fila, eliminamos las columnas originales [Paises] y [Ventas] que ya no necesitamos.

Finalmente expandimos la última columna personalizada y extraemos las dos nuevas columnas..
Power Query: Separar por filas múltiples columnas


Nuestros datos ya aparecen como pretendíamos, separados por filas..


Solo quedan un par de pasos simples para acabar con nuestra consulta...
Reordenar la posición de las columnas, y
Cambiar el tipo de dato de la nueva columna de Ventas a type.number
Listos para cerrar y cargar en nuestra hoja de cálculo!!

El código completo, que podemos ver en el editor avanzado sería...
let
    Origen = Excel.CurrentWorkbook(){[Name="TblDATOS"]}[Content],
    Tipo_Cambiado = Table.TransformColumnTypes(Origen,{{"Fecha", type date}, {"Paises", type text}, {"Ventas", type text}, {"Unidades vendidas", Int64.Type}}),
    AddCol_RepartoUDS = Table.AddColumn(Tipo_Cambiado, "RepartoUds", each [Unidades vendidas]/List.Count(Text.Split([Paises],";"))),
    AddCol_CreaTabla = Table.AddColumn(AddCol_RepartoUDS, "NuevasCols", each Table.FromColumns({Text.Split([Paises],";"),Text.Split([Ventas],";")},{"Pais","Ventas"})),
    ColumnasQuitadas1 = Table.RemoveColumns(AddCol_CreaTabla,{"Unidades vendidas", "Ventas", "Paises"}),
    Expande_NuevasCols = Table.ExpandTableColumn(ColumnasQuitadas1, "NuevasCols", {"Pais", "Ventas"}, {"Pais", "Ventas"}),
    Columnas_Reordenadas = Table.ReorderColumns(Expande_NuevasCols,{"Fecha", "Pais",  "RepartoUds", "Ventas"}),
    Tipo_Cambiado1 = Table.TransformColumnTypes(Columnas_Reordenadas,{{"Ventas", type number}})
in
    Tipo_Cambiado1

Power Query: Separar por filas múltiples columnas