📊

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.