nirnroot
nirnroot

Reputation: 724

Deneb/Vega-Lite: Hierarchial y-axis on a Gantt chart

I'm trying to make a Deneb Gantt chart, but I'd like to show the Projects grouped by Assignee ( quite the same way as if I had a Power BI table and I'd stack both Assignee and Project on rows, I could see all the Assignee's projects by clicking the +/- -sign and opening the row)

I now have the Assignees on the y-axis and tasks as ranged bars, but as you can see, it's really confusing (for Assignee 1) if the projects aren't listed on the y-axis as well. I've tried to make facets but I've had zero luck. Are facets even possible with Gantt charts, or how should I go around with this?

Example data:

Project Task Assignee StartDate EndDate
Project1 Task1 Assignee1 01/05/2023 30/06/2023
Project1 Task1 Assignee2 01/04/2023 31/05/2023
Project1 Task2 Assignee3 01/07/2023 14/07/2023
Project2 Task1 Assignee1 01/07/2023 31/07/2023

Example code:

{
  "title": {
    "text": "Title",
    "fontSize": 16,
    "color": "#34282C"
  },
  "data": {"name": "dataset"},
  "layer": [
    {
      "transform": [
        {
          "calculate": "now()",
          "as": "Today"
        }
      ],
      "mark": {
        "type": "rule",
        "color": "Sienna",
        "strokeDash": [0, 0],
        "size": 1,
        "tooltip": true
      },
      "encoding": {
        "x": {
          "field": "Today",
          "type": "temporal"
        }
      }
    },
    {
      "layer": [
        {
          "params": [
            {
              "name": "grid",
              "select": "interval",
              "bind": "scales"
            },
            {
              "name": "highlight",
              "select": {
                "type": "point",
                "on": "mouseover"
              }
            },
            {
              "name": "select",
              "select": "point"
            }
          ],
          "mark": {
            "type": "bar",
            "opacity": 1,
            "cornerRadius": 6,
            "height": 25,
            "tooltip": true,
            "color": "#FFFFFF"
          }
        },
        {
          "mark": {
            "type": "bar",
            "opacity": 0.6,
            "cornerRadius": 6,
            "height": 20,
            "tooltip": true,
            "stroke": "black"
          },
          "encoding": {
            "color": {
              "field": "Assignee",
              "legend": null
            }
          }
        },
        {
          "mark": {
            "type": "text",
            "align": "left",
            "dx": 5,
            "fontSize": 12
          },
          "encoding": {
            "text": {
              "field": "Task",
              "type": "nominal"
            }
          }
        }
      ],
      "encoding": {
        "y": {
          "field": "Assignee",
          "type": "ordinal",
          "axis": {
            "title": null,
            "grid": true,
            "gridWidth": 2,
            "tickBand": "extent",
            "labelFontSize": 15,
            "labelColor": "#34282C",
            "zindex": 1
          }
        },
        "x": {
          "field": "StartDate",
          "type": "temporal",
          "axis": {
            "title": null,
            "format": "%m/%y",
            "labelExpr": "[timeFormat(datum.value, '%m/%y'), timeFormat(datum.value, '%m') == '01' ? timeFormat(datum.value, '%Y') : '']",
            "orient": "top",
            "grid": true,
            "gridWidth": 2,
            "gridColor": "#ddd",
            "gridDash": {
              "condition": {
                "test": {
                  "field": "value",
                  "timeUnit": "month",
                  "equal": 1
                },
                "value": []
              },
              "value": [2, 2]
            },
            "labelOffset": 15,
            "labelAlign": "left",
            "labelPadding": 0
          }
        },
        "x2": {"field": "EndDate"},
        "yOffset": {
          "field": "Assignee"
        },
        "fillOpacity": {
          "condition": {
            "param": "select",
            "value": 1
          },
          "value": 0.3
        },
        "strokeWidth": {
          "condition": [
            {
              "param": "select",
              "empty": false,
              "value": 2
            },
            {
              "param": "highlight",
              "empty": false,
              "value": 1
            }
          ],
          "value": 0
        }
      }
    }
  ]
}

Example plot:

enter image description here

Upvotes: 1

Views: 2270

Answers (2)

APB Reports
APB Reports

Reputation: 2441

