🌳

Funciones Padre-Hijo

PATH, PATHITEM, PATHLENGTH, PATHCONTAINS — maneja jerarquías irregulares como organigramas y planes de cuentas

Avanzado
🔍
🎯

¿Qué es una jerarquía padre-hijo? El problema que resuelven

Imagina que tienes un organigrama de empresa guardado en una tabla. Cada persona tiene un ID y el ID de su manager directo. La jerarquía tiene profundidad variable — la directora tiene 1 nivel, los managers 2, los coordinadores 3, los técnicos 4, los juniors 5... Power BI no sabe cómo manejar jerarquías de profundidad variable directamente. Ahí entran las funciones Padre-Hijo.

El problema en datos

TABLA 'Empleados':

EmpleadoID Nombre ManagerID
E001 Elena García (vacío ← es la CEO)
E002 Carlos López E001
E003 Ana Martín E001
E004 Pedro Ruiz E002
E005 Laura Sanz E002
E006 Miguel Torres E003
E007 Sofia Pérez E004

Jerarquía resultante

Elena García (CEO)
├── Carlos López
│   ├── Pedro Ruiz
│   │   └── Sofia Pérez
│   └── Laura Sanz
└── Ana Martín
    └── Miguel Torres
            

El problema: la profundidad varía por rama (1, 2, 3, 4 niveles...).
La solución: convertir esta estructura recursiva en columnas fijas.

Otros casos de uso típicos

  • Plan de cuentas contable — cuenta padre > subcuenta > subsubcuenta...
  • Taxonomía de productos — categoría > subcategoría > familia > SKU
  • Estructura organizativa — CEO > Director > Manager > Coordinador > Técnico
  • BOM (Bill of Materials) — productos con componentes y subcomponentes
🛤️

PATH — el punto de partida

PATH(<id_columna>, <padre_columna>)
Avanzado
Para cada nodo de la jerarquía, genera una cadena de texto con el camino completo desde la raíz hasta ese nodo, separado por el carácter pipe "|". Es la función base que habilita todo el análisis padre-hijo.

Ejemplo básico:

// Columna calculada en la tabla Empleados:
'Empleados'[Path] = PATH('Empleados'[EmpleadoID], 'Empleados'[ManagerID])

// Resultado:
// Elena García (E001):   "E001"
// Carlos López (E002):   "E001|E002"
// Pedro Ruiz (E004):     "E001|E002|E004"
// Sofia Pérez (E007):    "E001|E002|E004|E007"

// ¡Importante! El campo ManagerID de la raíz (CEO) debe ser BLANK o el mismo ID
// Si tienes texto vacío "" en lugar de BLANK, conviértelo primero:
'Empleados'[ManagerIDLimpio] =
IF('Empleados'[ManagerID] = "", BLANK(), 'Empleados'[ManagerID])

'Empleados'[Path] =
PATH('Empleados'[EmpleadoID], 'Empleados'[ManagerIDLimpio])

Ejemplo plan de cuentas:

// Lo mismo para plan de cuentas:
'Cuentas'[Path] = PATH('Cuentas'[CuentaID], 'Cuentas'[CuentaPadreID])
📏

PATHLENGTH — la profundidad

PATHLENGTH(<path>)
Avanzado
Devuelve el número de nodos en el camino (la profundidad del nodo en la jerarquía). La raíz tiene PATHLENGTH = 1, sus hijos directos = 2, etc.

Ejemplo básico:

// Nivel de cada empleado en la jerarquía:
'Empleados'[Nivel] = PATHLENGTH('Empleados'[Path])

// Resultado:
// Elena García: Nivel 1 (la raíz)
// Carlos López: Nivel 2
// Pedro Ruiz:   Nivel 3
// Sofia Pérez:  Nivel 4

Ejemplo con nombres de nivel:

// Nombre del nivel:
'Empleados'[Nombre Nivel] =
SWITCH(
    'Empleados'[Nivel],
    1, "CEO / Dirección",
    2, "Director",
    3, "Manager",
    4, "Coordinador",
    5, "Técnico Senior",
    "Junior"
)

