1avergne

Gérer les hiérarchies déséquilibrées

2023-01-18

image

On est habitué à utiliser des hiérarchies où la somme des éléments d’un niveau donne le résultat pour le niveau parent. Par exemple le CA de chaque produit vendu dans une boutique donne le CA de la boutique ; le CA de toutes les boutiques dans un pays donne le CA du pays. Mais certaines hiérarchies ne fonctionnent pas comme ça :

Dans ces deux cas une agrégation naturelle n’est pas possible, il faut adapter la donnée ou son traitement.

Valeurs non-additives

Pour les valeurs non-additives on peut ajouter des valeurs de régulation pour corriger les écarts entre les sous-niveaux et le total. Par exemple, si j’ai une hiérarchie Pays –> Boutique et que je veux afficher les valeurs :

Si j’intègre les données correctement :

Pays Boutique CA
FR Paris 67
FR Lyon 38
FR Perpignan 15

Dans ce cas le cumulé des boutiques est 120. Mais je veux afficher 123 (ne me demandez pas pourquoi, c’est le problème du comptable). Je dois donc ajouter une ligne de régulation :

Pays Boutique CA
FR Paris 67
FR Lyon 38
FR Perpignan 15
FR NA 3
└───FR = 123
    ├───Paris = 67
    ├───Lyon = 38
    ├───Perpigna = 15
    └───NA = 3

Néanmoins la vérification des données et l’ajout de chaque ligne de régulation peuvent être très fastidieux et couteux dès qu’on a plusieurs catégories et plusieurs niveaux. On risque aussi d’ajouter des erreurs dans les données alors qu’elles nous ont toutes été fournies est qu’il ne devrait pas être nécessaire de les recalculer. Enfin, la régulation sera visible, ce qui n’est pas toujours souhaité par l’utilisateur.

Hiérarchie non-équilibrée

Lorsqu’on a un nombre de niveaux différent dans les branches d’une hiérarchie, la solution la plus courante est d’ajouter des niveaux fictifs pour égaliser les branches. Si dans ma base de données, j’ai en plus des données de la France, les valeurs d’autres pays mais sans le détail par boutique :

Pour intégrer les données je vais devoir préciser une boutique pour ces pays : Pays | Boutique | CA — | — | — FR | Paris | 67 FR | Lyon | 38 FR | Perpignan | 15 FR | NA | 3 ES | Boutiques ES | 80 IT | Boutiques IT | 55

├───FR = 123
│   ├───Paris = 67
│   ├───Lyon = 38
│   ├───Perpignan = 15
│   └───NA = 3
├───ES = 80
│   └───Boutiques ES = 80
└───IT = 55
    └───Boutiques IT = 55

La bonne méthode

La méthode idéale (à mon avis) pour traiter les deux situations décrites ci-dessus est d’intégrer l’ensemble des données, sans régulation, pour recréer la hiérarchie et les mesures en DAX.

Par exemple, on souhaite analyser l’efficacité d’une population pour exécuter une tâche quelconque. Les personnes sont rassemblées en sections, plusieurs sections forment une équipe, plusieurs équipes forment un groupe. Mais les groupes peuvent aussi être un ensemble de personnes sans répartition par équipe. Et les équipes peuvent être un ensemble de personnes sans répartition par section On a deux mesures :

└───Root : Eff = 55% / Pax = 35
    ├───Grp_A : Eff = 60% / Pax = 9
    │   ├───Eqp_A1 : Eff = 40% / Pax = 5
    │   └───Eqp_A2 : Eff = 70% / Pax = 4
    │       ├───Sec_A2_ker : Eff = 75% / Pax = 2
    │       └───Sec_A2_bis : Eff = 55% / Pax = 2
    ├───Grp_B : Eff = 65% / Pax = 16
    │   ├───Eqp_B1 : Eff = 40% / Pax = 6
    │   │   ├───Sec_B1_ker : Eff = 70% / Pax = 2
    │   │   ├───Sec_B1_bis : Eff = 40% / Pax = 2
    │   │   └───Sec_B1_ter : Eff = 20% / Pax = 2
    │   ├───Eqp_B2 : Eff = 35% / Pax = 5
    │   └───Eqp_B3 : Eff = 30% / Pax = 5
    └───Grp_C : Eff = 30% / Pax = 10

Les données en entrée

On récupère les données au format tabulaire : Elément | Parent | ValeurA | ValeurB | ...

