vega-lite

Power BI: Deneb/Vega-lite Sorting Specification


I am attempting to sort the Y axis based on a numeric field already available in my dataset, however, the sorting does not seem to work. See full specifications below. The sorting section is outside of the layer and in the y encoding channel.

{
    "data": {
        "name": "dataset"
    },
    "transform": [
        {
            "calculate": "length(data('dataset'))",
            "as": "RowCount"
        },
        {
            "calculate": "datum.JTOSatUnSatCount / datum.JTOSatUnSatTotalCount",
            "as": "PercentageTextLabel"
        },
        {
            "calculate": "if(datum.GradeCategory === 'Repeat Unsat', '#F3667F', if(datum.GradeCategory === 'Unsat', '#C00000', if(datum.GradeCategory === 'Repeat Sat', '#638F00', if(datum.GradeCategory === 'Sat', '#00C800', '#000000'))))",
            "as": "GradeCategoryColorCode"
        },
        {
            "calculate": "if(datum.PercentageTextLabel >= 0.05, 1, 0)",
            "as": "PercentageTextLabelOpacity"
        },
        {
            "calculate": "'Total: ' + datum.JTOSatUnSatTotalCount",
            "as": "TotalTextLabel"
        }
    ],
    "height": {
        "step": 50
    },
    "padding": {
        "top": 0,
        "bottom": 0,
        "left": 0,
        "right": 80
    },
    "encoding": {
        "x": {
            "axis": {
                "labels": false,
                "title": "Distribution",
                "grid": false
            }
        },
        **"y": {
            "field": "Tasks",
            "type": "nominal",
            "sort": {
                "field": "TaskTableSortLogic",
                "order": "ascending"
            }**,
            "axis": {
                "labelPadding": 20,
                "labelLimit": 600,
                "title": "Tasks",
                "titleX": -600,
                "titleY": {
                    "expr": "pbiContainer.scrollTop + pbiContainer.height / 2"
                }
            }
        }
    },
    "layer": [
        {
            "params": [
                {
                    "name": "hoverEffect",
                    "select": {
                        "type": "point",
                        "on": "pointerover"
                    }
                }
            ],
            "name": "Horizontal Stacked Bar Chart (bar mark)",
            "mark": {
                "type": "bar",
                "tooltip": true,
                "cursor": "pointer",
                "height": {
                    "expr": "if(datum.RowCount > 10, 28, 45)"
                },
                "stroke": {
                    "expr": "datum.GradeCategoryColorCode"
                }
            },
            "encoding": {
                "x": {
                    "field": "JTOSatUnSatCount",
                    "type": "quantitative",
                    "stack": "normalize"
                },
                "order": {
                    "field": "SatUnSatRepeatSort",
                    "sort": "ascending",
                    "type": "quantitative"
                },
                "color": {
                    "field": "GradeCategory",
                    "type": "nominal",
                    "legend": null,
                    "scale": {
                        "domain": [
                            "Repeat Unsat",
                            "Unsat",
                            "Repeat Sat",
                            "Sat"
                        ],
                        "range": [
                            "#F3667F",
                            "#C00000",
                            "#638F00",
                            "#00C800"
                        ]
                    }
                },
                "opacity": {
                    "condition": {
                        "param": "hoverEffect",
                        "empty": false,
                        "value": 0.7
                    },
                    "value": 1
                },
                "strokeWidth": {
                    "condition": {
                        "param": "hoverEffect",
                        "empty": false,
                        "value": 2
                    },
                    "value": 0
                }
            }
        },
        {
            "params": [
                {
                    "name": "TextHoverEffect",
                    "select": {
                        "type": "point",
                        "on": "pointerover"
                    }
                }
            ],
            "name": "Percentage Label (text mark)",
            "mark": {
                "type": "text",
                "baseline": "middle",
                "color": "white"
            },
            "encoding": {
                "text": {
                    "field": "PercentageTextLabel",
                    "type": "quantitative",
                    "format": ".0%"
                },
                "order": {
                    "field": "SatUnSatRepeatSort",
                    "type": "quantitative",
                    "sort": "ascending"
                },
                "x": {
                    "field": "PercentageTextLabel",
                    "type": "quantitative",
                    "stack": "normalize",
                    "bandPosition": 0.5
                },
                "opacity": {
                    "field": "PercentageTextLabelOpacity",
                    "type": "quantitative",
                    "scale": null
                },
                "size": {
                    "condition": {
                        "param": "TextHoverEffect",
                        "empty": false,
                        "value": 13
                    },
                    "value": 11
                }
            }
        },
        {
            "name": "Total Label (text mark)",
            "transform": [
                {
                    "aggregate": [
                        {
                            "op": "max",
                            "field": "TotalTextLabel",
                            "as": "UniqueTotalTextLabel"
                        }
                    ],
                    "groupby": [
                        "Tasks"
                    ]
                }
            ],
            "mark": {
                "type": "text",
                "baseline": "middle",
                "color": "black",
                "dx": 325,
                "fontWeight": "bold"
            },
            "encoding": {
                "text": {
                    "field": "UniqueTotalTextLabel",
                    "type": "nominal"
                }
            }
        }
    ]
}

I have tried moving the sorting into each individual layer as well, and that did not work either, I think the sorting should be shared by all layers.

Screenshot (https://i.sstatic.net/7AzxxIpe.png)


Solution

  • I have a little hack I do quite often. Combine a sort with the description. Then it it sorts correctly. Then I just strip away the sort value from the label.

    Create a new transform like this:

    {
          "calculate": "format(datum.TaskTableSortLogic + 10000, '.0f') + '|' + datum.Tasks ",
          "as": "Tasks_descr"
        }
    

    Then adjust gloabal Y like this. Notice the asix.labelExpr

    "y": {
          "field": "Tasks_descr",
          "type": "nominal",
          "axis": {
            "labelPadding": 20,
            "labelLimit": 600,
            "title": "Tasks",
            "titleX": -600,
            "labelExpr": "split(datum.label, '|')[1]",
            "titleY": {
              "expr": "pbiContainer.scrollTop + pbiContainer.height / 2"
            }
          }
        }
    

    Also fix your tool-tips and manually add them.