📊

Funciones de Agregación

SUM, COUNT, AVERAGE, MIN, MAX y todas sus variantes, los cimientos de cualquier modelo DAX

Principiante Intermedio
🔍

¿Qué son las funciones de agregación? 🤔

Las funciones de agregación toman una columna (o una expresión evaluada fila a fila) y devuelven un único valor escalar. Son el primer tipo de función que aprende cualquier persona en DAX porque son imprescindibles para construir medidas.

La distinción más importante desde el principio: funciones simples vs iteradoras X

  • SUM([Ventas]), agrega directamente una columna numérica
  • SUMX(Tabla, Expresión), itera fila a fila, calcula la expresión, y luego agrega
Analogía kawaii 🌸
SUM es como pedirle a alguien que cuente el dinero en una caja. SUMX es como pedirle que por cada factura calcule (precio × cantidad × descuento) y luego lo sume todo. Mucho más flexible pero también más trabajo.
La regla de oro 🏆
Si necesitas hacer un cálculo por fila antes de agregar → usa la versión X (SUMX, AVERAGEX, MAXX...). Si solo necesitas agregar una columna que ya existe → usa la versión simple (SUM, AVERAGE, MAX...).

SUM, La función más usada en DAX

SUM(<columna>)
Principiante
Suma todos los valores de una columna numérica en el contexto de filtro actual. La medida más habitual en cualquier modelo.

Ejemplo básico:

// Medida básica de ventas totales
Total Ventas = SUM('Ventas'[Importe])

// En un visual filtrado por "España" → suma solo las ventas de España
// En un visual sin filtro → suma TODAS las ventas

Ejemplo con contexto:

// SUM siempre respeta el contexto de filtro automáticamente
// Si tienes un slicer de País seleccionado en "Francia":
Total Ventas = SUM('Ventas'[Importe])  -- devuelve solo ventas de Francia
// No hace falta escribir nada especial, CALCULATE hace eso automáticamente
⚠️ Importante
SUM solo funciona con columnas numéricas. Para sumar en base a una expresión por fila (ej: precio × cantidad), necesitas SUMX. Y nunca uses SUM dentro de CALCULATE como primer argumento, ahí va una medida o expresión.
SUMX(<tabla>, <expresión>)
Intermedio
Itera cada fila de la tabla, evalúa la expresión en el contexto de esa fila, y suma todos los resultados. Imprescindible cuando no tienes una columna con el importe ya calculado.

Ejemplo, cuando no existe columna de importe:

// Tabla Ventas tiene: Cantidad y PrecioUnitario, pero NO Importe
// ❌ Esto NO funciona:
Total Ventas WRONG = SUM('Ventas'[Cantidad] * 'Ventas'[PrecioUnitario])

// ✅ Esto SÍ funciona:
Total Ventas = SUMX('Ventas', 'Ventas'[Cantidad] * 'Ventas'[PrecioUnitario])
// Por cada fila: calcula Cantidad × PrecioUnitario → suma todo

Ejemplo con descuento:

// Precio con descuento aplicado fila a fila
Total Ventas Neto =
SUMX(
    'Ventas',
    'Ventas'[Cantidad] * 'Ventas'[PrecioUnitario] * (1 - 'Ventas'[Descuento])
)

Ejemplo con tabla filtrada:

// Suma solo las ventas de productos con margen > 20%
Ventas Alto Margen =
SUMX(
    FILTER('Ventas', 'Ventas'[Margen] > 0.20),
    'Ventas'[Importe]
)
🏆 Best Practice
SUMX con una tabla grande puede ser lento. Si el cálculo fila a fila es simple (precio × cantidad), suele ser eficiente. Pero si la expresión es compleja, considera crear una columna calculada en el modelo y usar SUM sobre esa columna.
🔢

COUNT y familia, contar con criterio

Tabla rápida de referencia:

FUNCIÓN QUÉ CUENTA IGNORA BLANCOS TIPO DE COLUMNA
COUNT Valores numéricos ✅ Sí Solo números
COUNTA Cualquier valor no vacío ✅ Sí Cualquier tipo
COUNTBLANK Celdas vacías/BLANK N/A Cualquier tipo
COUNTROWS Filas de una tabla N/A Tabla completa
COUNTX Expresión no vacía por fila ✅ Sí Expresión iterada
COUNTAX Expresión no vacía (cualquier tipo) ✅ Sí Expresión iterada
COUNT(<columna>) | COUNTA(<columna>)
Principiante

Ejemplo COUNT:

// Cuenta cuántas ventas tienen importe registrado (número)
Nº Ventas = COUNT('Ventas'[Importe])
// ¡OJO! Si alguna fila tiene BLANK en Importe, NO la cuenta

// Para contar filas con cualquier dato (texto, fecha, número):
Nº Registros = COUNTA('Ventas'[ID_Venta])
COUNTBLANK(<columna>)
Principiante

