Search code examples
jsonpowerbipowerbi-desktopvega-litedeneb

Vega-Lite (Deneb): Struggling to apply Min & Max Value on Line Chart AND text label and point at end of Line


I'm lost in the Vega-lite sauce. Having trouble understanding where I'm going wrong.

I essentially need two layers:

  • lines for all years ("color":)
  • a single red line for the current year (2023)
  • a mark to highlight: a) the highest point, b) the lowest point and c) the latest point on the same 2023 filtered line - with a text label showing the value (see image below)

enter image description here

I'm getting partial success but feel I'm coding things in the wrong order or wrong layer. The lowest temperature mark for 2023 isn't respecting the filter transform, and the text label is being effected by the "color": encoding instead of the one for the 2023 mark.

I've attached the pbix here: TemperatureDEV_pbix
(nb: this is a large dataset, I have toggled on the "override data limit" option)

Apologies in advance for the JSON mess. For the highest and lowest marks I have tried to use "window": expression. In a separate attempt I was able to get the end mark using "argmax": but I wasn't able to combine both the end mark and the highest/lowest marks.

{
  "data": {"name": "dataset"},
  "encoding": {
    "x": {
      "field": "DayOfYear",
      "type": "temporal",
      "axis": {
        "labels": false,
        "ticks": false,
        "grid": false,
        "title": "Day Of Year"
      }
    },
    "y": {
      "field": "Temp",
      "type": "quantitative",
      "scale": {"zero": false},
      "title": "Temperature"
    },
    "color": {
      "legend": null,
      "field": "Year",
      "type": "quantitative",
      "scale": {
        "reverse": false,
        "range": [
          "#0041C2",
          "#0041C250",
          "#0041C230",
          "#4169E220",
          "#4863A015"
        ]
      }
    }
  },
  
  "layer": [
    
    {
      "mark": {
        "type": "line",
        "interpolate": "monotone",
        "strokeWidth": 1.5
      }
    },
    {
      "transform": [
        {
          "calculate": "(datum.Temp)",
          "as": "2023_temp"
        },
        {
          "filter": {
            "field": "Year",
            "oneOf": ["2023"]
          }
        }
      ],
      "encoding": {
        "x": {
          "field": "DayOfYear",
          "type": "temporal",
          "axis": {
            "labels": false,
            "ticks": false,
            "grid": false,
            "title": "Day Of Year"
          }
        },
        "y": {
          "field": "2023_temp",
          "type": "quantitative",
          "scale": {"zero": false},
          "title": "Temperature"
        }
      },
      "layer": [
        {
          "mark": {
            "type": "line",
            "interpolate": "monotone",
            "stroke": "crimson",
            "strokeWidth": 3
          },
          "encoding": {
            "y": {"field": "2023_temp"}
          }
        },
        {
          "mark": {
            "type": "text",
            "align": "left",
            "dx": 5,
            "size": 15,
            "fontWeight": "bold",
            "color": "red"
          },
          "encoding": {
            "x": {
              "aggregate": "max",
              "field": "DayOfYear"
            },
            "y": {
              "aggregate": {
                "argmax": "DayOfYear"
              },
              "field": "2023_temp"
            },
            "text": {
              "aggregate": {
                "argmax": "DayOfYear"
              },
              "field": "2023_temp",
              "format": ".3~s"
            }
          }
        }
      ]
    },
    {
      "transform": [
        {
          "window": [
            {"op": "rank", "as": "jeff"}
          ],
          "sort": [
            {
              "field": "Temp",
              "order": "descending"
            }
          ]
        },
        {"filter": "datum.jeff ==1"}
      ],
      "mark": {
        "type": "point",
        "shape": "triangle-down",
        "size": 200,
        "yOffset": -8,
        "fill": "red",
        "opacity": 1
      },
      "encoding": {
        "x": {"field": "DayOfYear"},
        "y": {"field": "Temp"}
      }
    },
    {
      "transform": [
        {
          "window": [
            {
              "op": "rank",
              "as": "tempRank"
            }
          ],
          "sort": [
            {
              "field": "Temp",
              "order": "ascending"
            }
          ]
        },
        {
          "filter": "datum.tempRank == 1"
        }
      ],
      "mark": {
        "type": "point",
        "shape": "circle",
        "size": 200,
        "yOffset": -8,
        "fill": "red",
        "opacity": 1,
        "stroke": null
      },
      "encoding": {
        "x": {"field": "DayOfYear"},
        "y": {"field": "Temp"}
      }
    },
    {
      "layer": [
        {
          "transform": [
            {
              "calculate": "(datum.Temp)",
              "as": "1979_temp"
            },
            {
              "filter": "datum.Year == '1979'"
            }
          ],
          "mark": {
            "type": "line",
            "interpolate": "monotone",
            "stroke": "orange",
            "strokeWidth": 3
          },
          "encoding": {
            "y": {"field": "1979_temp"}
          }
        }
      ]
    }
  ]
}

