Vega, How to draw 2 Threshold/Limit lines

Hi,
I currently have this vega visualiation (example):

{
  "$schema": "https://vega.github.io/schema/vega/v4.3.json",
  "description": "A basic line chart example.",
  "padding": 5,
  
data: [
{
  name:"table"
    url: {
      %context%: true
      %timefield%: @timestamp
      index: archives
      body: {
        "size": 1000,
        "_source": ["@timestamp", "value"],
        "sort" : { "@timestamp" : "desc" }
      },
    }
    format: { property: "hits.hits" },
    transform: [
        { type: "formula",as: "time", expr: "datetime(datum._source['@timestamp'])"}
      ]
  }
],
  "scales": [
    {
      "name": "timex",
      "type": "time",
      "range": "width",
      "domain": {"data": "table", "field": "time"}
    },
    {
      "name": "NameArchives",
      "type": "linear",
      "range": "height",
      "nice": true,
      "zero": true,
      "domain": {"data": "table", "field": "_source.value"}
    }
  ],
  "axes": [
    {
    "orient": "bottom", 
    "scale": "timex", 
    "format": "%Y",
    "grid":"true"
    },
    {
    "orient": "left", 
    "scale": "NameArchives",
    "grid":"true"}
  ],
  "marks": [
  {
      "type": "line",
      "from": {"data": "table"},
      "encode": {
        "enter": {
          "x": {"scale": "timex", "field": "time"},
          "y": {"scale": "NameArchives", "field": "_source.value"}
          "stroke": {"value": "#FF00FF"},
          "strokeWidth": "1"
        }
      }
    }
  ]
}

What I need is to draw 1 min. and 1 max. line like this: Paint is great

I tried it with some examples


but that didn't work out too well. I was never able to draw 2 lines because the second line I added overruled the first line.

Code
{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
  "description": "The PM2.5 value of Beijing observed 15 days, highlighting the days when PM2.5 level is hazardous to human health. Data source https://chartaccent.github.io/chartaccent.html",
    "layer": [{
      "data": {
        "values": [
          {"Day": 1, "Value": 54.8},
          {"Day": 2, "Value": 112.1},
          {"Day": 3, "Value": 63.6},
          {"Day": 4, "Value": 37.6},
          {"Day": 5, "Value": 79.7},
          {"Day": 6, "Value": 137.9},
          {"Day": 7, "Value": 120.1},
          {"Day": 8, "Value": 103.3},
          {"Day": 9, "Value": 394.8},
          {"Day": 10, "Value": 199.5},
          {"Day": 11, "Value": 72.3},
          {"Day": 12, "Value": 51.1},
          {"Day": 13, "Value": 112.0},
          {"Day": 14, "Value": 174.5},
          {"Day": 15, "Value": 130.5},
          {"limithigh":20, "limitlow":10}
        ]
      },
      "layer": [{
        "mark": "line",
        "encoding": {
          "x": {"field": "Day", "type": "ordinal", "axis": {"labelAngle": 0}},
          "y": {"field": "Value", "type": "quantitative"}
        }
      }, 
      {
        "mark": "bar",
        "transform": [
          {"filter": "datum.Value >= 2"},
          {"calculate": "200", "as": "baseline"}
        ],
        "encoding": {
          "x": {"field": "Day", "type": "ordinal"},
          "y": {"field": "baseline", "type": "quantitative", "title": "PM2.5 Value"},
          "y2": {"field": "Value"},
          "color": {"value": "#e45755"}
        }
      },
      {
        "mark": "line",
        "transform": [
          {"filter": "datum.Value >= 200 <= 20"},
          {"calculate": "100", "as": "baseline"}
        ],
        "encoding": {
          "x": {"field": "Day", "type": "ordinal"},
          "y": {"field": "baseline", "type": "quantitative", "title": "PM2.5 Value"},
          "y2": {"field": "Value"},
          "color": {"value": "#cc57c5"}
        }
      }
    ]}, {
      "data": {
         "values": [{}]
      },
      "encoding": {
        "y": {"datum": 100}
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "hazardous"
        }
      }]
    }
  ]
}

Questions:

  1. How to draw multiple threshold lines
  2. How to mark those values which are over or under those lines.

Thanks,
defalt

