Funciones Estadísticas
MEDIAN, PERCENTILE, STDEV, RANKX, distribuciones estadísticas y regresión lineal — el análisis estadístico completo en DAX
Estadística descriptiva vs inferencial en DAX
¿Crees que DAX es solo SUM y COUNT? ¡Para nada! Tiene un conjunto de funciones estadísticas digno de R o Python en modo básico. Desde la mediana hasta distribuciones chi-cuadrado, pasando por regresión lineal. Te cuento cuándo y por qué usarlas en un contexto de BI. 📊
Clasificación rápida de funciones estadísticas
| Tipo | Qué hace | Funciones DAX |
|---|---|---|
| Estadística DESCRIPTIVA | Describe los datos que tienes | MEDIAN, STDEV, VAR, PERCENTILE, RANKX |
| Estadística INFERENCIAL | Hace inferencias sobre una población | NORM.DIST, T.DIST, CHISQ.DIST, BETA.DIST |
| Machine Learning básico | Predice valores basándose en patrones | LINEST, LINESTX (regresión lineal) |
En la práctica, el 95% del uso estadístico en Power BI es descriptivo — mediana, percentiles, desviación estándar. Las distribuciones estadísticas y la regresión son para usuarios avanzados que integran análisis estadístico directamente en sus informes.
Medidas de posición — dónde están los datos
¿Cuándo usar mediana vs media?
// Supongamos salarios: 20.000, 22.000, 24.000, 25.000, 200.000
// (el CEO infla la media)
Salario Medio = AVERAGE('Empleados'[Salario])
// Resultado: 58.200€ (el CEO infla la media)
Salario Mediano = MEDIAN('Empleados'[Salario])
// Resultado: 24.000€ (el valor central real — mucho más representativo)
Ejemplos prácticos
// ✅ Ticket mediano (más robusto que el ticket medio)
Ticket Mediano = MEDIAN('Ventas'[Importe])
Ticket Medio = AVERAGE('Ventas'[Importe])
// Si hay ventas atípicas muy grandes, Ticket Mediano es más representativo
// ✅ MEDIANX — mediana de una expresión calculada
Margen Mediano =
MEDIANX(
'Pedidos',
DIVIDE([Margen Pedido], [Importe Pedido])
)
Cuartiles y percentiles
// ✅ Cuartiles de ventas
Q1 Ventas = PERCENTILE.INC('Ventas'[Importe], 0.25) -- 25% inferior
Q2 Ventas = PERCENTILE.INC('Ventas'[Importe], 0.50) -- mediana
Q3 Ventas = PERCENTILE.INC('Ventas'[Importe], 0.75) -- 75% inferior
P90 Ventas = PERCENTILE.INC('Ventas'[Importe], 0.90) -- top 10%
// ✅ Rango intercuartílico (IQR — medida de dispersión robusta)
IQR =
PERCENTILE.INC('Ventas'[Importe], 0.75) -
PERCENTILE.INC('Ventas'[Importe], 0.25)
// ✅ Detectar outliers (método IQR)
Limite Superior Outlier =
PERCENTILE.INC('Ventas'[Importe], 0.75) + 1.5 * [IQR]
Limite Inferior Outlier =
PERCENTILE.INC('Ventas'[Importe], 0.25) - 1.5 * [IQR]
Es Outlier =
OR(
'Ventas'[Importe] > [Limite Superior Outlier],
'Ventas'[Importe] < [Limite Inferior Outlier]
)
// ✅ Percentil 90 del margen calculado por pedido
P90 Margen =
PERCENTILEX.INC(
'Pedidos',
DIVIDE(
SUMX(RELATEDTABLE('Lineas'), 'Lineas'[Margen]),
SUMX(RELATEDTABLE('Lineas'), 'Lineas'[Importe])
),
0.90
)
Dispersión — cómo de dispersos están los datos
Tabla comparativa de funciones de dispersión
| Función | Qué mide | Variante | Cuándo usar |
|---|---|---|---|
STDEV.P |
Desv. estándar poblacional | Toda la población | Tienes TODOS los datos |
STDEV.S |
Desv. estándar muestral | Muestra | Tienes una MUESTRA de los datos |
STDEVX.P |
Como STDEV.P pero iteradora | Expresión por fila | Con expresiones calculadas |
STDEVX.S |
Como STDEV.S pero iteradora | Expresión por fila | Con expresiones calculadas |
VAR.P |
Varianza poblacional | Toda la población | (STDEV.P al cuadrado) |
VAR.S |
Varianza muestral | Muestra | (STDEV.S al cuadrado) |
VARX.P |
Varianza poblacional iteradora | Expresión por fila | Con expresiones calculadas |
VARX.S |
Varianza muestral iteradora | Expresión por fila | Con expresiones calculadas |
Análisis de consistencia
// ✅ ¿Cómo de consistentes son las ventas diarias?
Consistencia Ventas = STDEV.S('Ventas'[Importe])
// Alta → ventas muy variables día a día (riesgo)
// Baja → ventas estables y predecibles
// ✅ Coeficiente de variación (CV) — dispersión relativa
// Comparable entre medidas con diferentes escalas
CV Ventas = DIVIDE(STDEV.S('Ventas'[Importe]), AVERAGE('Ventas'[Importe]))
// 0.1 = 10% de variación (muy estable)
// 0.5 = 50% de variación (muy variable)
// Permite comparar variabilidad entre métricas con distintas escalas
// ✅ Z-Score — a cuántas desviaciones estándar está cada venta de la media
'Ventas'[Z-Score] =
DIVIDE(
'Ventas'[Importe] - AVERAGE('Ventas'[Importe]),
STDEV.S('Ventas'[Importe])
)
// Z > 2 o Z < -2 → posible outlier
// Z = 0 → valor igual a la media
// Z = 1 → una desviación estándar por encima de la media
Usa .P (poblacional): Si tus datos son el universo completo (todas las ventas del año pasado).
Usa .S (muestral): Si tus datos son una muestra de una población más grande.
En la práctica de BI, casi siempre usarás
.S porque trabajas con los datos disponibles como muestra del comportamiento futuro.
Rankings — RANKX y RANK.EQ
Parámetros de RANKX
- tabla: el conjunto sobre el que ranquear (ALL, VALUES, tabla completa...)
- expresión: qué se rankea (generalmente una medida)
- valor: qué valor ranquear (por defecto, la expresión evaluada en el contexto actual)
- orden: DESC (mayor=1, por defecto) o ASC (menor=1)
- empates: SKIP (1,2,2,4) o DENSE (1,2,2,3)
Ranking básico de productos
// ✅ Ranking de productos por ventas (el más vendido = posición 1)
Ranking Producto =
RANKX(
ALL('Productos'), -- sobre todos los productos
[Total Ventas], -- ranquear por ventas totales
, -- valor actual (se omite = usa la expresión)
DESC, -- mayor ventas = posición 1
DENSE -- sin saltos en empates
)
// ✅ Ranking dentro de categoría
Ranking en Categoría =
RANKX(
ALLEXCEPT('Productos', 'Productos'[Categoría]),
-- solo productos de la misma categoría
[Total Ventas],
,
DESC,
DENSE
)
// ✅ Usar RANKX para mostrar solo el Top N
Es Top 10 = IF([Ranking Producto] <= 10, TRUE, FALSE)
// ✅ Top N dinámico (con slicer de N)
Top N Ventas =
VAR N = SELECTEDVALUE('TopN Selector'[Valor], 10)
RETURN
IF([Ranking Producto] <= N, [Total Ventas], BLANK())
RANKX puede ser lento en tablas grandes porque es un iterador que evalúa la expresión para CADA FILA de la tabla especificada. Si la tabla tiene millones de filas, considera usar la función RANK (Window Function) en tablas calculadas, o limitar la tabla con TOPN primero.
// ✅ Posición de una categoría específica en la lista de ventas
Posición Electrónica =
RANK.EQ(
[Ventas Electrónica],
'ResumenCategorías'[TotalVentas],
DESC
)
Muestras y medias geométricas
// ✅ Mostrar 10 transacciones representativas (distribuidas uniformemente)
Muestra Transacciones =
SAMPLE(10, 'Ventas', 'Ventas'[Importe], ASC)
// Devuelve 10 filas de Ventas distribuidas a lo largo del rango de importes
¿Cuándo usar media geométrica vs aritmética?
// Si tienes rendimientos: +50%, -50%
// Media aritmética: (50 + (-50)) / 2 = 0% → parece neutro
// Pero si inviertes 100€:
// +50% → 150€, luego -50% → 75€ (¡has perdido dinero!)
// Media geométrica: SQRT(1.5 × 0.5) - 1 = -13.4% → refleja la realidad
CAGR (tasa de crecimiento compuesto anual)
// ✅ CAGR = (Valor Final / Valor Inicial)^(1/años) - 1
CAGR =
VAR ValorInicial = FIRSTNONBLANK('Ventas'[TotalAño], 1)
VAR ValorFinal = LASTNONBLANK('Ventas'[TotalAño], 1)
VAR Años = DATEDIFF(MIN('Calendario'[Fecha]), MAX('Calendario'[Fecha]), YEAR)
RETURN
POWER(DIVIDE(ValorFinal, ValorInicial), DIVIDE(1, Años)) - 1
// ✅ Alternativa con GEOMEANX (para factores de crecimiento año a año)
CAGR con Geomean =
GEOMEANX('Crecimientos', 1 + 'Crecimientos'[Tasa]) - 1
LINEST y LINESTX — regresión lineal en DAX
Sí, DAX puede hacer regresión lineal. Quizás no al nivel de Python con scikit-learn, pero sí lo suficiente para calcular tendencias, coeficientes de correlación y líneas de ajuste directamente en Power BI.
Línea de tendencia de ventas
// ✅ Calcular la pendiente de la tendencia de ventas en el tiempo
Ventas Predichas =
VAR RegresionTabla =
LINEST(
SUMMARIZE('Ventas', 'Calendario'[MesNum], "Y", SUM('Ventas'[Importe])),
[Y], -- variable dependiente: ventas
[MesNum] -- variable independiente: número de mes
)
VAR Slope = MAXX(FILTER(RegresionTabla, [Nombre] = "Slope"), [Valor])
VAR Intercept = MAXX(FILTER(RegresionTabla, [Nombre] = "Intercept"), [Valor])
RETURN
-- Ventas predichas para el mes N:
Slope * [MesNum] + Intercept
Correlación entre descuento y margen
// ✅ ¿A mayor descuento, menor margen? (esperamos que sí...)
Pendiente Descuento-Margen =
VAR Regresion =
LINESTX(
'Ventas',
'Ventas'[Margen], -- Y: lo que queremos predecir
'Ventas'[Descuento] -- X: el predictor
)
VAR Pendiente = MAXX(FILTER(Regresion, [Nombre] = "Slope"), [Valor])
RETURN Pendiente
// Pendiente negativa → a mayor descuento, menor margen (confirma la hipótesis)
// Pendiente positiva → algo raro pasa (revisar datos)
Para análisis estadístico más profundo (regresión múltiple, Machine Learning, clustering) considera usar Python o R integrado en Power BI, o exportar los datos a Fabric para procesarlos con notebooks. LINEST es útil para tendencias simples directamente en el modelo.
Distribuciones estadísticas — para análisis avanzado
Estas funciones son el territorio de las personas con formación en estadística. Si eres analista de datos, ingeniera o científica de datos, las reconocerás inmediatamente. Si no tienes ese background, no te preocupes — la mayoría de proyectos de BI no las necesitan. Pero están aquí para cuando las necesites.
Tabla de todas las distribuciones disponibles
| Distribución | Funciones DAX | Uso principal |
|---|---|---|
| Normal | NORM.DIST, NORM.INV, NORM.S.DIST, NORM.S.INV | Intervalos de confianza, z-scores |
| T de Student | T.DIST, T.DIST.2T, T.DIST.RT, T.INV, T.INV.2T | Tests de hipótesis con muestras pequeñas |
| Chi-cuadrado | CHISQ.DIST, CHISQ.DIST.RT, CHISQ.INV, CHISQ.INV.RT | Tests de independencia, goodness of fit |
| Beta | BETA.DIST, BETA.INV | Probabilidades de proporciones (ej: tasa de conversión) |
| Exponencial | EXPON.DIST | Tiempos entre eventos (procesos de Poisson) |
| Poisson | POISSON.DIST | Conteo de eventos en un intervalo |
Probabilidad de superar un umbral
// ✅ ¿Cuál es la probabilidad de que las ventas de mañana sean > 5000€?
// Asumiendo que las ventas siguen una distribución normal con media=4000 y σ=800
P(Ventas > 5000) = 1 - NORM.DIST(5000, 4000, 800, TRUE)
// Resultado: ~10.6%
// ✅ Intervalo de confianza del 95% para la media de ventas
IC 95% Ventas =
VAR Media = AVERAGE('Ventas'[Importe])
VAR Sigma = STDEV.S('Ventas'[Importe])
VAR N = COUNTROWS('Ventas')
VAR MargenError = 1.96 * DIVIDE(Sigma, SQRT(N)) -- 1.96 = z para 95%
VAR LimiteSup = Media + MargenError
VAR LimiteInf = Media - MargenError
RETURN
"IC 95%: [" & FORMAT(LimiteInf, "#,##0") & ", " & FORMAT(LimiteSup, "#,##0") & "]"
A/B Test bayesiano simple
// Versión A: 100 visitas, 10 conversiones (alpha=11, beta=91)
// Versión B: 100 visitas, 15 conversiones (alpha=16, beta=86)
// ¿Cuál es mejor con qué probabilidad?
// ✅ Probabilidad de que la tasa de conversión de B > 5%
P(B > 0.05) = 1 - BETA.DIST(0.05, 16, 86, TRUE)
Combinatoria estadística
Aunque las funciones combinatorias (COMBIN, COMBINA, PERMUT) están documentadas en la página de Funciones Matemáticas, aquí te dejamos un recordatorio rápido porque son fundamentales para cálculos de probabilidad.
| Función | Qué calcula | Ejemplo |
|---|---|---|
COMBIN(n, k) |
Combinaciones sin repetición | ¿De cuántas formas puedo elegir 3 productos de 10? → COMBIN(10, 3) = 120 |
COMBINA(n, k) |
Combinaciones con repetición | ¿De cuántas formas puedo elegir 3 productos de 10 (con repetición)? → COMBINA(10, 3) = 220 |
PERMUT(n, k) |
Permutaciones (orden importa) | ¿De cuántas formas puedo ordenar 3 productos de 10? → PERMUT(10, 3) = 720 |
Patrones de análisis estadístico
// Tabla calculada con resumen estadístico de ventas
Resumen Estadístico =
DATATABLE(
"Estadístico", STRING,
"Valor", DOUBLE,
{
{"Media", AVERAGE('Ventas'[Importe])},
{"Mediana", MEDIAN('Ventas'[Importe])},
{"Desv. Estándar", STDEV.S('Ventas'[Importe])},
{"Mínimo", MIN('Ventas'[Importe])},
{"Q1 (25%)", PERCENTILE.INC('Ventas'[Importe], 0.25)},
{"Q3 (75%)", PERCENTILE.INC('Ventas'[Importe], 0.75)},
{"Máximo", MAX('Ventas'[Importe])}
}
)
Es Outlier =
VAR Q1 = PERCENTILE.INC(ALL('Ventas'[Importe]), 0.25)
VAR Q3 = PERCENTILE.INC(ALL('Ventas'[Importe]), 0.75)
VAR IQR = Q3 - Q1
RETURN
OR(
'Ventas'[Importe] < Q1 - 1.5 * IQR,
'Ventas'[Importe] > Q3 + 1.5 * IQR
)
¡Impresionante — ya manejas estadística en DAX! Ahora pasamos a algo más visual y muy usado en el día a día: las funciones de texto para formatear, limpiar y transformar cadenas de caracteres. 🚀