avatorl / Deneb-Vega-Help

Do you need help with Deneb custom visual for Power BI and/or Vega visualization grammar? Create an issue here to get assistance from Deneb community expert Andrzej Leszkiewicz.
3 stars 0 forks source link

Vega - Add link between nodes which are at the same level. #4

Closed DW19904 closed 3 months ago

DW19904 commented 3 months ago

Hi,

I have the visual below:

Vega Visual

But i need the visual now to have links between nodes which are at the same level, these would be determined by a column in my dataset. I have gone through the Vega documentation and unable to find a suitable way to get it to work. My desired outcome would look like below, note the additional red links.

Vega Visual with links

Below also is my current vega code, any help or direction is appreciated.

{ "width": 1100, "height": 600, "padding": 5, "signals": [ { "name": "labels", "value": true, "bind": {"input": "checkbox"} }, { "name": "layout", "value": "tidy", "bind": { "input": "radio", "options": ["tidy", "cluster"] } }, { "name": "links", "value": "diagonal", "bind": { "input": "select", "options": [ "line", "curve", "diagonal", "orthogonal" ] } }, { "name": "separation", "value": false, "bind": {"input": "checkbox"} } ], "data": [ { "name": "dataset", "transform": [ { "type": "stratify", "key": "id", "parentKey": "parent" }, { "type": "tree", "method": { "signal": "layout" }, "size": [ {"signal": "height"}, {"signal": "width - 100"} ], "separation": { "signal": "separation" }, "as": [ "y", "x", "depth", "children" ] } ] }, { "name": "links", "source": "dataset", "transform": [ {"type": "treelinks"}, { "type": "linkpath", "orient": "horizontal", "shape": {"signal": "links"} } ] } ], "scales": [ { "name": "color", "type": "linear", "range": {"scheme": "lightgreyteal"}, "domain": {"data": "dataset", "field": "depth"}, "zero": true } ], "marks": [ { "type": "path", "from": {"data": "links"}, "encode": { "update": { "path": {"field": "path"}, "stroke": {"value": "#ccc"}, "strokeDash": {"signal": "datum.target.relationship === 'X' ? [5,5] : null"} } } }, { "type": "symbol", "from": {"data": "dataset"}, "encode": { "enter": { "size": {"value": 1000}, "stroke": {"value": "#fff"} }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "fill": { "scale": "color", "field": "depth" }, "shape": { "signal": "datum.gender === 'Male' ? 'square' : 'circle'" } } } }, { "type": "text", "from": {"data": "dataset"}, "encode": { "enter": { "text": {"field": "name"}, "fontSize": {"value": 12}, "baseline": {"value": "middle"} }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "dx": {"signal": "datum.children ? -20 : 20"}, "align": {"signal": "datum.children ? 'right' : 'left'"}, "opacity": {"signal": "labels ? 1 : 0"} } } } ] }

avatorl commented 3 months ago

But i need the visual now to have links between nodes which are at the same level, these would be determined by a column in my dataset.

You'll need to add either "rule" or "path" mark. If they are just straight lines, then I think "rule" mark is the simplest option.

https://vega.github.io/vega/docs/marks/rule/

The mark will be based on a new table, let's say "data-same-level-links" table.

"from": {"data": "data-same-level-links"} mark property means there will be a "rule" (line) created for each row in "data-same-level-links" table.

To prepare "data-same-level-links" table, transform your data to make a table that contains x, y, x2, y2 coordinates for each red link.

You likely have a "tree" data table that is similar to this: image

And you probably have a list of same level nodes to connect. Use https://vega.github.io/vega/docs/transforms/lookup/ transform to get x, y, x2, y2 coordinates from the "tree" table.

DW19904 commented 3 months ago

Yes thank you, i am getting there now.

I have the supporting table for the same level links, and i have an x and y coordinate from my original table (below), but i am struggling how to get the x2 and y2 coordinates in? As i need these to create the rule?

image

your help is much appreciated, thank you.

avatorl commented 3 months ago

A link connects 2 nodes: source and target. x2 and y2 are x and y coordinates of the second (target) node.

For example: "Uncle" node has x and y coordinates "Auntie" node has x and y coordinates

Uncle-Auntie link connects both nodes by drawing a line from x, y point to x2, y2 point. x = Uncle x, y = Uncle y, x2 = Auntie x, y2 = Auntie y