Profundidad máxima:

// Profundidad máxima de la jerarquía:
Niveles Totales = MAXX(ALL('Empleados'), 'Empleados'[Nivel])
🔍

PATHITEM y PATHITEMREVERSE — extraer niveles

PATHITEM(<path>, <posición>, [<tipo>])
Avanzado
Extrae el elemento en la posición indicada del path (empezando desde la raíz, posición 1). Devuelve texto por defecto, o el tipo especificado (INTEGER, etc.) si se indica.

Ejemplo básico:

// Extraer el nivel 1 (la raíz — siempre es la CEO):
'Empleados'[Nivel1 ID] = PATHITEM('Empleados'[Path], 1)
// Siempre devuelve "E001" (Elena García) para todos los empleados

// Extraer el nivel 2 (director directo de la CEO):
'Empleados'[Nivel2 ID] = PATHITEM('Empleados'[Path], 2)
// Para Pedro Ruiz: "E002" (Carlos López)
// Para Elena García: BLANK (no tiene nivel 2)

Extraer el propio ID:

// Extraer el propio ID (último nivel = el propio nodo):
'Empleados'[Mi ID desde Path] =
PATHITEM('Empleados'[Path], PATHLENGTH('Empleados'[Path]))

Convertir ID a nombre:

// Usar LOOKUPVALUE para convertir ID en nombre:
'Empleados'[Nombre Nivel1] =
LOOKUPVALUE(
    'Empleados'[Nombre],
    'Empleados'[EmpleadoID],
    PATHITEM('Empleados'[Path], 1)
)
PATHITEMREVERSE(<path>, <posición>, [<tipo>])
Avanzado
Como PATHITEM pero cuenta desde el final (desde el nodo actual hacia la raíz). Posición 1 = el propio nodo, posición 2 = su padre, posición 3 = su abuelo.

Ejemplo básico:

// El propio empleado (posición 1 desde el final):
'Empleados'[Mi ID] = PATHITEMREVERSE('Empleados'[Path], 1)
// Siempre devuelve el propio EmpleadoID

// El manager directo (posición 2 desde el final):
'Empleados'[Manager Directo ID] = PATHITEMREVERSE('Empleados'[Path], 2)
// Si Sofia Pérez: devuelve "E004" (Pedro Ruiz, su manager)

// El "abuelo" en la jerarquía:
'Empleados'[Nivel -2 ID] = PATHITEMREVERSE('Empleados'[Path], 3)

Diferencia PATHITEM vs PATHITEMREVERSE:

// Para Sofia Pérez, Path = "E001|E002|E004|E007":

PATHITEM('Empleados'[Path], 1)          → "E001" (Elena, la raíz)
PATHITEM('Empleados'[Path], 2)          → "E002" (Carlos)
PATHITEM('Empleados'[Path], 3)          → "E004" (Pedro)
PATHITEM('Empleados'[Path], 4)          → "E007" (Sofia, ella misma)

PATHITEMREVERSE('Empleados'[Path], 1)   → "E007" (Sofia, ella misma)
PATHITEMREVERSE('Empleados'[Path], 2)   → "E004" (Pedro, su manager)
PATHITEMREVERSE('Empleados'[Path], 3)   → "E002" (Carlos, su gran-manager)
PATHITEMREVERSE('Empleados'[Path], 4)   → "E001" (Elena, la raíz)

PATHCONTAINS — verificar pertenencia

PATHCONTAINS(<path>, <elemento>)
Avanzado
Devuelve TRUE si el elemento especificado está en el path. Esencial para verificar si un nodo es ancestro de otro, o para filtrar toda la descendencia de un nodo.

Ejemplo básico:

// ¿Es Carlos López (E002) un ancestro de Sofia Pérez?
Es Ancestro = PATHCONTAINS('Empleados'[Path], "E002")
// Para Sofia (Path = "E001|E002|E004|E007"): TRUE
// Para Miguel Torres (Path = "E001|E003|E006"): FALSE

