¡Jueves de post! Hoy vengo con un pequeño overview a modo sintesís de las funciones ventana. Las funciones de ventana (window functions) son un conjunto de funciones DAX introducidas en versiones recientes de Power BI y del motor tabular. Permiten acceder a filas específicas dentro de un conjunto de datos definido por una partición y un orden, sin necesidad de manipular explícitamente el contexto de filtro con CALCULATE o de recurrir a patrones iterativos complejos.
¿Por qué surgen?
Antes de la llegada de estas funciones, escenarios como obtener el valor de la fila anterior, calcular un acumulado dentro de un grupo, construir rankings por partición o navegar entre filas requerían combinaciones de CALCULATE, FILTER, EARLIER, ALL, ALLEXCEPT, variables e iteradores. Estos patrones funcionaban, pero eran verbose, propensos a errores y difíciles de mantener. Las funciones de ventana resuelven estos escenarios de forma más directa, legible y, en muchos casos, con mejor rendimiento.
Analogía con SQL
Si vienes del mundo SQL, el concepto te resultará familiar. Las funciones de ventana en DAX son conceptualmente equivalentes a las window functions de SQL (ROW_NUMBER, LAG, LEAD, SUM OVER, etc.). Sin embargo, hay diferencias importantes: DAX no opera sobre tablas relacionales planas, sino sobre un modelo tabular con contextos de evaluación. La partición y el orden no se definen en una cláusula OVER, sino mediante los argumentos PARTITIONBY y ORDERBY dentro de la propia función.
Escenarios donde aportan más valor:
• Rankings dentro de grupos (ranking por categoría, por cliente, por región).
• Comparaciones con filas vecinas: valor anterior, valor siguiente, diferencias entre periodos.
• Acumulados parciales dentro de una partición (acumulado por cliente, por producto).
• Navegación posicional: obtener el primer, último o enésimo elemento de un grupo.
• Medias móviles y ventanas deslizantes sobre conjuntos ordenados.
Qué es una «ventana» en DAX
Una ventana en DAX no es una tabla física almacenada en el modelo. Es un subconjunto lógico de filas que se define en tiempo de ejecución y que depende de tres elementos: el contexto actual de evaluación, la partición (PARTITIONBY) y el orden (ORDERBY).
Podemos pensar en la ventana como una «vista» temporal que el motor DAX construye para resolver un cálculo específico. El motor toma la tabla subyacente, la segmenta según la partición, la ordena según el criterio definido y, dentro de esa estructura, identifica la posición de cada fila. Esa posición es lo que permite a INDEX, OFFSET y WINDOW hacer su trabajo.
Ejemplo: Imagina una tabla de ventas con 1.000 filas. Si definimos PARTITIONBY(Cliente) y ORDERBY(Fecha), el motor crea una ventana separada para cada cliente, dentro de la cual las filas están ordenadas por fecha. El resultado es como tener muchas sub-tablas independientes, una por cliente, cada una con sus filas ordenadas cronológicamente.
Componentes clave de una función de ventana
PARTITIONBY — Segmentación
Define cómo se segmentan los datos en grupos independientes. Cada grupo (partición) se trata como una unidad aislada: los cálculos se reinician en cada partición. Es equivalente al PARTITION BY de SQL.
Ejemplo: PARTITIONBY( Producto[Categoría] ) divide los datos en tantos grupos como categorías existan. Un ranking calculado con esta partición produce un ranking independiente dentro de cada categoría.
ORDERBY — Orden dentro de la partición
Define el orden en que se procesan las filas dentro de cada partición. Es fundamental porque determina qué significa «anterior», «siguiente», «primero» o «posición N». Sin un ORDERBY explícito, el orden es indeterminado y los resultados pueden ser impredecibles.
Ejemplo: ORDERBY( Calendario[Fecha], ASC ) ordena cronológicamente. ORDERBY( Ventas[Importe], DESC ) ordena de mayor a menor importe.
Posición relativa
Una vez definida la partición y el orden, cada fila tiene una posición dentro de su ventana: primera, segunda, tercera... Esa posición es la que utilizan INDEX (posición absoluta), OFFSET (desplazamiento relativo) y WINDOW (rango de posiciones) para identificar las filas objetivo del cálculo.
Antes de las funciones de ventana, los escenarios de navegación entre filas, rankings por grupo y acumulados parciales se resolvían con patrones clásicos. Vamos a compararlos.
RANKX
RANKX es una función iteradora que calcula rankings evaluando una expresión para cada fila de una tabla y comparando el resultado. Funciona, pero para rankings dentro de particiones requiere combinarlo con CALCULATE y ALL/ALLEXCEPT, lo que complica la fórmula.
CALCULATE + FILTER
Para acceder al valor de otra fila (por ejemplo, el mes anterior), tradicionalmente se usaba CALCULATE con FILTER para modificar el contexto de filtro y apuntar a la fila deseada. Esto requiere conocer bien la mecánica de contexto y produce fórmulas largas y frágiles.
Variables + iteradores
Otro patrón común es capturar valores en variables y usarlos dentro de iteradores (SUMX, FILTER, ADDCOLUMNS) para navegar entre filas. Funcional, pero poco legible y difícil de depurar.
INDEX
Qué hace: Devuelve una tabla de una sola fila correspondiente a una posición específica dentro de la ventana definida por PARTITIONBY y ORDERBY. Esa posición puede ser absoluta (1, 2, 3...) o negativa (contando desde el final: -1 es la última fila).
Idea mental: «Dame la fila que está en la posición N de mi grupo ordenado».
Sintaxis:
Parámetros:
• Posición: número entero. Valores positivos cuentan desde el inicio (1 = primera fila). Valores negativos cuentan desde el final (-1 = última fila). Si la posición solicitada excede el número de filas de la partición, INDEX devuelve BLANK.
• Relación: la tabla base sobre la que se construye la ventana.
• ORDERBY y PARTITIONBY: definen orden y segmentación como se explicó en la sección 2.
Cuándo usarla: Cuando necesitas acceder a una fila específica por su posición dentro de un grupo: primera venta de cada cliente, tercera venta más grande de cada categoría, último registro de cada periodo.
Cuándo NO usarla: Cuando lo que necesitas es un desplazamiento relativo (para eso está OFFSET) o un rango de filas (para eso está WINDOW).
Ejemplo 1: Primera venta por cliente
Queremos obtener, para cada almacen, el importe de su primera venta ordenada por fecha.
En este caso: INDEX(1, ...) selecciona la primera fila
de cada partición (cliente), ordenada por fecha ascendente. CALCULATE evalúa
SUM(Importe) en el contexto de esa única fila. El resultado es el importe de la
primera transacción de cada Almacen.
OFFSET
Qué hace: Devuelve una tabla de una sola fila desplazada un número determinado de posiciones respecto a la fila actual, dentro de la ventana definida por PARTITIONBY y ORDERBY. Es decir, permite navegar hacia adelante (+) o hacia atrás (-) desde la posición actual.
Idea mental: «Desde donde estoy ahora, muévete N filas y dame lo que hay ahí».
Parámetros:
• Delta: número entero. -1 = fila anterior, +1 = fila siguiente, -2 = dos filas atrás, etc. Un delta de 0 devuelve la fila actual.
• Si el desplazamiento lleva fuera de los límites de la partición (por ejemplo, pedir la fila anterior cuando estamos en la primera), OFFSET devuelve BLANK.
Cuándo usarla: Cuando necesitas comparar con la fila anterior o siguiente, calcular variaciones periodo a periodo, detectar cambios entre registros consecutivos.
Cuándo NO usarla: Cuando necesitas una posición absoluta (usa INDEX) o un rango de filas (usa WINDOW).
Diferencia con PREVIOUS/NEXT de cálculos visuales:
PREVIOUS y NEXT de los cálculos visuales navegan por la matriz visual (lo que se ve en pantalla). OFFSET navega por la tabla del modelo de datos, dentro de la ventana definida por PARTITIONBY y ORDERBY. OFFSET es una función de medida que vive en el modelo; PREVIOUS/NEXT son cálculos visuales que viven en el visual. OFFSET se puede usar en cualquier visual sin cambios; PREVIOUS/NEXT dependen de la estructura de cada visual concreto.
En este caso: Desde el mes actual, retrocede una posición en la secuencia de meses y evalúa [Ventas] en ese contexto. En enero (primer mes), devuelve BLANK.
WINDOW
Qué hace: Devuelve una tabla con múltiples filas correspondientes a un rango dentro de la ventana. A diferencia de INDEX (una posición) y OFFSET (un desplazamiento), WINDOW define un intervalo: desde la fila X hasta la fila Y, ya sea en posiciones absolutas o relativas.
Idea mental: «Dame todas las filas que están entre la posición A y la posición B de mi ventana».
Sintaxis:
Parámetros:
• Desde / Hasta: números que definen el inicio y fin del rango.
• Tipo ABS (absoluto): la posición se cuenta desde el inicio de la partición. 1 = primera fila. Valores negativos cuentan desde el final.
• Tipo REL (relativo): la posición se cuenta desde la fila actual. -1 = una fila antes, 0 = fila actual, +1 = una fila después.
Cuándo usarla: Para acumulados parciales, medias móviles, sumas sobre ventanas deslizantes, análisis de vecinos, cualquier cálculo que requiera operar sobre un rango de filas dentro de la partición.
Cuándo NO usarla: Cuando solo necesitas una fila (usa INDEX o OFFSET). WINDOW es para rangos.
Diferencia entre WINDOW, OFFSET e INDEX:
En este caso: Desde 2 posiciones antes (-2 REL) hasta la posición actual (0 REL), selecciona hasta 3 meses y calcula el promedio de ventas. En los primeros meses, la ventana se ajusta a las filas disponibles.
¡Nos vemos en los datos!