Post

S1E7 - Expressions & Conditional Formatting (pt2)

Marks are the core building blocks for chart development. In this article we will take a little dip into the different mark types available🕊️🧙🏼‍♂️✨

S1E7 - Expressions & Conditional Formatting (pt2)

Styling Marks

💌 PBIX file available at the end of the article 1 Enjoy!


 Picking Up Where We Left Off

If you haven’t been scared off by Part 1 😅then I promise in Part 2, you will be cruising…easy, breezy. Power Up! 🌈

formatting


1. Conditional Formatting via the Encoding channel

Previously, we achieved conditional formatting using a combination of Calculate Transforms and Expressions. In this section, we will apply the same logic, using the Condition property in the mark’s Encoding channel.

As with all learning journeys, there is always an element of pain, and that pain my friends is documentation. It hurts, but it most importantly it can definitely help. How to explain the Condition property? What does Vega-Lite say?

formatting formatting

In other words, the Conditional property is also flexible, and there are several ways in which you can achieve conditional formatting using this method. Let’s take a look at those using the PBIX file at the end of the article.

Let’s try some malpractice first, so we can better understand the VL behaviours. Then we’ll end with a refined example to finish. Below is the skeleton structure

Mark Expression Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Expression Method
{                                    //<-- open mark property
      "mark": {
        "type": "bar",
        "width": {"band": 0.65},
        "xOffset": 0,

///*----------------------------------------------------------------
//Format the mark's colour property with an expression
//----------------------------------------------------------------*/
        "color": {
          "expr": "datum._isYTD == 1 ? _barColourACT : _barColourFRC"
        },
//----------------------------------------------------------------
        "stroke": "black",
        "strokeWidth": 1
      }
    }                              //<-- close mark property
//* 
//• "?" = if the logical test true then return subsequent value...
//• ":" = otherwise return subsequent value
//• eg: "logical test ? return value : return another Value"

// plain english: if the value in the [isYTD] field equals 1
// then return _barColourACT otherwise return _barColourFRC */


Encoding Condition Method 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Encoding Condition Method
  "encoding": {    //<-- open encoding properties
    "x": {...},
    "y": {...},
    "color": {        //<<-- open colour properties
      "condition": [          //<-- open conditions
        {
          "test": {
            "field": "_isYTD",
            "equal": 1
          },
          "value": {
            "expr": "_barColourACT"
          }
        }
      ],                     //<-- close condition
      "value": {"expr": "_barColourFRC"}  //<-- value to return if condition not met
    }                //<<-- close colour properties
  }              //<-- close encoding properties
//* 
//• "condition" = the property which will contain our logical test...
//• "test" = the elements that comprise our test
//• "field" = the field we are testing against
//• "equal": this is the predicate (equal, lessthan, morethan etc)
//• "value": the result to return if the test is met/not met

//plain english: if the value in the [isYTD] field equals 1
//then return _barColourACT otherwise return _barColourFRC */


Encoding Condition Method 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Encoding Condition Method
  "encoding": {    //<-- open encoding properties
    "x": {...},
    "y": {...},
    "color": {        //<<-- open colour properties
      "condition": [      //<-- open conditions
        {
          "test": "datum._isYTD == 1",
          "value": {
            "expr": "_barColourACT"
          }
        }
      ],                  //<-- close condition
      "value": {"expr": "_barColourFRC"}  //<-- value to return if condition not met
    }                 //<<-- close colour properties
  }               //<-- close encoding properties
/* 
//• "condition" = the property which will contain our logical test...
//• "test" = the logical test (similar to the mark expression method)
//• "value": the result to return if the test is met/not met

// plain english: if the value in the [isYTD] field equals 1
// then return _barColourACT otherwise return _barColourFRC */


