Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

The Fabric Community site will be in read-only mode on Monday, Feb 24 from 12:01 AM to 8 AM PST for scheduled upgrades.

Reply
scee07
Resolver I
Resolver I

Multiparent Graph, Recursiveness

Hi community,

I am addressing the following issue, stemming from 2 observations concerning graph structures in DAX:

  • The build in PATH function does not support several parents for one child.
  • DAX is not recursive. This means that the implementation of graph exploration strategies starting with only e.g., an adjacency list is not possible in DAX

My business problem is simple to understand: in a hierarchical company structure revenue of a child organization is recognized for several parents. A common application would be bonus agreements, where employees get bonus payments for revenues in child organizations. These bonus agreements cover then a completely internal structure which has nothing to do with the structure of the legal entities of the company, they rather are arbitrary.

For these reasons I executed the entire graph search in Power Query and then handed over the result for visualization. The approach is outlined below.

I have then several questions, where I would like your help:

  • in the approach below, are there elements which can be replaced by features that are out of the box either in Power Query or DAX?
  • If not, is there a pattern here, where necessarily recursive work is executed in Power Query and then handed over to the Vertipaq engine?
  • Are there any performance concerns when doing so?

Approach:
We have as toy model a turnover table which specifies the turnover in the period and the child organizations.

scee07_3-1674046605880.png

 

 

Organizations can have turnover or not (if they are pure umbrella organizations they do not). The real model has over 100 organizations and ideally, we explore the graph to get the dept of graph and do not put in this information beforehand. Therefore, the algorithm must be recursive.

This is results in the following adjacency list:

{{1,{3}},{2,{3}},{3,{4,5}},{4,{7}},{5,{}},{6,{4}},{7,{}}}

 

Now some standard Depth First Search (DFS) is applied:

DFS = (adjacencyList as list, visited as list, queue as list) =>

let 

    result = if List.NonNullCount(queue) = 0

                then visited

                    else

                        let

                         v = Pop(queue){0},

                         restQueue = Pop(queue){1},

                         children = GetNeighbours(adjacencyList, v)

                         in

                            if List.Contains(visited, v)

                                then @DFS(adjacencyList, visited, restQueue)

                                    else @DFS(adjacencyList, List.Combine ({ {v},  visited}), List.Combine({restQueue ,children}))

in

    result

All vertices are taken and the children are determined:

GetDescendants = (adjacencyList as list, v as number) =>

let

children = GetNeighbours(adjacencyList, v ),

result = DFS(adjacencyList, {}, children)

in

result

with

GetNeighbours  = (adjacencyList as list, v as number) =>

let

    selection = List.Select(adjacencyList, (x) => x{0} = v),

    result = if List.Count(selection) > 0 then selection{0}{1} else {}   

in

    result



The output can be transformed into  a table with the added feature that a vertex is its own child.

let

    Vertices = Table.SelectColumns(Turnover, "Vertex"),

    Descendants = Table.AddColumn(Vertices, "Col1",

        each

            let

                v = [Vertex],

                children = GetNeighbours(TurnoverTree, [Vertex]),

                result = {v, GetDescendents(TurnoverTree, v)}

            in

                result),

        ls1 = Table.Column(Descendants, "Col1"),

        ls2 = List.Transform(ls1, (ls) =>

            let

                v = ls{0},

                n = List.Count(ls{1}),

                elementToAdd = if n > 0 then

                    let

                        generator = List.Repeat({v},n),

                        zip = List.Zip({generator, ls{1}})

                    in

                        zip

                                else {}

            in

                elementToAdd),

        ls3 = List.Combine(ls2),

        ls4 = List.Transform(Table.Column(Vertices, "Vertex"), (x) => {x,x}),

        ls5 = List.Combine({ls3, ls4}),

        tab = #table( {"Vertex", "Child"}, ls5)

in

    tab

 

scee07_4-1674046675161.png

 

 

The rest is then straight forward. We can now assign now turnovers to the parents and have a completely basic unformatted visual like this (a nice visual is not the point of the question)

scee07_5-1674046686096.png

 

 

 

Again, my key questions are:

  • In the approach above, are there elements which can be replaced by features that are out of the box either in Power Query or DAX?
  • If not, is there a pattern here, where necessarily recursive work is executed in Power Query and then handed over to the Vertipaq engine?
  • Are there any performance concerns when doing so?

 

Thank you for your help.

 

Best regards

 

Christian

 

0 REPLIES 0

Helpful resources

Announcements
Las Vegas 2025

Join us at the Microsoft Fabric Community Conference

March 31 - April 2, 2025, in Las Vegas, Nevada. Use code MSCUST for a $150 discount!

FebPBI_Carousel

Power BI Monthly Update - February 2025

Check out the February 2025 Power BI update to learn about new features.

Feb2025 Sticker Challenge

Join our Community Sticker Challenge 2025

If you love stickers, then you will definitely want to check out our Community Sticker Challenge!

Feb2025 NL Carousel

Fabric Community Update - February 2025

Find out what's new and trending in the Fabric community.