Comprendre le message d’erreur « Dépendance circulaire détectée » et le corriger

Proposé par André Meyer-Roussilhon

27 avril 2021

Dax

Restez informé, abonnez-vous à la newsletter (2 fois / mois)

8 + 1 =

Présentation du scénario

Dans ce scénario, j’étudie les variations journalières (intraday) d’une action. L’objectif est de compter le nombre de jours où la variation est inférieure à 1%, à 2% , à 3% etc., en tenant compte du volume d’actions échangées (<1 million ou >1 million) :

Pour cela je dispose d’une table ASML demo et de mesures me précisant :

  • La date (c’est une colonne)
  • Un index (colonne indiquant un numéro unique pour chaque ligne de la table)
  • Le plus bas (mesure _bas)
  • Le plus haut (mesure _haut)
  • Le volume (mesure _vol)
  • Le nombre de jours (mesure définie par COUNT(‘ASML demo’[Date]))
  • La variation intraday (mesure définie par ([_haut] – [_bas]) / [_bas])

Pour créer le segment volumétrie, je crée cette fois-ci une colonne définie par

volumétrie = 
IF(
    [_vol] > 1000000,
    ">1M",
    "<1M"
)

Jusque là pas de message d’erreur.

Pour créer l’axe X (horizontale) de mon graphique, je dois également créer une colonne (la mesure ne permet pas de créer cet axe) : il s’agit de multiplier la variation par 100 et de prendre l’arrondi supérieur. C’est l’amplitude de variation que vous voyez sur le graphique. La première idée est donc :

groupe variation = 
"<" & CEILING( [_variation intraday] *100 , 1 ) &"%"

C’est à ce moment que l’erreur liée à la dépendance circulaire apparaît.

Pourquoi une erreur liée à la dépendance circulaire ?

La dépendance circulaire, comme son nom l’indique, survient lorsque qu’une colonne B fait référence lors de son calcul à une colonne A, qui elle-même fait référence à la colonne B.

Ce phénomène ne se produit que parce que les deux calculs sont des colonnes ! Une dépendance circulaire n’est pas possible avec une mesure.

(Notez qu’il existe aussi des cas plus rares de dépendance entre deux tables, dans certaines conditions)

Pourtant, dans la formule

"<" & CEILING( [_variation intraday] *100 , 1 ) &"%"

Je ne fais pas référence à une autre colonne !

Et c’est que vous ne devez pas oublier trois règles fondamentales du DAX : tout d’abord, première règle, lors de l’appel d’une mesure dans une formule (ici la mesure _variation intraday), un CALCULATE est toujours implicitement ajouté.

Deuxième règle fondamentale : lorsqu’il est appelé dans un contexte de ligne, CALCULATE déclenche une transition de contexte. Ici, parce que nous sommes en train de créer une colonne, nous sommes bien dans un contexte de ligne.

Troisième règle fondamentale : une transition de contexte consiste à transformer un contexte de ligne en un ensemble de filtres équivalents, et concrètement, à filtrer chacune des autres colonnes de la table.

La boucle est bouclée ! La transition de contexte déclenchée par le CALCULATE implicite dû à l’appel de la mesure _variation intraday fait bien référence aux autres colonnes de la table, et par conséquent à la colonne volumétrie. Mais la colonne volumétrie elle-même fait appel à la mesure _vol : celle-ci introduit donc un CALCULATE implicite et une transition de contexte, qui fait référence à la colonne groupe variation.

En définitive, et de manière implicite, la dépendance circulaire vient donc du fait que groupe variation fait référence à volumétrie qui fait elle-même référence à groupe variation

Mais alors comment s’en sortir ?

La première façon d’éviter cette erreur est d’éviter de créer des colonnes ! Créer des mesures le plus souvent possible est la quatrième règle fondamentale du DAX, et vous ne devez recourir aux colonnes que dans des cas bien précis (dont ceux proposés dans ce scénario).

Dans le cas où la formule doit être une colonne, il faut donc s’arranger pour que le CALCULATE implicite ne déclenche pas une transition de contexte sur toutes les colonnes de la table. Or, et c’est là le secret, si une colonne de votre table permet d’identifier de manière unique chaque ligne, DAX n’aura pas besoin de filtrer toutes les colonnes dans le cadre d’une transition de contexte.

Dans notre scénario, deux colonnes répondent à cette condition : la date et l’index (notez que ce dernier peut être ajouté à l’aide de Power Query).

Je vais donc réécrire la formule de groupe variation, et écrire explicitement le CALCULATE et son filtre, en faisant appel à la fonction ALLEXCEPT, qui enlève tous les filtres sauf celui de la colonne indiquée :

groupe variation = 
"<" &
CALCULATE(
    CEILING([_variation intraday]*100,1),
    ALLEXCEPT('ASML demo', 'ASML demo'[Index])
)
&"%"

Conclusion

N’oubliez pas les quatre règles fondamentales du DAX :

  • L’appel d’une mesure ajoute un CALCULATE implicite
  • Dans un contexte de ligne, CALCULATE déclenche une transition de contexte
  • Une transition de contexte pose un filtre sur toutes les colonnes de la table
  • Ne créez des colonnes que lorsque vous n’avez pas le choix

Et pour ce qui concerne la dépendance circulaire, dans le cadre de la création d’une colonne, le motif explicite CALCULATE (ALLEXCEPT()) est recommandé dès que vous faite appel à une mesure, c’est-à-dire très souvent.

Post-scriptum

Une erreur de débutant consisterait à créer la formule suivante sous forme d’une colonne :

En effet, le filtre ‘ASML demo'[Volume] > 1500000 est traduit par DAX en

FILTER(
    ALL(ASML demo) , 
    'ASML demo'[Volume] > 1500000)
)

C’est le fait que la fonction ALL porte sur toute la table qui génère l’erreur. Là encore, utiliser ALLEXCEPT permet de régler le problème :

Mais rappelons-le, ici, l’erreur principale c’est de vouloir faire de cette formule une colonne : c’est clairement sous forme d’une mesure qu’il faut faire ce calcul.

Articles associés :

Créer un graphique à base zéro pour suivre l’évolution

Créer un graphique à base zéro pour suivre l’évolution

Dans un graphique à base zéro, le premier point sert de référence (le point zéro), et tous les autres sont calculés en pourcentage de variation par rapport à cette référence. Ce type de graphique est utilisé par exemple dans le domaine financier, pour suivre l’évolution du cours d’une action – mais il peut servir pour suivre l’évolution de tout type de donnée.
Le graphique est intimidant, mais sa réalisation est en fait très facile, et repose sur des formules DAX étonnamment simples.

Délai entre deux dates, week-end et jours fériés

Délai entre deux dates, week-end et jours fériés

Une table du temps qui calcule automatiquement les jours fériés ? Vous en rêviez, daxone.fr vous l’offre ! Et j’en profite pour vous donner la méthode pour calculer le nombre de jours écoulés entre deux dates, en excluant les dimanches et les jours fériés (je suis sûr que ça va vous servir)

SUMMARIZECOLUMNS : la super-fonction du DAX

SUMMARIZECOLUMNS : la super-fonction du DAX

Vous ne connaissez peut-être pas cette fonction, et c’est pourtant celle que Power BI utilise le plus fréquemment pour répondre à vos demandes : il suffit de regarder le code généré par Power BI pour afficher votre visuel pour s’en convaincre.
Avec SUMMARIZECOLUMNS , la création d’une table issue de données provenant de différentes autres tables du modèle, incluant de nouveaux indicateurs, et filtrée sur des données précises, devient un jeu d’enfant.