Now to test it in practice. Let’s simply add the color condition at the end of our encoding channel in the Spec :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
{
  "data": {"name": "dataset"},
  "params": [],
  "transform": [
    {
      "calculate": "datetime( now()) ",
      "as": "_Today"
    },
    {
      "calculate": "datum['Date'] <= datum._Today ? 1 : 0 ",
      "as": "_isYTD"
    }
  ],
  "layer": [
    {
      "mark": {
        "type": "bar",
        "width": {"band": 0.65},
        "xOffset": 0,
//*----------------------------------------------------------------
//Format the mark's colour property with an expression
//NOTE: This is superfluous to requirements now, but we'll test
//which propery has a higher precedence
//----------------------------------------------------------------*/
        "color": {
          "expr": "datum._isYTD == 1 ? _barColourACT : _barColourFRC"
        },
        "stroke": "black",
        "strokeWidth": 1
      }
    }
  ],
  "encoding": {
    "x": {
      "field": "Date",
      "type": "ordinal",
      "timeUnit": "yearmonth",
      "axis": {
        "title": null,
        "format": "%b-%y"
      }
    },
    "y": {
      "field": "AC",
      "type": "quantitative",
      "axis": {
        "format": "£0,,.#M",
        "formatType": "pbiFormat"
      }
    },
///*----------------------------------------------------------------
//Format the encoding colour property with a condition (method 2)
//----------------------------------------------------------------*/
    "color": {
      "condition": [
        {
          "test": "datum._isYTD == 1",
          "value": {
            "expr": "_barColourACT"
          }
        }
      ],
      "value": {"expr": "_barColourFRC"}
    }
//---------------------------------------------------------------
  }
}

formatting


Fantastic! Next, out of curiosity, I want to see which method (Mark Expression or Encoding Condition) has a higher precedence: i.e. which one has a higher priority?

To test this, let’s just change the colour of the Actual bar in the encoding condition.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
{
  "data": {"name": "dataset"},
  "params": [],
  "transform": [...],
  "layer": [
    {
      "mark": {
        "type": "bar",
        "width": {"band": 0.65},
        "xOffset": 0,
//*----------------------------------------------------------------
//Format the mark's colour property with an expression
//NOTE: This is superfluous to requirements now, but we'll test
//which propery has a higher precedence
//----------------------------------------------------------------*/
        "color": {
          "expr": "datum._isYTD == 1 ? _barColourACT : _barColourFRC"
        },
        "stroke": "black",
        "strokeWidth": 1
      }
    }
  ],
  "encoding": {,
    "x": {...},
    "y": {...},
//*----------------------------------------------------------------
//Format the encoding colour property with a condition (method 2)
//----------------------------------------------------------------*/
    "color": {
      "condition": [
        {
          "test": "datum._isYTD == 1",
          "value": {
            "expr": "'yellow'" //<--- make sure to use single quotes 'yellow'
          }
        }
      ],
      "value": {"expr": "_barColourFRC"}
    }
//---------------------------------------------------------------
  }
}


And what do we find…

formatting

Exactly… the encoding color properties have a higher precedence than the mark color properties, it effectively overwrites any formatting we’ve applied to the mark color property. So, we can remove the mark color properties from our Vega-Lite code. See below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
{
  "data": {"name": "dataset"},
  "params": [],
  "transform": [...],
  "layer": [
    {
      "mark": {
        "type": "bar",
        "width": {"band": 0.65},
        "xOffset": 0,
//        "color": {...}     //<-- we've deleted this section
    }
  ],
  "encoding": {...},
    "y": {...},
///*----------------------------------------------------------------
// Format the encoding colour property with a condition (method 2)
//----------------------------------------------------------------*/
    "color": {
      "condition": [
        {
          "test": "datum._isYTD == 1",
          "value": {
            "expr": "'yellow'" //<--- make sure to use single quotes 'yellow'
          }
        }
      ],
      "value": {"expr": "_barColourFRC"}
    }
//---------------------------------------------------------------
  }
}


Now run that neater code and….behold!

Congratulations, you now have COMPLETE control over your Deneb chart formatting!

formatting


. . .

  En Fin, Serafin

Thank you for staying to the end of the article… I hope you find it useful 😊. See you soon, and remember… #StayQueryous!🧙‍♂️🪄

  PBIX 💾

🔗 Repo: Github Repo PBIX Treasure Trove

"Buy Me A Coffee"

. . .


Footnotes

This post is licensed under CC BY 4.0 by the author.