Deneb/Vega-Lite can be used to create visuals with high information density (HID). The example presented herein displays month, year-to-month, and full year sales information complete with previous year variances.
A HID visual was presented by Jürgen Faisst/IBCS at about the 30-minute mark of the recent YouTube video with Reid Havens and it piqued my interest to see if I could create such a visual in Deneb/Vega-Lite.
All bars use the same x-axis scale so variance size insights can be drawn. Amount data labels are show in normal font and variance data labels are shown in italics.
A known issue is that data labels can overlap and make insights more difficult. To allow for increased clarity, screen widgets/display parameters were added to turn off the amount data labels, the variance data labels, or to offset the variance data.
The timing of this first-cut at trying such an HID visual afforded me the opportunity to review it with the beta release of Deneb 1.7.
All functionality worked as designed, no issues were found with Deneb 1.7, and I gained experience with many of the (very) welcome enhancements; I look forward to the full release of Deneb 1.7 once the certification process is complete.
This example illustrates a number of Deneb/Vega-Lite features, including:
0 - General:
- a “title” block for the composite visual
- array used for subtitle to display on multiple lines
- a shared “transform” block to extend the dataset in-visual with:
- “joinaggregate”, “window/rank”, and “calculate” transforms to compose a rank-country name concetenated field so that Vega-Lite could use its’ default sorting
- 9x “calculate” transforms to create the month, year-to-month, and year variance values and ranged-bar x positions
- a shared “params” block to enhance the visual with:
- a checkbox to toggle the display of amount data labels
- a checkbox to toggle the display of variance data labels
- a checkbox to toggle the X position (offset) of variance data (ranged bars, data labels, titles)
- (multiple non-breaking space characters were used in the display names to roughly vertically align the checkboxes)
- a “vconcat” block to vertically concatenate the legend and layered bar charts
1 - Legend:
- a nested “title” block positioned to make the composite visual appear as a single visual
- a hard-coded dataset with 9 entries to set the legend values
- an “arc” mark with zero radius to enable and display the legend while hiding the visual
- a “scale/domain/range” encoding to set the legend colours/patterns
- a custom “legend” encoding with a horizontal layout and 3 columns
2 - Bar Charts:
- a shared “encoding” block to set:
- the X type, axis, and scale
- the Y field, type, and axis
- the tooltip for all bars
- a “layer” block with:
- 3x “bar” marks for year, year-to-month, and month amounts (normal; X only)
- year bars use “pbiPatternSVG” Deneb fill formatting
- 3x “text” marks for month, year-to-month, and year amount labels (normal font)
- vertically offset
- “color” (black/transparent) set according to amount “show” checkbox
- 3x “bar” marks for year, year-to-month, and month variances (ranged; X and X2)
- year bars use “pbiPatternSVG” Deneb fill formatting
- “color” (green/red) set according to variance value (positive/negative)
- 3x “text” marks for month, year-to-month, and year variance labels (italic font)
- “align” and “xOffset” set according to variance value
- “color” (black/transparent) set according to variance “show” checkbox
- 3x “text” marks for variance titles
- nested “filter” transform to render only once per type (month, year-to-month, year)
- 3x “bar” marks for year, year-to-month, and month amounts (normal; X only)
Deneb/Vega-Lite JSON Code:
{
"title": {
"anchor": "start",
"align": "left",
"offset": 20,
"text": "Power BI High Information Density Visuals using Deneb",
"font": "Segoe UI",
"fontSize": 32,
"fontWeight": "bold",
"fontStyle": "normal",
"subtitle": [
"Country Sales Performance (Current Year vs. Previous Year [Month vs. YTM vs. Year])",
"(Base data source: Microsoft Contoso Sample Database [countries filtered, dates adjusted +14 years, amounts adjusted for visualization purposes])"
],
"subtitleFont": "Segoe UI",
"subtitleFontSize": 16,
"subtitleFontWeight": "normal",
"subtitleFontStyle": "italic"
},
"data": {"name": "dataset"},
"transform": [
{
"joinaggregate": [
{
"op": "sum",
"field": "CY Year Sales",
"as": "_year_sales_for_sorting"
}
],
"groupby": ["Year", "Country"]
},
{
"window": [
{
"op": "dense_rank",
"as": "_country_rank"
}
],
"sort": [
{
"field": "_year_sales_for_sorting",
"order": "descending"
}
]
},
// CONCATENATE RANK AND COUNTRY NAME TO PERMIT DEFAULT VEGA-LITE "ALPHABETIC" SORTING
{
"calculate": "pad( datum['_country_rank'], 2, '0', 'left' ) + '-' + datum['Country']",
"as": "_country_rank_country_name_label"
},
// MONTH VARIANCES
{
"calculate": "monthAbbrevFormat( datum['Month'] - 1 )",
"as": "_month_abbreviation"
},
{
"calculate": "'Δ' + datum['_month_abbreviation']",
"as": "_delta_month_title"
},
{
"calculate": "datum['CY Month Sales'] - datum['PY Month Sales']",
"as": "_month_variance"
},
{
"calculate": "_offset_variances == true ? 100000000 : 60000000",
"as": "_month_variance_base_x"
},
{
"calculate": "datum['_month_variance'] + datum['_month_variance_base_x']",
"as": "_month_variance_x"
},
// YEAR-TO-MONTH VARIANCES
{
"calculate": "monthAbbrevFormat( 0 ) + '-' + monthAbbrevFormat( datum['Month'] - 2 )",
"as": "_ytd_abbreviation"
},
{
"calculate": "'Δ' + datum['_ytd_abbreviation']",
"as": "_delta_ytd_title"
},
{
"calculate": "datum['CY YTD Sales'] - datum['PY YTD Sales']",
"as": "_ytd_variance"
},
{
"calculate": "_offset_variances == true ? 120000000 : 80000000",
"as": "_ytd_variance_base_x"
},
{
"calculate": "datum['_ytd_variance'] + datum['_ytd_variance_base_x']",
"as": "_ytd_variance_x"
},
// YEAR VARIANCES
{
"calculate": "'Δ' + datum['Year']",
"as": "_delta_year_title"
},
{
"calculate": "datum['CY Year Sales'] - datum['PY Year Sales']",
"as": "_year_variance"
},
{
"calculate": "_offset_variances == true ? 140000000 : 100000000",
"as": "_year_variance_base_x"
},
{
"calculate": "datum['_year_variance'] + datum['_year_variance_base_x']",
"as": "_year_variance_x"
}
],
"params": [
// USE NON-BREAKING SPACE CHARACTER TO ROUGHLY ALIGN CHECKBOXES
{
"name": "_show_sales_data_labels",
"value": true,
"bind": {
"input": "checkbox",
"name": "Amount Data Labels: "
}
},
{
"name": "_show_variance_data_labels",
"value": true,
"bind": {
"input": "checkbox",
"name": "Variance Data Labels: "
}
},
{
"name": "_offset_variances",
"value": false,
"bind": {
"input": "checkbox",
"name": "Offset Variances: "
}
}
],
"vconcat": [
{
"name": "LEGEND",
"title": {
"anchor": "start",
"align": "left",
"offset": 0,
"dx": -120,
"dy": -4,
"text": "Sales in kUSD",
"font": "Segoe UI",
"fontSize": 20,
"fontWeight": "bold",
"fontStyle": "normal"
},
"width": 1300,
"height": 50,
"data": {
"values": [
{
"legend_id": 1,
"legend_size": 1,
"legend_label": "Month"
},
{
"legend_id": 2,
"legend_size": 1,
"legend_label": "Year to Month"
},
{
"legend_id": 3,
"legend_size": 1,
"legend_label": "Year"
},
{
"legend_id": 4,
"legend_size": 1,
"legend_label": "+ Month Variance"
},
{
"legend_id": 5,
"legend_size": 1,
"legend_label": "+ Year to Month Variance"
},
{
"legend_id": 6,
"legend_size": 1,
"legend_label": "+ Year Variance"
},
{
"legend_id": 7,
"legend_size": 1,
"legend_label": "- Month Variance"
},
{
"legend_id": 8,
"legend_size": 1,
"legend_label": "- Year to Month Variance"
},
{
"legend_id": 9,
"legend_size": 1,
"legend_label": "- Year Variance"
}
]
},
"mark": {
"type": "arc",
"radius": 0
},
"encoding": {
"theta": {
"field": "legend_size",
"type": "quantitative"
},
"color": {
"field": "legend_label",
"type": "nominal",
"scale": {
"domain": [
"Month",
"Year to Month",
"Year",
"+ Month Variance",
"+ Year to Month Variance",
"+ Year Variance",
"- Month Variance",
"- Year to Month Variance",
"- Year Variance"
],
"range": [
"#969696",
"#C9C9C9",
{
"expr": "pbiPatternSVG('diagonal-stripe-1', '#969696', '#FFFFFF')"
},
"#0D5420",
"#1AAB40",
{
"expr": "pbiPatternSVG('diagonal-stripe-1', '#0D5420', '#FFFFFF')"
},
"#821C27",
"#D64554",
{
"expr": "pbiPatternSVG('diagonal-stripe-1', '#821C27', '#FFFFFF')"
}
]
},
"legend": {
"direction": "horizontal",
"title": null,
"orient": "none",
"legendX": -120,
"legendY": 0,
"labelColor": "black",
"labelFont": "Segoe UI",
"labelFontSize": 12,
"labelFontStyle": "italic",
"symbolSize": 400,
"symbolType": "circle",
"symbolStrokeColor": "#C9C9C9",
// SET THE NUMBER OF COLUMNS TO ENABLE A GRID-LIKE LAYOUT
"columns": 3
}
}
}
},
{
"name": "AMOUNTS",
"width": 1740,
"height": 500,
"encoding": {
"x": {
"type": "quantitative",
"axis": {
"tickCount": 15,
"formatType": "pbiFormat",
"format": "$#0,,. M",
"domain": false,
"ticks": false,
"labels": false,
"title": null
},
"scale": {
"domain": [0, 150000000]
}
},
"y": {
"field": "_country_rank_country_name_label",
"type": "nominal",
"axis": {
"title": null,
"labelExpr": "slice( datum.value, 3, 100 )",
"labelAlign": "right",
"labelPadding": 4,
"labelFont": "Segoe UI",
"labelFontSize": 16,
"labelLimit": 500,
"domain": false,
"ticks": false
}
},
"tooltip": [
{
"field": "Country",
"type": "nominal"
},
{
"field": "CY Month Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY Month Sales (kUSD)"
},
{
"field": "PY Month Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "PY Month Sales (kUSD)"
},
{
"field": "_month_variance",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY/PY Month Variance (kUSD)"
},
{
"field": "CY YTD Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY YTM Sales (kUSD)"
},
{
"field": "PY YTD Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "PY YTM Sales (kUSD)"
},
{
"field": "_ytd_variance",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY/PY YTM Variance (kUSD)"
},
{
"field": "CY Year Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY Year Sales (kUSD)"
},
{
"field": "PY Year Sales",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "PY Year Sales (kUSD)"
},
{
"field": "_year_variance",
"type": "quantitative",
"formatType": "pbiFormat",
"format": "#,#,",
"title": "CY/PY Year Variance (kUSD)"
}
]
},
"layer": [
{
"name": "ACTUAL_SALES",
"layer": [
{
"name": "YEAR_SALES",
"mark": {
"type": "bar",
"fill": {
"expr": "pbiPatternSVG('diagonal-stripe-1', '#969696', '#FFFFFF')"
},
"stroke": "#969696",
"tooltip": true
},
"encoding": {
"x": {
"field": "CY Year Sales"
},
"y": {
"field": "_country_rank_country_name_label"
}
}
},
{
"name": "YTD_SALES",
"mark": {
"type": "bar",
"color": "#C9C9C9",
"stroke": "#C9C9C9",
"tooltip": {
"content": "data"
}
},
"encoding": {
"x": {
"field": "CY YTD Sales"
},
"y": {
"field": "_country_rank_country_name_label"
}
}
},
{
"name": "MONTH_SALES",
"mark": {
"type": "bar",
"color": "#969696",
"stroke": "#969696",
"tooltip": {
"content": "data"
}
},
"encoding": {
"x": {
"field": "CY Month Sales"
},
"y": {
"field": "_country_rank_country_name_label"
}
}
}
]
},
{
"name": "ACTUAL_LABELS",
"layer": [
{
"name": "MONTH_LABELS",
"mark": {
"type": "text",
"align": "left",
"xOffset": 4,
"yOffset": -12,
"font": "Segoe UI",
"fontSize": 14,
"baseline": "middle",
"color": {
"expr": "_show_sales_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "CY Month Sales",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "CY Month Sales"
}
}
},
{
"name": "YTD_LABELS",
"mark": {
"type": "text",
"align": "left",
"xOffset": 4,
"yOffset": 0,
"font": "Segoe UI",
"fontSize": 14,
"baseline": "middle",
"color": {
"expr": "_show_sales_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "CY YTD Sales",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "CY YTD Sales"
}
}
},
{
"name": "YEAR_LABELS",
"mark": {
"type": "text",
"align": "left",
"xOffset": 4,
"yOffset": 12,
"font": "Segoe UI",
"fontSize": 14,
"baseline": "middle",
"color": {
"expr": "_show_sales_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "CY Year Sales",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "CY Year Sales"
}
}
}
]
},
{
"name": "VARIANCE_AMOUNTS",
"layer": [
{
"name": "VARIANCE_YEAR_AMOUNT",
"layer": [
{
"name": "DELTA_YEAR_VARIANCE",
"mark": {
"type": "bar" ,
"fill": {
"expr": "datum['_year_variance'] >= 0 ? pbiPatternSVG('diagonal-stripe-1', '#0D5420', '#FFFFFF') : pbiPatternSVG('diagonal-stripe-1', '#821C27', '#FFFFFF')"
},
"stroke": {
"expr": "datum['_year_variance'] >= 0 ? '#0D5420' : '#821C27'"
}
},
"encoding": {
"x": {
"field": "_year_variance_x"
},
"x2": {
"field": "_year_variance_base_x"
}
}
},
{
"name": "DELTA_YEAR_TITLE",
"transform": [
{
"filter": "datum['_country_rank'] == 1"
}
],
"mark": {
"type": "text",
"align": "center",
"fontSize": 18
},
"encoding": {
"x": {
"field": "_year_variance_base_x"
},
"y": {"value": -10},
"text": {
"field": "_delta_year_title"
}
}
}
]
},
{
"name": "VARIANCE_YTD_AMOUNT",
"layer": [
{
"name": "DELTA_YTD_VARIANCE",
"mark": {
"type": "bar",
"color": {
"expr": "datum['_ytd_variance'] >= 0 ? '#1AAB40' : '#D64554'"
}
},
"encoding": {
"x": {
"field": "_ytd_variance_x"
},
"x2": {
"field": "_ytd_variance_base_x"
}
}
},
{
"name": "DELTA_YTD_TITLE",
"transform": [
{
"filter": "datum['_country_rank'] == 1"
}
],
"mark": {
"type": "text",
"align": "center",
"fontSize": 18
},
"encoding": {
"x": {
"field": "_ytd_variance_base_x"
},
"y": {"value": -10},
"text": {
"field": "_delta_ytd_title"
}
}
}
]
},
{
"name": "VARIANCE_MONTH_AMOUNT",
"layer": [
{
"name": "DELTA_MONTH_VARIANCE",
"mark": {
"type": "bar",
"color": {
"expr": "datum['_month_variance'] >= 0 ? '#0D5420' : '#821C27'"
}
},
"encoding": {
"x": {
"field": "_month_variance_x"
},
"x2": {
"field": "_month_variance_base_x"
}
}
},
{
"name": "DELTA_MONTH_TITLE",
"transform": [
{
"filter": "datum['_country_rank'] == 1"
}
],
"mark": {
"type": "text",
"align": "center",
"fontSize": 18
},
"encoding": {
"x": {
"field": "_month_variance_base_x"
},
"y": {"value": -10},
"text": {
"field": "_delta_month_title"
}
}
}
]
}
]
},
{
"name": "VARIANCE_LABELS",
"layer": [
{
"name": "VARIANCE_MONTH_LABELS",
"mark": {
"type": "text",
"align": {
"expr": "datum['_month_variance'] < 0 ? 'right' : 'left'"
},
"xOffset": {
"expr": "datum['_month_variance'] < 0 ? -4 : 4"
},
"font": "Segoe UI",
"fontSize": 14,
"fontStyle": "italic",
"baseline": "middle",
"color": {
"expr": "_show_variance_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "_month_variance",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "_month_variance_x"
}
}
},
{
"name": "VARIANCE_YTD_LABELS",
"mark": {
"type": "text",
"align": {
"expr": "datum['_ytd_variance'] < 0 ? 'right' : 'left'"
},
"xOffset": {
"expr": "datum['_ytd_variance'] < 0 ? -4 : 4"
},
"font": "Segoe UI",
"fontSize": 14,
"fontStyle": "italic",
"baseline": "middle",
"color": {
"expr": "_show_variance_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "_ytd_variance",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "_ytd_variance_x"
}
}
},
{
"name": "VARIANCE_YEAR_LABELS",
"mark": {
"type": "text",
"align": {
"expr": "datum['_year_variance'] < 0 ? 'right' : 'left'"
},
"xOffset": {
"expr": "datum['_year_variance'] < 0 ? -4 : 4"
},
"font": "Segoe UI",
"fontSize": 14,
"fontStyle": "italic",
"baseline": "middle",
"color": {
"expr": "_show_variance_data_labels == true ? 'black' : 'transparent'"
}
},
"encoding": {
"text": {
"field": "_year_variance",
"formatType": "pbiFormat",
"format": "#,#,"
},
"x": {
"field": "_year_variance_x"
}
}
}
]
}
]
}
]
}
Sample Dataset:
The dataset used originated from the Microsoft Contoso sample database; the data were filtered, dates adjusted forward by 14 years, and amounts adjusted in Excel for visualization purposes.
The intent of this example is not to provide a finished visual, but rather to explore the use of the Deneb custom visual and the Vega-Lite language within Power BI and to serve as a starting point for further development.
Also included is the development PBIX using data
This example is provided as-is for information purposes only, and its use is solely at the discretion of the end user; no responsibility is assumed by the author.
Greg
Deneb Example - High Information Density - V10.pbix (3.9 MB)