Filtrar subordinados:

// Todos los subordinados directos e indirectos de Carlos López:
Equipo Carlos =
FILTER(
    'Empleados',
    PATHCONTAINS('Empleados'[Path], "E002")
    && 'Empleados'[EmpleadoID] <> "E002"  -- excluir al propio Carlos
)

Ventas del equipo:

// Ventas del equipo de un manager (incluyendo todos sus subordinados):
Ventas Equipo =
CALCULATE(
    SUM('Ventas'[Importe]),
    FILTER(
        'Empleados',
        PATHCONTAINS(
            'Empleados'[Path],
            SELECTEDVALUE('Empleados'[EmpleadoID])
        )
    )
)
🏆

El patrón completo — aplanar la jerarquía

El patrón definitivo para trabajar con jerarquías padre-hijo en Power BI

Power BI necesita columnas fijas para hacer jerarquías visuales (como las que ves en una matriz expandible). El patrón estándar es convertir la jerarquía padre-hijo recursiva en N columnas fijas (una por nivel máximo de la jerarquía). Así Power BI puede hacer drill-down.

Paso 1 — Determinar la profundidad máxima

// Primero crea la columna Path:
'Empleados'[Path] = PATH('Empleados'[EmpleadoID], 'Empleados'[ManagerID])

// Luego la columna Nivel:
'Empleados'[Nivel] = PATHLENGTH('Empleados'[Path])

// Ver la profundidad máxima (para saber cuántas columnas necesitas):
// En DAX Studio: EVALUATE ROW("MaxNivel", MAXX(ALL('Empleados'), 'Empleados'[Nivel]))

Paso 2 — Crear columnas por nivel (asumiendo max 5 niveles)

// Columnas de ID por nivel:
'Empleados'[Nivel1 ID] = PATHITEM('Empleados'[Path], 1)

'Empleados'[Nivel2 ID] =
IF(PATHLENGTH('Empleados'[Path]) >= 2, PATHITEM('Empleados'[Path], 2), BLANK())

'Empleados'[Nivel3 ID] =
IF(PATHLENGTH('Empleados'[Path]) >= 3, PATHITEM('Empleados'[Path], 3), BLANK())

'Empleados'[Nivel4 ID] =
IF(PATHLENGTH('Empleados'[Path]) >= 4, PATHITEM('Empleados'[Path], 4), BLANK())

'Empleados'[Nivel5 ID] =
IF(PATHLENGTH('Empleados'[Path]) >= 5, PATHITEM('Empleados'[Path], 5), BLANK())

Paso 3 — Convertir IDs a nombres

// Columnas de Nombre por nivel (más útiles para visualización):
'Empleados'[Nivel1 Nombre] =
LOOKUPVALUE('Empleados'[Nombre], 'Empleados'[EmpleadoID], 'Empleados'[Nivel1 ID])

'Empleados'[Nivel2 Nombre] =
IF(NOT ISBLANK('Empleados'[Nivel2 ID]),
   LOOKUPVALUE('Empleados'[Nombre], 'Empleados'[EmpleadoID], 'Empleados'[Nivel2 ID]),
   BLANK())

'Empleados'[Nivel3 Nombre] =
IF(NOT ISBLANK('Empleados'[Nivel3 ID]),
   LOOKUPVALUE('Empleados'[Nombre], 'Empleados'[EmpleadoID], 'Empleados'[Nivel3 ID]),
   BLANK())
// ... repetir para Nivel4 y Nivel5

Paso 4 — Crear la jerarquía en Power BI

En el panel de Campos:

  • → Clic derecho en 'Nivel1 Nombre' → "Crear jerarquía"
  • → Arrastrar 'Nivel2 Nombre', 'Nivel3 Nombre'... a la jerarquía
  • → Usar la jerarquía en una matriz con drill-down habilitado

Paso 5 — Medidas que respetan la jerarquía