UPDATE
I manged to create this visualization with some sample data:

Code
{
  "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
  "description": "The PM2.5 value of Beijing observed 15 days, highlighting the days when PM2.5 level is hazardous to human health. Data source https://chartaccent.github.io/chartaccent.html",
    "layer": [{
      "data": {
        "values": [
          {"Day": 1, "Value": 54.8},
          {"Day": 2, "Value": 112.1},
          {"Day": 3, "Value": 63.6},
          {"Day": 4, "Value": 37.6},
          {"Day": 5, "Value": 79.7},
          {"Day": 6, "Value": 137.9},
          {"Day": 7, "Value": 120.1},
          {"Day": 8, "Value": 103.3},
          {"Day": 9, "Value": 394.8},
          {"Day": 10, "Value": 199.5},
          {"Day": 11, "Value": 72.3},
          {"Day": 12, "Value": 51.1},
          {"Day": 13, "Value": 112.0},
          {"Day": 14, "Value": 174.5},
          {"Day": 15, "Value": 130.5},
          {"limithigh":20, "limitlow":10}
        ]
      },
      "layer": [{
        "mark": "line",
        "encoding": {
          "x": {"field": "Day", "type": "ordinal", "axis": {"labelAngle": 0}},
          "y": {"field": "Value", "type": "quantitative"}
        }
      }, 
      {
        "mark": "line",
        "transform": [
          {"filter": "datum.Value >= 200"},
          {"calculate": "200", "as": "baseline"}
        ],
        "encoding": {
          "x": {"field": "Day", "type": "ordinal"},
          "y": {"field": "baseline", "type": "quantitative", "title": "PM2.5 Value"},
          "y2": {"field": "Value"},
          "color": {"value": "#FF0000"}
        }
      },
      {
        "mark": "line",
        "transform": [
          {"filter": "datum.Value <= 100"},
          {"calculate": "100", "as": "baseline"}
        ],
        "encoding": {
          "x": {"field": "Day", "type": "ordinal"},
          "y": {"field": "baseline", "type": "quantitative", "title": "PM2.5 Value"},
          "y2": {"field": "Value"},
          "color": {"value": "#FF0000"}
        }
      }
    ]}, {
      "data": {
         "values": [{}]
      },
      "encoding": {
        "y": {"datum": 100},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limitlow"
        }
      }]
    },
    {
      "data": {
         "values": [{}]
      },
      "encoding": {
        "y": {"datum": 200},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limithigh"
        }
      }]
    }
  ]
}

Problems:
This only works in the Vega Editor. The exact same script looks terrible in kibana:

Questions:
Why does Kibana display something so different?
How can I change this script so that it runs on elastic data?
How can I change limithigh and limitlow according to a document field in elastic?

It seems like you are using both Vega and Vega-Lite specs interchangeably, which is not possible. Also, the main reason you could see differences is that Kibana is using Vega-Lite 2 and Vega 4, which are different from what's available in the online editor. This will be fixed in Kibana 7.9, which is not released yet.

There are other differences that I think you are being affected by:

  • Stop using a line chart for this. Use bars instead, and you will see the results you expect.
  • To draw a second line in Vega-Lite, try:
mark: rule
encoding: {
  y: { datum: 100 }
}
  • Your data appears to have null or undefined values. Why?

Thank you for your answer. What are Vega and what are vega-Lite specs that I used? I am new to vega so I copy and paste whaterver looks usefull and hope it works. I know thats not a good idea. I imported the same vega version in the online editor and kibana but still it looks different. How can that be?
The null values in my data are actually usefull and will happen in the real application. We measure a lot of systems and sometimes if nothing is measured we will get back a null. We somehow have to display that there is no data available. But thats another topic.
How can I change the "100" with a value I get out of elastic? Lets say the document contains the limits. How can vega react on those limits?

mark: rule
encoding: {
  y: { datum: limithigh }
}

or something like this?

Thanks again,
defalt

I don't understand what you mean about "limithigh" and "limitlow" coming from Elasticsearch. Can you construct an Elasticsearch query that returns the documents you want, and show a sample response? It's impossible to help without being specific about the exact data you have.

Sure. So my json looks something like this:

{
"_index": "index-1",
"_type": "_doc",
"_id": "u1GSM3MBY5DVSVCPlCsG",
"_score": 1,
"_source": {
"serialnumber": "ABCD",
.
.
"datetimestart": "2020-01-25 09:48:50",
"datetimestop": "2020-01-25 09:50:37",
"tester": "",
"group": "Station 1",
"subgroup": null,
"testsequence": null,
"versiontestsequence": null,
"variant": "XXXXX",
"stepid": "010",
"stepname": "Check Hi-State",
"resultstate": "P",
"limitlow": "100",
"limithigh": "200",
.
.
.

"error": "",
"ordercounter": 0,
"resultdecimal": 121,
"resultstring": "",
"key": "001D6905B2FE2020-01-25 09:48:50",
"timestamp": "2020-07-09 14:35:09"
}
},

Vega should display lines (or bars) according to the value of resultdecimal and threshold lines according to limithigh and limitlow.
So the query would look something like this:

     body: {
            "size": 1,
            "_source": ["limithigh", "limitlow"]
          },
  body: {
            "size": 100,
            "_source": ["resultdecimal", "limitlow"]
          },

Thanks

Also would it be possible to display the data if it was stored in the following way:

   "main":[
      {
         "name":"measure Motorvoltage",
         "data":[
            -24.11567552002,
            -24.11041611212,
            -24.11473538108,
            -24.11397716553,
            -24.13126907349,
            -24.11604690552,
            -24.13298931986,
            -23.80578285018,
            -23.81188564301,
            -24.12420920842,
            -24.11945743561,
            -24.12527008057,
            -24.12200634153,
            -24.14177821509,
            -24.10886670773,
            -24.1270651633,
            -24.12090964328,
            -24.1275623502,
            -24.12081482591,
            -24.11173815342,
            -24.12824227191,
            -24.11341289719,
            -24.13316020966,
            -24.11966495514,
            -24.11388301849,
            -24.127078339,
            -24.13276177518,
            -24.12728393552,
            -24.12185897827,
            -23.81337313126,
            -24.11725834598,
            -24.14834588144,
            -24.1223714026,
            -24.11600112915,
            -24.12093830109,
            -24.11075413521,
            -24.11621341705,
            -24.12289524078,
            -24.13053887805,
            -24.13469951091,
            -24.1039119354,
            -24.13914549242,
            -24.13136028249,
            -24.11679527291,
            -24.11186161041,
            -24.12458744049,
            -24.12575836182,
            -24.13502767775,
            -24.13085002899,
            -24.1241650738,
            -24.1233663268
         ]
      }
   ],
   "limitlow":-24.96,
   "limithigh":-23.04
} 

Thanks a lot,
defalt

Okay, to answer the questions:

  1. It is possible to run multiple queries in both Vega and Vega-Lite, but you need to give them unique names to do so. Look at the docs for Vega-Lite datasets and Vega data model, as well as the querying Elasticsearch from Vega docs we have.

  2. When the data is stored in an array inside an array, you need to use the Vega flatten transform. This is available in all versions of Kibana. The Vega-Lite flatten transform is not yet available in Kibana, it will be released in 7.9

Thanks for the input. And is it possible to change the threshold line according to the value in the document?

Thanks

Once you flatten the documents and verify the contents using VEGA_DEBUG.view.data('whatever name you chose') then yes, you can use those properties in the encoding