Solution

  • How's this?

    enter image description here

    {
      "data": {"name": "dataset"},
      "encoding": {
        "x": {
          "field": "DayOfYear",
          "type": "temporal",
          "axis": {
            "labels": false,
            "ticks": false,
            "grid": false,
            "title": "Day Of Year"
          }
        },
        "y": {
          "field": "Temp",
          "type": "quantitative",
          "scale": {"zero": false},
          "title": "Temperature"
        },
        "color": {
          "legend": null,
          "field": "Year",
          "type": "quantitative",
          "scale": {
            "reverse": false,
            "range": [
              "#0041C2",
              "#0041C250",
              "#0041C230",
              "#4169E220",
              "#4863A015"
            ]
          }
        }
      },
      "layer": [
        {
          "mark": {
            "type": "line",
            "interpolate": "monotone",
            "strokeWidth": 1.5
          }
        },
        {
          "transform": [
            {
              "calculate": "(datum.Temp)",
              "as": "2023_temp"
            },
            {
              "filter": {
                "field": "Year",
                "oneOf": ["2023"]
              }
            }
          ],
          "encoding": {
            "x": {
              "field": "DayOfYear",
              "type": "temporal",
              "axis": {
                "labels": false,
                "ticks": false,
                "grid": false,
                "title": "Day Of Year"
              }
            },
            "y": {
              "field": "2023_temp",
              "type": "quantitative",
              "scale": {"zero": false},
              "title": "Temperature"
            }
          },
          "mark": {
            "type": "line",
            "interpolate": "monotone",
            "stroke": "crimson",
            "strokeWidth": 3
          }
        },
        {
          "transform": [
            {
              "filter": {
                "field": "Year",
                "oneOf": ["2023"]
              }
            },
            {
              "window": [
                {"op": "rank", "as": "high"}
              ],
              "sort": [
                {
                  "field": "Temp",
                  "order": "descending"
                }
              ]
            },
            {
              "window": [
                {"op": "rank", "as": "low"}
              ],
              "sort": [
                {
                  "field": "Temp",
                  "order": "ascending"
                }
              ]
            },
            {
              "window": [
                {"op": "rank", "as": "last"}
              ],
              "sort": [
                {
                  "field": "Year",
                  "order": "ascending"
                },
                {
                  "field": "DayOfYear",
                  "order": "descending"
                }
              ]
            }
          ],
          "layer": [
                  {
              "mark": {
                "type": "text",
                
                "fontSize": {"expr": "datum.high ==1 || datum.low==1|| datum.last==1?10:0"
                },
                "dy":-25,
                "fill": "black",
                "yOffset": 0,
                "opacity": 1
              },
              "encoding": {
          "text": {"field": "Temp", "type": "quantitative"}
        }
            },
            {
              "mark": {
                "type": "point",
                "shape": "circle",
                "size": {
                  "expr": "datum.high ==1 || datum.low==1|| datum.last==1?150:0"
                },
                "yOffset": 0,
                "strokeWidth": 0.8,
                "stroke": "black",
                "fill": "red",
                "opacity": 1
              }
            }
          ]
        },
        {
          "layer": [
            {
              "transform": [
                {
                  "calculate": "(datum.Temp)",
                  "as": "1979_temp"
                },
                {
                  "filter": "datum.Year == '1979'"
                }
              ],
              "mark": {
                "type": "line",
                "interpolate": "monotone",
                "stroke": "orange",
                "strokeWidth": 3
              },
              "encoding": {
                "y": {"field": "1979_temp"}
              }
            }
          ]
        }
      ]
    }