Ejemplo:

// Cuántos productos no tienen precio asignado
Productos Sin Precio = COUNTBLANK('Productos'[Precio])
// Útil para auditorías de calidad de datos
COUNTROWS(<tabla>)
Principiante
La más usada para contar filas, no depende del tipo de dato.

Ejemplos:

// Contar todas las filas de la tabla
Nº Pedidos = COUNTROWS('Pedidos')

// Con filtro:
Pedidos Pendientes = COUNTROWS(FILTER('Pedidos', 'Pedidos'[Estado] = "Pendiente"))

// Contar clientes únicos que han comprado (patrón muy común)
Clientes Activos = COUNTROWS(VALUES('Ventas'[ID_Cliente]))
COUNTX(<tabla>, <expresión>)
Intermedio

Ejemplo:

// Contar filas donde el margen calculado es positivo
Ventas Rentables =
COUNTX(
    'Ventas',
    IF('Ventas'[PrecioVenta] > 'Ventas'[Coste], 1, BLANK())
)
Regla práctica para elegir la función de conteo 💡
¿Quieres contar FILAS de una tabla? → COUNTROWS. ¿Quieres contar VALORES no vacíos de una columna numérica? → COUNT. ¿Valores de cualquier tipo? → COUNTA. ¿Celdas vacías? → COUNTBLANK. ¿Con lógica fila a fila? → COUNTX.
🎯

DISTINCTCOUNT, Contar únicos

DISTINCTCOUNT(<columna>)
Principiante, Intermedio
Cuenta los valores únicos (distintos) de una columna, ignorando duplicados. Equivale a COUNT(DISTINCT columna) en SQL. Imprescindible para métricas como "número de clientes únicos" o "SKUs vendidos".

Ejemplo básico:

// Cuántos clientes diferentes han comprado
Clientes Únicos = DISTINCTCOUNT('Ventas'[ID_Cliente])
// Si el cliente 001 compró 5 veces → cuenta como 1

// Cuántos productos diferentes se han vendido
SKUs Vendidos = DISTINCTCOUNT('Ventas'[ID_Producto])

// Diferencia importante:
Nº Ventas = COUNTROWS('Ventas')           -- número de transacciones (filas)
Clientes Únicos = DISTINCTCOUNT('Ventas'[ID_Cliente])  -- número de compradores únicos
Ticket Medio Clientes = DIVIDE([Total Ventas], [Clientes Únicos])
DISTINCTCOUNTNOBLANK(<columna>)
Intermedio
Igual que DISTINCTCOUNT pero excluye explícitamente los valores BLANK. DISTINCTCOUNT sí incluye el BLANK como un valor distinto en algunos contextos.

Ejemplo:

// Si la columna Región tiene: "Norte", "Sur", BLANK, "Norte"
DISTINCTCOUNT('Ventas'[Región])        -- resultado: 3 (Norte, Sur, BLANK)
DISTINCTCOUNTNOBLANK('Ventas'[Región]) -- resultado: 2 (Norte, Sur)
📏

AVERAGE, Medias y promedios

AVERAGE(<columna>)
Principiante
Calcula la media aritmética de los valores numéricos de una columna. Ignora automáticamente los valores BLANK (no los trata como 0, los excluye del cálculo).

Ejemplo:

// Ticket medio por transacción
Ticket Medio = AVERAGE('Ventas'[Importe])
// Si hay 10 ventas con importes: 100, 200, BLANK, 300...
// AVERAGE ignora el BLANK → media de los 9 valores no vacíos

// ¡OJO! Diferencia importante:
Ticket Medio Real = AVERAGE('Ventas'[Importe])
// vs
Ticket Medio Con Ceros = DIVIDE(SUM('Ventas'[Importe]), COUNTROWS('Ventas'))
// El segundo divide entre TODAS las filas (incluyendo las que tienen BLANK)
// Elige según qué signifique un BLANK en tu contexto
AVERAGEA(<columna>)
Principiante
Como AVERAGE pero también procesa columnas no numéricas. Convierte TRUE=1, FALSE=0, texto=0. Raramente necesaria en Power BI, AVERAGE es suficiente para casi todo.
AVERAGEX(<tabla>, <expresión>)
Intermedio
Versión iteradora de AVERAGE. Calcula la expresión por fila y luego promedia los resultados.

Ejemplo, Margen medio por pedido:

// Media del margen de cada pedido (no el margen total / nº pedidos)
Margen Medio por Pedido =
AVERAGEX(
    'Pedidos',
    DIVIDE(
        SUMX(RELATEDTABLE('LineasPedido'), 'LineasPedido'[Margen]),
        SUMX(RELATEDTABLE('LineasPedido'), 'LineasPedido'[Importe])
    )
)
🎢

MIN y MAX, Valores extremos

Tabla de referencia:

FUNCIÓN QUÉ HACE TIPO
MIN Mínimo de columna numérica/fecha Simple
MINA Mínimo incluyendo lógicos Simple (raro)
MINX Mínimo de expresión por fila Iteradora
MAX Máximo de columna numérica/fecha Simple
MAXA Máximo incluyendo lógicos Simple (raro)
MAXX Máximo de expresión por fila Iteradora
MIN(<columna>) | MAX(<columna>)
Principiante

Ejemplo MIN/MAX básico:

// Fechas del primer y último pedido
Primera Venta = MIN('Ventas'[Fecha])
Última Venta = MAX('Ventas'[Fecha])

// Precio más bajo y más alto
Precio Mínimo = MIN('Productos'[Precio])
Precio Máximo = MAX('Productos'[Precio])

// Número de días activos (entre primera y última venta)
Días Activos = DATEDIFF(MIN('Ventas'[Fecha]), MAX('Ventas'[Fecha]), DAY)
MINX(<tabla>, <expresión>) | MAXX(<tabla>, <expresión>)
Intermedio

Ejemplo MINX/MAXX:

// El pedido de mayor importe (calculado como precio × cantidad)
Pedido Mayor Importe =
MAXX(
    'Pedidos',
    SUMX(RELATEDTABLE('LineasPedido'), 'LineasPedido'[Precio] * 'LineasPedido'[Cantidad])
)

// MIN con dos columnas (MIN con dos argumentos devuelve el menor de los dos valores):
Precio Aplicado = MIN('Productos'[Precio], 'Productos'[PrecioOferta])
✖️

PRODUCT y PRODUCTX, Multiplicación acumulada

PRODUCT(<columna>) | PRODUCTX(<tabla>, <expresión>)
Intermedio, Avanzado
Multiplica todos los valores de una columna (o expresión). Poco usada en business intelligence general, pero muy útil en finanzas para cálculos de rendimientos compuestos.

Ejemplo, Rendimiento compuesto:

// Si tienes una tabla con la variación mensual en %:
// Enero +5%, Febrero +3%, Marzo -2%...
// El rendimiento compuesto acumulado es el PRODUCTO de (1 + variación)
Rendimiento Compuesto =
PRODUCTX(
    'Rendimientos',
    1 + 'Rendimientos'[Variacion_Mensual]
) - 1

// Resultado: el rendimiento total del período considerando el interés compuesto

APPROXIMATEDISTINCTCOUNT, Velocidad vs Precisión

APPROXIMATEDISTINCTCOUNT(<columna>)
Avanzado
Versión aproximada (pero muy rápida) de DISTINCTCOUNT. Usa el algoritmo HyperLogLog para estimar el conteo distinto con un margen de error de ~2%. Ideal para tablas con millones de filas donde la velocidad importa más que la precisión exacta.

Ejemplo:

// En una tabla con 50 millones de filas:
// ❌ Lento en DirectQuery o tablas enormes:
Visitantes Únicos EXACT = DISTINCTCOUNT('WebLogs'[UserID])

// ✅ Mucho más rápido, ~2% de error (aceptable para dashboards):
Visitantes Únicos APPROX = APPROXIMATEDISTINCTCOUNT('WebLogs'[UserID])
💡 Cuándo usarla
Para la mayoría de casos de negocio donde un error de ±2% es aceptable (conteo de visitantes, usuarios activos, IDs únicos en big data), APPROXIMATEDISTINCTCOUNT puede ser 10-100x más rápido que DISTINCTCOUNT. ¡Úsala sin miedo en métricas de alto volumen!
🧐

Errores típicos y cómo evitarlos

Error 1, Usar SUM donde debería ir SUMX:

// ❌ Error: no puedes multiplicar columnas dentro de SUM
Total = SUM('Ventas'[Precio] * 'Ventas'[Cantidad])  -- ERROR de sintaxis

// ✅ Correcto:
Total = SUMX('Ventas', 'Ventas'[Precio] * 'Ventas'[Cantidad])

Error 2, AVERAGE no es lo mismo que Total/Conteo cuando hay blancos:

// Si el 30% de tus filas tienen BLANK en Importe:
AVERAGE('Ventas'[Importe])  -- divide entre el 70% de filas (las no vacías)
DIVIDE(SUM('Ventas'[Importe]), COUNTROWS('Ventas'))  -- divide entre el 100%

// ¿Cuál es correcta? Depende de tu lógica de negocio. ¡Decídelo conscientemente!

Error 3, DISTINCTCOUNT incluye BLANK:

// Si quieres solo IDs reales sin contar filas sin ID:
DISTINCTCOUNTNOBLANK('Ventas'[ID_Cliente])  -- más seguro
🚀 Siguiente paso
¡Ya dominas las agregaciones! Son la base de todo. El siguiente gran salto es entender cómo CALCULATE modifica el contexto en el que se evalúan estas funciones, eso es lo que hace DAX único y potente.