Can you show me how it could work with this code?:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v2.6.json",
  "description": "Testing",
  "width": 1000,
  "height": 200,
  "padding": 5,
    "layer": [{
      "data": {
        "values": [
          {"Test": "Test1", "resultdecimal": 54.8, "limithigh": 300, "limitlow":100},
          {"Test": "Test2", "resultdecimal": 112.1, "limithigh": 300, "limitlow":100},
          {"Test": "Test3", "resultdecimal": 63.6, "limithigh": 300, "limitlow":100},
          {"Test": "Test4", "resultdecimal": 37.6, "limithigh": 300, "limitlow":100},
          {"Test": "Test5", "resultdecimal": 79.7, "limithigh": 300, "limitlow":100},
          {"Test": "Test6", "resultdecimal": 137.9, "limithigh": 300, "limitlow":100},
          {"Test": "Test7", "resultdecimal": 120.1, "limithigh": 300, "limitlow":100},
          {"Test": "Test8", "resultdecimal": 103.3, "limithigh": 300, "limitlow":100},
          {"Test": "Test9", "resultdecimal": 394.8, "limithigh": 300, "limitlow":100},
          {"Test": "Test10", "resultdecimal": 199.5, "limithigh": 300, "limitlow":100},
          {"Test": "Test11", "resultdecimal": 72.3, "limithigh": 300, "limitlow":100},
          {"Test": "Test12", "resultdecimal": 51.1, "limithigh": 300, "limitlow":100},
          {"Test": "Test13", "resultdecimal": 112.0, "limithigh": 300, "limitlow":100},
          {"Test": "Test14", "resultdecimal": 174.5, "limithigh": 300, "limitlow":100},
          {"Test": "Test15", "resultdecimal": 130.5, "limithigh": 300, "limitlow":100}
        ]
      },
      "layer": [{
        "mark": "line",
        "encoding": {
          "x": {"field": "Test", "type": "ordinal", "axis": {"labelAngle": -70}},
          "y": {"field": "resultdecimal", "type": "quantitative"}
        }
      } 
    ]}, 
    {
      "data": {
         "values": [{}]
      },
      "encoding": {
        "y": {"datum": 100},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limitlow"
        }
      }]
    },
    {
      "data": {
         "values": [{}]
      },
      "encoding": {
        "y": {"datum": 200},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limithigh"
        }
      }]
    }
  ]
}

Thanks

This is probably a bad format so another format were limithigh and limitlow are only set once is also ok. Open for any suggestions.

With the combined format, you should only have one data block instead of separate data for each layer. Something like this:

{
  "$schema": "https://vega.github.io/schema/vega-lite/v2.6.json",
  "description": "Testing",
  "data": {
    "values": [
      {"Test": "Test1", "resultdecimal": 54.8, "limithigh": 300, "limitlow":100},
      {"Test": "Test2", "resultdecimal": 112.1, "limithigh": 300, "limitlow":100},
      {"Test": "Test3", "resultdecimal": 63.6, "limithigh": 300, "limitlow":100},
      {"Test": "Test4", "resultdecimal": 37.6, "limithigh": 300, "limitlow":100},
      {"Test": "Test5", "resultdecimal": 79.7, "limithigh": 300, "limitlow":100},
      {"Test": "Test6", "resultdecimal": 137.9, "limithigh": 300, "limitlow":100},
      {"Test": "Test7", "resultdecimal": 120.1, "limithigh": 300, "limitlow":100},
      {"Test": "Test8", "resultdecimal": 103.3, "limithigh": 300, "limitlow":100},
      {"Test": "Test9", "resultdecimal": 394.8, "limithigh": 300, "limitlow":100},
      {"Test": "Test10", "resultdecimal": 199.5, "limithigh": 300, "limitlow":100},
      {"Test": "Test11", "resultdecimal": 72.3, "limithigh": 300, "limitlow":100},
      {"Test": "Test12", "resultdecimal": 51.1, "limithigh": 300, "limitlow":100},
      {"Test": "Test13", "resultdecimal": 112.0, "limithigh": 300, "limitlow":100},
      {"Test": "Test14", "resultdecimal": 174.5, "limithigh": 300, "limitlow":100},
      {"Test": "Test15", "resultdecimal": 130.5, "limithigh": 300, "limitlow":100}
    ]
  },
  "padding": 5,
    "layer": [{
      "layer": [{
        "mark": "line",
        "encoding": {
          "x": {"field": "Test", "type": "ordinal", "axis": {"labelAngle": -70}},
          "y": {"field": "resultdecimal", "type": "quantitative"}
        }
      } 
    ]}, 
    {
      "encoding": {
        "y": {"datum": 100},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limitlow"
        }
      }]
    },
    {
      "encoding": {
        "y": {"datum": 200},
        "stroke": {"value": "#FF0000"} 
      },
      "layer": [{
        "mark": "rule"
      }, {
        "mark": {
          "type": "text",
          "align": "right",
          "baseline": "bottom",
          "dx": -2,
          "dy": -2,
          "x": "width",
          "text": "limithigh"
        }
      }]
    }
  ]
}

I don't actually have an example for you with separate queries.

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.