avatorl commented 3 months ago

If you have a list of links to create: Source Target "Uncle" "Auntie"

And a list of coordinates: Node x y "Uncle" 1 1 "Auntie" 2 3

Then you can merge both lists using lookup transform to get a list with x, y, x2, y2 coordinates for each link: Source Target x y x2 y2 "Uncle" "Auntie" 1 1 2 3

In your case different nodes have the same names (multiple "Uncle" nodes etc.), so you need to use some Ids instead of the names.

DW19904 commented 3 months ago

The tables are not hard-coded, in the vega, They are in the field well in PBI.

I am struggling to get the lookup transform working and this could be why i think?

I have the Source, Target, x and y as part of the dataset, but still cant figure out how to have that different dataset that includes the y2 and x2 for each rule that i require.

{ "width": 1100, "height": 600, "padding": 5, "signals": [ { "name": "labels", "value": true, "bind": {"input": "checkbox"} }, { "name": "layout", "value": "tidy", "bind": { "input": "radio", "options": ["tidy", "cluster"] } }, { "name": "links", "value": "diagonal", "bind": { "input": "select", "options": [ "line", "curve", "diagonal", "orthogonal" ] } }, { "name": "separation", "value": false, "bind": {"input": "checkbox"} } ], "data": [ { "name": "dataset", "transform": [ { "type": "stratify", "key": "id", "parentKey": "parent" }, { "type": "tree", "method": { "signal": "layout" }, "size": [ {"signal": "height"}, {"signal": "width - 100"} ], "separation": { "signal": "separation" }, "as": [ "y", "x", "depth", "children" ] } ] }, { "name": "links", "source": "dataset", "transform": [ {"type": "treelinks"}, { "type": "linkpath", "orient": "horizontal", "shape": {"signal": "links"} } ] } ], "scales": [ { "name": "color", "type": "linear", "range": { "scheme": "redyellowblue" }, "domain": { "data": "dataset", "field": "generation" }, "zero": true } ], "marks": [ { "type": "path", "from": {"data": "links"}, "encode": { "update": { "path": {"field": "path"}, "stroke": {"value": "#ccc"}, "strokeDash": { "signal": "datum.target.relationship === 'X' ? [5,5] : null" } } } }, { "type": "symbol", "from": {"data": "dataset"}, "encode": { "enter": { "size": {"value": 1000}, "stroke": {"value": "#fff"} }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "fill": { "scale": "color", "field": "generation" }, "shape": { "signal": "datum.gender === 'Male' ? 'square' : 'circle'" } } } }, { "type": "text", "from": {"data": "dataset"}, "encode": { "enter": { "text": {"field": "name"}, "fontSize": {"value": 12}, "baseline": { "value": "middle" } }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "dx": { "signal": "datum.children ? -20 : 20" }, "align": { "signal": "datum.children ? 'right' : 'left'" }, "opacity": { "signal": "labels ? 1 : 0" } } } } ] }

avatorl commented 3 months ago

The tables are not hard-coded, in the vega, They are in the field well in PBI.

