¡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.

Las funciones de ventana no vienen a sustituir lo que ya sabíamos de DAX, vienen a ordenarlo.

Durante años hemos resuelto estos problemas con CALCULATE, FILTER, iteradores y mucho conocimiento del contexto. Y eso sigue siendo válido. Pero ahora tenemos una forma más natural de expresar algo que, en realidad, siempre ha estado ahí: trabajar con conjuntos ordenados dentro de grupos.

La clave no está en aprender INDEX, OFFSET o WINDOW como funciones aisladas. Está en entender el concepto de ventana: particionar, ordenar y posicionar. Cuando interiorizas eso, las fórmulas dejan de ser un puzzle y pasan a ser una descripción directa de lo que quieres hacer.

¿La consecuencia? Código más legible, más mantenible y, en muchos casos, más eficiente. Pero, sobre todo, un cambio de mentalidad: dejamos de “forzar” el contexto con CALCULATE para empezar a “navegar” por los datos de forma explícita.

Ahora bien, igual que con cualquier avance en DAX, esto no elimina la necesidad de conocer los fundamentos. Si no entiendes el contexto de filtro, la granularidad o cómo se evalúa una medida, las funciones de ventana no te van a salvar. Solo harán que los errores sean más elegantes.

Porque al final, no va de usar la función más moderna. Va de entender qué estás calculando y por qué.

Y ahí es donde realmente está la diferencia entre alguien que usa DAX… y alguien que lo domina.

 ¡Nos vemos en los datos!