How does something like this look?

enter image description here

As you can see I added a transform to concatenate the data. This way it sorts nicely. Then I extract just part of this as the axis label and also color by Assignee.

 {
"calculate": "datum.Assignee + ' | ' + datum.Project + '/' + datum.Task + ' | Start:' + utcFormat(datum.start,'%B %d, %Y') + ' #' + datum.running_number",
      "as": "project_full_descr"
}

Full spec for the vega-lite community:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
  "width": 600,
  "data": {
    "values": [
      {
        "Project": "Project1",
        "Task": "Task1",
        "Assignee": "Assignee1",
        "start": "2023-05-01",
        "end": "2023-06-01"
      },
      {
        "Project": "Project1",
        "Task": "Task1",
        "Assignee": "Assignee2",
        "start": "2023-04-01",
        "end": "2023-05-01"
      },
      {
        "Project": "Project1",
        "Task": "Task2",
        "Assignee": "Assignee3",
        "start": "2023-07-01",
        "end": "2023-07-14"
      },
      {
        "Project": "Project2",
        "Task": "Task1",
        "Assignee": "Assignee1",
        "start": "2023-07-01",
        "end": "2023-08-01"
      }
    ]
  },
  "transform": [
    {"calculate": "toDate(utcFormat(now(), '%Y-%m-%d'))", "as": "currentDate"},
    {
      "window": [{"op": "row_number", "as": "running_number"}],
      "groupby": ["Assignee"],
      "sort": [{"field": "start"}]
    },
    {
      "calculate": "datum.Assignee + ' | ' + datum.Project + '/' + datum.Task + ' | Start:' + utcFormat(datum.start,'%B %d, %Y') + ' #' + datum.running_number",
      "as": "project_full_descr"
    }
  ],
  "title": {
    "text": "Order Gantt by: Assignee > Project > Task",
    "fontSize": 14,
    "anchor": "start",
    "dy": -15,
    "color": "#706D6C"
  },
  "layer": [
    {
      "mark": {
        "type": "bar",
        "tooltip": true,
        "cornerRadiusTopRight": 4,
        "cornerRadiusBottomRight": 4
      },
      "encoding": {
        "y": {
          "field": "project_full_descr",
          "type": "nominal",
          "axis": {
            "domain": true,
            "grid": false,
            "ticks": true,
            "labels": true,
            "labelLimit": 800,
            "labelFontSize": 12,
            "labelPadding": 10,
            "labelExpr": "split(datum.label, '|')[1]"
          },
          "title": null
        },
        "x": {
          "field": "start",
          "type": "temporal",
          "timeUnit": "yearmonthdate",
          "axis": null,
          "title": null
        },
        "x2": {"field": "end"},
        "color": {
          "title": null,
          "field": "Assignee",
          "type": "nominal",
          "legend": null
        }
      }
    },
    {
      "mark": {"type": "rule", "strokeDash": [2, 2], "strokeWidth": 2},
      "encoding": {
        "x": {
          "field": "currentDate",
          "type": "temporal",
          "axis": {"format": "%d-%b"}
        }
      }
    },
    {
      "mark": {
        "type": "text",
        "align": "right",
        "dx": -24,
        "dy": 0,
        "fontSize": 9
      },
      "encoding": {
        "x": {"field": "start"},
        "y": {"field": "project_full_descr", "type": "nominal"},
        "text": {"field": "start", "type": "temporal", "format": "%d-%b"},
        "color": {"value": "white"}
      }
    },
    {
      "mark": {
        "type": "text",
        "align": "left",
        "dx": 5,
        "dy": 0,
        "fontSize": 9
      },
      "encoding": {
        "x": {"field": "end"},
        "y": {"field": "project_full_descr", "type": "nominal"},
        "text": {"field": "end", "type": "temporal", "format": "%d-%b"},
        "color": {"value": "black"}
      }
    }
  ],
  "config": {"view": {"stroke": null}}
}

Happy charting!

Adam

Upvotes: 0

davidebacci
davidebacci

Reputation: 30174

I have created a Deneb Gantt chart in Vega already - you can see here. Can you reuse the logic from that?

https://github.com/PBI-David/Deneb-Showcase

enter image description here

Upvotes: 2

Related Questions