Once your Power BI data was loaded into the "dataset" table in Vega, you can create as many additional data tables (based on the "dataset" as you want by applying Vega transformations. This is how "links" data table is created in your Vega specification. It applies "type": "treelinks" and "type": "linkpath" transforms to the original "dataset" ("source": "dataset",).

I don't have you data so it's not clear for me how do you know between which same level nodes have to be linked together. You need to make sure you have a list of same level node pairs that you want to be linked together.

You either can create this list in Power BI and append to your dataset.

Sadly, current version of Deneb support only one "dataset", so in some case it's not easy to send data from Power BI into Deneb https://www.powerofbi.org/2024/01/16/1658/. Just an additional note regarding the "dataset", probably, but not necessary relevant to this specific issue.

Then you'll need just a lookup to add x, y, x2, y2 coordinates to the list of node pairs.

Or you probably can hardcode (?) some rule in Vega specification. Like if there are "Uncle" and "Auntie" nodes with the same parent, then they must be linked together. But this approach likely requires more tricky Vega data transforms.

avatorl commented 3 months ago

If you can export data from Deneb into a .csv file and attach it, I'll probably have a chance to help with the data transformations.

DW19904 commented 3 months ago

Thank you so much, this is really helping with my learning of Vega and seeing how these transformations are done.

I have attached csv from deneb of the data.

Essentially the targetid column where the links need to be made, from id to targetid, thanks again.

FamilyChartVegaData.csv

avatorl commented 3 months ago
    {
      "name": "data-same-level-links",
      "source": "dataset",
      "transform": [
        {"type": "filter", "expr": "datum.targetid != null"},
        {
          "type": "lookup",
          "from": "dataset",
          "key": "targetid",
          "fields": ["id"],
          "values": ["x", "y"],
          "as": ["x2", "y2"]
        }
      ]
    }

But this creates duplicate links (e.g. from id=2 to id=13 and from id=13 to id=2). No a big issue, it will just draw a line twice, but I suggest you to find a way to keep only one targetid for each pair. Probably you can do that in Power BI.

DW19904 commented 3 months ago

fantastic thank you, I have the relevant y2 and x2 now but the rule between the nodes still doesnt seem to show when i have added the rule mark in the below code, i have double checked but there must be something i am misssing.

{ "width": 1100, "height": 600, "padding": 5, "signals": [ { "name": "labels", "value": true, "bind": {"input": "checkbox"} }, { "name": "layout", "value": "tidy", "bind": { "input": "radio", "options": ["tidy", "cluster"] } }, { "name": "links", "value": "diagonal", "bind": { "input": "select", "options": [ "line", "curve", "diagonal", "orthogonal" ] } }, { "name": "separation", "value": false, "bind": {"input": "checkbox"} } ], "data": [ { "name": "dataset", "transform": [ { "type": "stratify", "key": "id", "parentKey": "parent" }, { "type": "tree", "method": { "signal": "layout" }, "size": [ {"signal": "height"}, {"signal": "width - 100"} ], "separation": { "signal": "separation" }, "as": [ "y", "x", "depth", "children" ] } ] }, { "name": "links", "source": "dataset", "transform": [ {"type": "treelinks"}, { "type": "linkpath", "orient": "horizontal", "shape": {"signal": "links"} } ] }, { "name": "data-same-level-links", "source": "dataset", "transform": [ { "type": "filter", "expr": "datum.targetid != null" }, { "type": "lookup", "from": "dataset", "key": "targetid", "fields": ["id"], "values": ["x", "y"], "as": ["x2", "y2"] } ] } ], "scales": [ { "name": "color", "type": "linear", "range": { "scheme": "redyellowblue" }, "domain": { "data": "dataset", "field": "generation" }, "zero": true } ], "marks": [ { "type": "path", "from": {"data": "links"}, "encode": { "update": { "path": {"field": "path"}, "stroke": {"value": "#ccc"}, "strokeDash": { "signal": "datum.target.relationship === 'X' ? [5,5] : null" } } } }, { "type": "symbol", "from": {"data": "dataset"}, "encode": { "enter": { "size": {"value": 1000}, "stroke": {"value": "#fff"} }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "fill": { "scale": "color", "field": "generation" }, "shape": { "signal": "datum.gender === 'Male' ? 'square' : 'circle'" } } } }, { "type": "text", "from": {"data": "dataset"}, "encode": { "enter": { "text": {"field": "name"}, "fontSize": {"value": 12}, "baseline": { "value": "middle" } }, "update": { "x": {"field": "x"}, "y": {"field": "y"}, "dx": { "signal": "datum.children ? -20 : 20" }, "align": { "signal": "datum.children ? 'right' : 'left'" }, "opacity": { "signal": "labels ? 1 : 0" } } } }, { "type": "rule", "from": { "data": "data-same-level-links"}, "encode": { "update": { "x": {"field": "x"}, "y": {"field": "y"}, "x2": {"field": "x2"}, "y2": {"field": "y2"}, "stroke": { "value": "steelblue" }, "strokeWidth": {"value": 2} } } } ] }

avatorl commented 3 months ago

I don't see any problem. Although, I'm not testing in Deneb, but in Vega online editor