// Esta medida funciona en cualquier nivel de la jerarquía:
Ventas Equipo =
SUMX(
    FILTER(
        ALL('Empleados'),
        PATHCONTAINS(
            'Empleados'[Path],
            SELECTEDVALUE('Empleados'[EmpleadoID])
        )
    ),
    CALCULATE(SUM('Ventas'[Importe]))
)
🏆 Best Practice
Este es el patrón estándar que todo modelo con jerarquías padre-hijo debe seguir. Las columnas calculadas de Path, Nivel, y Nivel1-5 deben estar en la tabla fuente. Luego creas una jerarquía visual en Power BI para habilitar drill-down en matrices y visuales.
💼

Casos de uso reales

Patrón 1 — Plan de cuentas contable

// Misma lógica pero con cuentas:
'Cuentas'[Path] = PATH('Cuentas'[CuentaID], 'Cuentas'[CuentaPadreID])
'Cuentas'[Nivel] = PATHLENGTH('Cuentas'[Path])
'Cuentas'[Cuenta Nivel1] = PATHITEM('Cuentas'[Path], 1)  // Activo / Pasivo / Patrimonio
'Cuentas'[Cuenta Nivel2] =
IF(PATHLENGTH('Cuentas'[Path]) >= 2, PATHITEM('Cuentas'[Path], 2), BLANK())

'Cuentas'[Cuenta Nivel3] =
IF(PATHLENGTH('Cuentas'[Path]) >= 3, PATHITEM('Cuentas'[Path], 3), BLANK())

// Saldo acumulado (incluyendo todas las subcuentas hijas):
Saldo Acumulado =
CALCULATE(
    SUM('Movimientos'[Importe]),
    FILTER(
        ALL('Cuentas'),
        PATHCONTAINS('Cuentas'[Path], SELECTEDVALUE('Cuentas'[CuentaID]))
    )
)

Patrón 2 — Headcount y organigrama

// Total de personas en el equipo de un manager (incluyendo todos los niveles):
Headcount Total =
COUNTROWS(
    FILTER(
        ALL('Empleados'),
        PATHCONTAINS('Empleados'[Path], SELECTEDVALUE('Empleados'[EmpleadoID]))
        && 'Empleados'[EmpleadoID] <> SELECTEDVALUE('Empleados'[EmpleadoID])
    )
)

// Manager con más personas a su cargo:
Manager Mayor Equipo =
MAXX(
    ALL('Empleados'),
    CALCULATE(
        COUNTROWS(
            FILTER(
                ALL('Empleados'),
                PATHCONTAINS('Empleados'[Path], 'Empleados'[EmpleadoID])
            )
        )
    )
)

Patrón 3 — BOM (Bill of Materials) — coste total de un producto

// Tabla de componentes: ProductoID | ComponenteID | Cantidad | Coste
'Componentes'[Path] =
PATH('Componentes'[ComponenteID], 'Componentes'[ComponentePadreID])

// Coste total incluyendo todos los subcomponentes:
Coste Total BOM =
SUMX(
    FILTER(
        ALL('Componentes'),
        PATHCONTAINS(
            'Componentes'[Path],
            SELECTEDVALUE('Componentes'[ComponenteID])
        )
        && PATHLENGTH('Componentes'[Path]) = MAXX(
            ALL('Componentes'),
            PATHLENGTH('Componentes'[Path])
        )
    ),
    'Componentes'[Coste] * 'Componentes'[Cantidad]
)
🎉 ¡Enhorabuena! Has completado la guía DAX completa
¡Lo conseguiste! Has completado la guía más completa de funciones DAX en español. Desde SUM hasta PATH, pasando por CALCULATE, Window Functions, Visual Calculations y Grupos de Cálculo. Ahora tienes una referencia de cabecera para cualquier función DAX que necesites.

Recuerda: DAX se aprende usándolo. No te preocupes si no recuerdas todo — para eso está esta guía. Lo importante es saber que existe la función, qué hace y dónde buscarla cuando la necesites.

¡Tú puedes! 🌸🚀