Creating Box Plot in Kibana using Vega is what this tutorial all about. To keep it simple I will use hard coded data. However in the second part of this tutorial the data will come via aggregation from elasticsearch and will be plotted.
WHY
- Box plots are very useful and manufacturing engineers especially love them.
- Kibana at the time of writing does not support boxplot visualization. Yikes!
- Good news: Box plots are supported in Vega-Lite 4.0. And Kibana allows us to use Vega to make visualizations. Lets do it.
- Bad news: Kibana 7.x supports only Vega-Lite 2.6.0. Facepalm.
However here is the big thing. Boxplot aggregation is supported in Elasticsearch 7.x. This opens an avenue to actually plot the results of this aggregation using plain simple Vega.
Idea itself is simple. Do the aggregation in Elasticsearch itself. The values it returns are then used in creating Box Plot in Kibana using Vega.
Life Pro Tip: It is always recommended to do aggregations in Elasticsearch as much as possible. Extracting the raw values out and running Vega on it is not going to scale. It is doomed to end in grief.
HOW
Open kibana and go into visualizations. Choose Vega visualization. Just paste the code snippet given 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 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
{ "$schema": "https://vega.github.io/schema/vega/v4.json", "description": "A box plot example", "width": 600, "autosize": "pad", "padding": 5, "config": { "axisBand": { "bandPosition": 1, "tickExtra": true, "tickOffset": 0 } }, "signals": [ { "name": "plotWidth", "value": 60 }, { "name": "height", "update": "(plotWidth + 10) * 4"} ], "data": [ { "name": "boxdata", "values": [ { "task": "A", "min" : 31.08, "max" : 82.79, "q1" : 41.08, "q2" : 51.93, "q3" : 62.79 }, { "task": "B", "min" : 23.45, "max" : 53.65, "q1" : 33.45, "q2" : 43.55, "q3" : 49.65 }, { "task": "C", "min" : 24.19, "max" : 54.337, "q1" : 34.19, "q2" : 39.26, "q3" : 44.33 }, { "task": "D", "min" : 10.22, "max" : 45.82, "q1" : 12.227, "q2" : 22.52, "q3" : 36.82 } ] } ], "scales": [ { "name": "layout", "type": "band", "range": "height", "domain": {"data": "boxdata", "field": "task"} }, { "name": "xscale", "type": "linear", "range": "width", "round": true, "domain": {"data": "boxdata", "field": "max"}, "zero": true, "nice": true }, { "name": "color", "type": "ordinal", "range": "category", "domain": {"data": "boxdata", "field": "task"} } ], "axes": [ {"orient": "bottom", "scale": "xscale", "zindex": 1}, {"orient": "left", "scale": "layout", "tickCount": 5, "zindex": 1} ], "marks": [ { "type": "group", "from": { "facet": { "data": "boxdata", "name": "mytasks", "groupby": "task" } }, "encode": { "enter": { "yc": {"scale": "layout", "field": "task", "band": 0.5}, "height": {"signal": "plotWidth"}, "width": {"signal": "width"} } }, "marks": [ { "type": "rect", "from": {"data": "mytasks"}, "strokeDash": "[8,4]", "encode": { "enter": { "fill": {"value": "grey"}, "tooltip": {"signal": "datum"}, "height": {"value": 1} }, "update": { "yc": {"signal": "plotWidth / 2", "offset": -0.5}, "x": {"scale": "xscale", "field": "min"}, "x2": {"scale": "xscale", "field": "max"} } } }, { "type": "rect", "from": {"data": "mytasks"}, "encode": { "enter": { "fill": {"value": "steelblue"}, "tooltip": {"signal": "datum"}, "cornerRadius": {"value": 5} }, "update": { "yc": {"signal": "plotWidth / 2"}, "height": {"signal": "plotWidth / 2"}, "x": {"scale": "xscale", "field": "q1"}, "x2": {"scale": "xscale", "field": "q3"}, "fill": {"scale": "color", "field": "task"} } } }, { "type": "rect", "from": {"data": "mytasks"}, "encode": { "enter": { "fill": {"value": "aliceblue"}, "tooltip": {"signal": "datum"}, "width": {"value": 2} }, "update": { "yc": {"signal": "plotWidth / 2"}, "height": {"signal": "plotWidth / 2"}, "x": {"scale": "xscale", "field": "q2"} } } }, { "type": "rect", "from": {"data": "mytasks"}, "encode": { "enter": { "fill": {"value": "grey"}, "tooltip": {"signal": "datum"}, "width": {"value": 2} }, "update": { "yc": {"signal": "plotWidth / 2"}, "height": {"signal": "plotWidth / 2"}, "x": {"scale": "xscale", "field": "min"} } } }, { "type": "rect", "from": {"data": "mytasks"}, "encode": { "enter": { "fill": {"value": "grey"}, "tooltip": {"signal": "datum"}, "width": {"value": 2} }, "update": { "yc": {"signal": "plotWidth / 2"}, "height": {"signal": "plotWidth / 2"}, "x": {"scale": "xscale", "field": "max"} } } } ] } ] } |
Click on Update button. And you will see a nice box plot.
I have added a nice touch. It supports hover text!!
This is very important to a manufacturing engineer. It enables him to see the actual values if needed without leaving the plot.
So what is happening in this code?
I will not go into nitty gritty of the code. Honestly if you are well versed in Vega you won’t be here. On the other hand if you are not comfortable with Vega then you are out of luck. I won’t be explaining it. It will make this tutorial way too long and complicated.
However here are some points. The data I want to plot is named boxdata
and is in the same format as the elasticsearch aggregation returns. Well almost. Ask in comments if you need clarifications on some section of code.
Part two of this tutorial will be all about linking the data in Elasticsearch to this Box plot. Stay tuned.
Hi there! Very interesting tutorial! You mentioned this: “The data I want to plot is named boxdata and is in the same format as the elasticsearch aggregation returns. Well almost. ” Could you please explain how I transformed the data from Elasticsearch into “boxdata”? I’m actually struggling with that part. Thanks in advance!
This is what I was planning to cover in the second part of the tutorial. Coming soon. Stay tuned. 🙂
Anyways till that happens here is what you do. Elasticsearch supports boxplot aggregations. You run that as a part of Vega query. The result is then used to plot the box plot.