https://vega.github.io/editor/#/url/vega/N4Ig7glgJgLgFiAXARmQBjQGhHAphAczhiQDYNsAHAQyiggDsCkBWbAZ0IeoBt2kA2qG4BbXEhA9qAI1x8Q2AG68AruMQwATmuzTGUJKEaUVJRCADGeCwGtpAewAeIAL4vMw6mIlSAnvdMFEGUeNQkYaF8gvQYDRCMGEzMQTVoIeyD7Sgj7Bn5EARAIqCjsC1D2GFxNEABdNw8QUXVJRht+JVUW+moCXN5o-UMQY0DzdjlcCxJsLJy8wVaGcTKVTUUVkB6+7h5MzXh7HYH6909vcdwaVPmgkLDEADNeCd0h+JHEsctrOydXFy1bBQagwaiCc7dUHUCYzEBrPbmYgwSjsRAAenRBAglQAdNj4CppCoJpoLLkqgwYLjySJ0dRlDB7JoeOjkKQOdRSNIoKQAJwAFjouAA7MghQAmCW4CUWR5QNCC8jSaTo1JgdEi2UCgXSNDIPmoAAc0hFLCNEr5AGYoNJSJajRZRVarXrqCKLBZrbg+eiAGJeCA8XwAYTg1AOADVcARqAARaE09iKIKPZkiUHDGC+SgtCzJoLXCYSaimDLAuQQEQQKo1czYdxFVJ5NOaEQQoo5lqVG4QR6lEA2XBRczQQsR3BUgDSw4k10nJDOndz4U0uE2YkOcVAnAIux81H8gUbnAAXuohCBd-ukfgiIvGteBuZILA4AACAC07-QaFcQKvK4I1BdIGGGJ9EUA64QNyVxsBhRYB2cCtsgQMo4CDKA1zA+p6kaZofDaDorwCMkoTBWEgi0agW3TDtsxXcwtHXHgiLg0AGJaViGBsGh4H2CAFwkOBmQgU8KQGDhw0YncuGfJZ2gBPDIQkEEwU-dgvFwT8eFwDYeB0tiOFIp1VOhSjsGo2i23orsJEeINayCXBHEoOstlBFQRFxMFNAIXBinfABCABed8GBUHg9iXTifHsewbBUShU00ex23MNSYQCoIhxHIoI384pU0EngoHyQoxwA+5cHKkBkJAKIAIQgo6olIJfDa3DAQ4CxeBqjsCPMckeGZKi7PMbjcAjIJm388CrFwC4UlwEo5BGsBpFCcRGygNLqEYYZMrMijsuwBy5DiEB-OWXtYMbc9UqQLQ1G6kAM00RSCg48aQD4tCQEeVL0tAI6JrYxtJ3JKB1FAJK1Jh37QQQD5ztKuckbgq8tAShHqokABiT0LExnscYTdhkdkvd5LUryfIKgLcTXKR5gpiBKHfEKuffAByAANHn3wAfnfAQ2BYWp30QcLIuitwYp+9hfBEBw9jOoHDuhY6sofEBIfsaHhgXapwLE3GuiQX80BPbGh2GPHzHxx5nYBbA4dBBHnBRkrLucRsR1AVHLqiRsHKi+a+okYbRrOn2JGu6oYLAk9pIRiDjrp67oc0TnuZ5gBZPrBZFnn2AARxUCdBelnmLAgMldJ5gEGm+xiihcuFAbSzWwW1yiIYYKGEeNusOI74Yg4kAjQ4pABlM37YtlAJUbaQssmxetokas6F0134UoeHhi9wO4-MP3sADgGz4azGoBPq85Mg2nvKsTDsOFr8JTQKX32-zHeBcFNtTZ+nlX4YVKh-EumhCDEGrrzXSjwYBN0bFkagddszAJvJIGQch2Cf2QL-P88sFZt20HvdW3cPigw8upTSYgdJ6TkIZHi-AB5D2GO7Kox8J43wvrfb2F0JAh2wI4Nqgi0bnzav7cRp8hHmA6iTW25st7jCqHITaYQbapSHAAdWgPxD4DsV7y3qEAA

Shouldn't be any difference, but maybe there is some. I would verify in Deneb Vega editor if there is data in "data-same-level-links" table and compare it with what you see on my screenshot (or in Vega online editor):

image

Also put "rule" mark before "symbol" mark if you don't want lines to be drawn on top of the node symbols.

DW19904 commented 3 months ago

Thank you so much for your help! Its working perfectly now and really helped with my learning of Vega! Thanks again.