Elément Parent Eff Pax
Root null 55 null
Grp_A Root 60 null
Eqp_A1 Grp_A 40 5
Eqp_A2 Grp_A 70 null
Sec_A2_ker Eqp_A2 75 2
Sec_A2_bis Eqp_A2 55 2
Grp_B Root 65 null
Eqp_B1 Grp_B 40 null
Sec_B1_ker Eqp_B1 70 2
Sec_B1_bis Eqp_B1 40 2
Sec_B1_ter Eqp_B1 20 2
Eqp_B2 Grp_B 35 5
Eqp_B3 Grp_B 30 5
Grp_C Root 30 10

Il est important que la racine de la hiérarchie (Root) soit une valeur nulle comme parent (et pas une valeur vide). Il est possible d’avoir plusieurs racines qui sont chacunes au début d’une hiérarchie.

On ajoute également une colonne d’index.

image

Colonnes calculées

Un fois la table chargée dans le modèle, on va créer plusieurs colonnes et mesures pour rendre la hiérarchie facilement utilisable. Pour cela on va notamment utiliser les fonctions parents et enfants.

On va à présent recomposer la hiérarchie en ajoutant une colonne par niveau. Il y a 4 niveaux dans notre exemple, il faut donc ajouter 4 colonnes.

Niveau 1 = PATHITEM(AnalysePerf[Path], 1)
Niveau 2 = PATHITEM(AnalysePerf[Path], 2)
Niveau 3 = PATHITEM(AnalysePerf[Path], 3)
Niveau 4 = PATHITEM(AnalysePerf[Path], 4)

Les colonnes créées peuvent être réunies dans une hiérarchie.

image

NB : La méthode pour répéter les niveaux à la place de valeurs vides est décrite plus bas.

Champs calculés

La table est prète à être utilisée, on va créer les mesures qui vont gérer les valeurs non-additives et additives.

Indicateurs version 1

image

Attention, en filtrant sur un élément en particulier de la hiérarchie : les mesures renvoient des valeurs uniquement pour cet élément et ses enfants. Par exemple si je sélectionne Eqp_2 j’aurai le résultat pour Eqp_2, Sec_A2_ker et Sec_A2_bis. Pour les niveaux supérieurs je n’ai pas de données. Ce comportement n’est pas incohérent et est celui attendu dans la plupart des cas.

image

Indicateurs version 2 : conservation des totaux

Si on souhaite afficher les valeurs des niveaux supérieurs (les totaux), lorsque la hiérarchie est filtrée, il faut modifier les mesures :

image

Indicateurs version 3 : répétition des parents

Si on affiche ces mesures dans un histogramme, seules les valeurs pour le niveau en cours sont visibles. Par exemple si on se place dans la hiérarchie au niveau 3 (Eqp_A1, Eqp_A2, etc.) on ne verra pas de résultat pour l’élément Grp_C qui ne contient pas de niveau enfant.

image

Si on souhaite afficher les éléments parents dans le visuel il faut à nouveau modifier les mesures. On reprend la version 2 des mesures et on remplace l’appel à [BrowseDepth] par le calcul MIN([BrowseDepth], [RowDepth]) : on garde le plus petit niveau entre les deux indicateurs.

image

Indicateurs v1, v2 & v3

image

Segments

Le visuel Segment dans Power BI affiche tous les niveaux d’une hiérarchie. Si on l’utilise pour afficher les 4 niveaux, des valeurs (Vide) seront affichées pour chaque niveau. Il faut donc filtrer ces valeurs.

La mesure BrowseDepth est non nulle lorsqu’il faut afficher le niveau, on pourrait vouloir l’utiliser pour filtrer le segment. Mais en faisant cela on ne garde que les branches de la hiérarchie avec des niveaux complets (de 1 à 4) ; en effet le visuel segment n’affiche que des branches complètes. Ce n’est donc pas la bonne solution.

On peut utiliser la colonne Leaf pour ne conserver que les éléments qui terminent une branche. Dans ce cas on ne perd pas d’élément dans la hiérarchie. Mais les braches incomplètes (avec moins de 4 niveaux) comportent des valeurs (Vide).

image

On remplace ses valeurs (Vide) en “complétant” les branches. Si un niveau est vide, on prend le nom du niveau précédent :

Niveau 1 = PATHITEM(AnalysePerf[Path], 1)
Niveau 2 = IF(PATHITEM(AnalysePerf[Path], 2) <> "", PATHITEM(AnalysePerf[Path], 2), [Niveau 1])
Niveau 3 = IF(PATHITEM(AnalysePerf[Path], 3) <> "", PATHITEM(AnalysePerf[Path], 3), [Niveau 2])
Niveau 4 = IF(PATHITEM(AnalysePerf[Path], 4) <> "", PATHITEM(AnalysePerf[Path], 4), [Niveau 3])