{"id":9192,"date":"2022-07-22T14:35:20","date_gmt":"2022-07-22T19:35:20","guid":{"rendered":"https:\/\/www.rushworth.us\/lisa\/?p=9192"},"modified":"2022-07-22T15:51:33","modified_gmt":"2022-07-22T20:51:33","slug":"kibana-visualization-vega-line-chart-with-baseline","status":"publish","type":"post","link":"https:\/\/www.rushworth.us\/lisa\/?p=9192","title":{"rendered":"Kibana Visualization &#8211; Vega Line Chart with Baseline"},"content":{"rendered":"\n<p>There&#8217;s often a difference between hypothetical (e.g. the physics formula answer) and real results &#8212; sometimes this is because sciences will ignore &#8220;negligible&#8221; factors that can be, well, <em>more<\/em> than negligible, sometimes this is because the &#8220;real world&#8221; isn&#8217;t perfect. In transmission media, this difference is a measurable &#8220;loss&#8221; &#8212; hypothetically, we know we could send X data in Y delta-time, but we only sent X&#8217;. Loss also happens because stuff breaks &#8212; metal corrodes, critters nest in fiber junction boxes, dirt builds up on a dish. And it&#8217;s not easy, when looking at loss data at a single point in time, to identify what&#8217;s normal loss and what&#8217;s a <em>problem<\/em>. <\/p>\n\n\n\n<p>We&#8217;re starting a project to record a baseline of loss for all sorts of things &#8212; this will allow individuals to check the current loss data against that which engineers say &#8220;this is as good as it&#8217;s gonna get&#8221;. If the current value is close &#8230; there&#8217;s not a problem. If there&#8217;s a big difference &#8230; someone needs to go fix something. <\/p>\n\n\n\n<p>Unfortunately, creating a graph in Kibana that shows the baseline was &#8230; not trivial. There is a <a rel=\"noreferrer noopener\" href=\"https:\/\/vega.github.io\/vega\/docs\/marks\/rule\/\" target=\"_blank\">rule mark<\/a> that allows you to draw a straight line between two points. You cannot just say &#8220;draw a line at <em>\u200by<\/em>\u200b from 0 to some large value that&#8217;s going to be off the graph. The line doesn&#8217;t render (say, 0 => today or the year 2525). You cannot just get the max value of the axis.<\/p>\n\n\n\n<p>I finally stumbled across a series of data contortions that make the baseline graphable. <\/p>\n\n\n\n<p>The data sets I have available have a datetime object (when we measured this loss) and a loss value. For scans, there may be lots of scans for a single device. For baselines, there will only be one record. <\/p>\n\n\n\n<p>The <a href=\"https:\/\/vega.github.io\/vega\/docs\/transforms\/joinaggregate\/\" target=\"_blank\" rel=\"noreferrer noopener\">joinaggregate transformation <\/a>method &#8212; which appends the value to each element of the data set &#8212; was essential because I needed to know the <em>largest<\/em> datetime value that would appear in the chart. <\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, {&#8220;type&#8221;: &#8220;joinaggregate&#8221;, &#8220;fields&#8221;: [&#8220;transformedtimestamp&#8221;], &#8220;ops&#8221;: [&#8220;max&#8221;], &#8220;as&#8221;: [&#8220;maxtime&#8221;]}<\/p>\n\n\n\n<p>The <a rel=\"noreferrer noopener\" href=\"https:\/\/vega.github.io\/vega\/docs\/transforms\/lookup\/\" target=\"_blank\">lookup transformation method<\/a> &#8212; which can access elements from other data sets &#8212; allowed me to get that maximum timestamp value into the baseline data set. Except &#8230; lookup needs an exact match in the search field. Luckily, it <em>does<\/em> return a random (I presume either first or last &#8230; but it didn&#8217;t matter in this case because <em>all<\/em> records have the same max date value) record when multiple matches are found.<\/p>\n\n\n\n<p>So I used a <a rel=\"noreferrer noopener\" href=\"https:\/\/vega.github.io\/vega\/docs\/transforms\/formula\/\" target=\"_blank\">formula transformation method<\/a> to add a <a rel=\"noreferrer noopener\" href=\"https:\/\/vega.github.io\/vega\/docs\/expressions\/#constants\" target=\"_blank\">constant<\/a> to each record as well<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;, {&#8220;type&#8221;: &#8220;formula&#8221;, &#8220;as&#8221;: &#8220;pi&#8221;, &#8220;expr&#8221;: &#8220;PI&#8221;}<\/p>\n\n\n\n<p>Now that there&#8217;s a record to be found, I can add the max time from our scan data into our baseline data<\/p>\n\n\n\n<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , {&#8220;type&#8221;: &#8220;lookup&#8221;, &#8220;from&#8221;: &#8220;scandata&#8221;, &#8220;key&#8221;: &#8220;pi&#8221;, &#8220;fields&#8221;: [&#8220;pi&#8221;], &#8220;values&#8221;: [&#8220;maxtime&#8221;], &#8220;as&#8221;: [&#8220;maxtime&#8221;]}<\/p>\n\n\n\n<p>Voila &#8212; a chart with a horizontal line at the baseline loss value. Yes, I randomly copied a record to use as the baseline and selected the wrong one (why some scans are below the &#8220;good as it&#8217;s ever going to get&#8221; baseline value!). But &#8230; once we have live data coming into the system, we&#8217;ll have reasonable looking graphs. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"445\" src=\"https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph-1024x445.png\" alt=\"\" class=\"wp-image-9193\" srcset=\"https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph-1024x445.png 1024w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph-300x130.png 300w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph-768x334.png 768w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph-750x326.png 750w, https:\/\/www.rushworth.us\/lisa\/wp-content\/uploads\/2022\/07\/Kibana-Vega-BaselineLineGraph.png 1325w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>The full Vega spec for this graph:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: groovy; title: ; notranslate\" title=\"\">\n{\n    &quot;$schema&quot;: &quot;https:\/\/vega.github.io\/schema\/vega\/v4.json&quot;,\n      &quot;description&quot;: &quot;Scan data with baseline&quot;,\n    &quot;padding&quot;: 5,\n\n    &quot;title&quot;: {\n        &quot;text&quot;: &quot;Scan Data&quot;,\n        &quot;frame&quot;: &quot;bounds&quot;,\n        &quot;anchor&quot;: &quot;start&quot;,\n        &quot;offset&quot;: 12,\n        &quot;zindex&quot;: 0\n      },\n    &quot;data&quot;: &#x5B;\n    {\n        &quot;name&quot;: &quot;scandata&quot;,\n        &quot;url&quot;: {\n            &quot;%context%&quot;: true,\n            &quot;%timefield%&quot;: &quot;@timestamp&quot;,\n            &quot;index&quot;: &quot;traces-*&quot;,\n            &quot;body&quot;: {\n            &quot;sort&quot;: &#x5B;{\n                &quot;@timestamp&quot;: {\n                    &quot;order&quot;: &quot;asc&quot;\n                }\n            }],\n            &quot;size&quot;: 10000,\n            &quot;_source&quot;:&#x5B;&quot;@timestamp&quot;,&quot;Events.Summary.total loss&quot;]\n            }\n        }\n        ,&quot;format&quot;: { &quot;property&quot;: &quot;hits.hits&quot;}\n        ,&quot;transform&quot;:&#x5B;\n            {&quot;type&quot;: &quot;formula&quot;, &quot;expr&quot;: &quot;datetime(datum._source&#x5B;&#039;@timestamp&#039;])&quot;, &quot;as&quot;: &quot;transformedtimestamp&quot;}\n            , {&quot;type&quot;: &quot;joinaggregate&quot;, &quot;fields&quot;: &#x5B;&quot;transformedtimestamp&quot;], &quot;ops&quot;: &#x5B;&quot;max&quot;], &quot;as&quot;: &#x5B;&quot;maxtime&quot;]}\n            , {&quot;type&quot;: &quot;formula&quot;, &quot;as&quot;: &quot;pi&quot;, &quot;expr&quot;: &quot;PI&quot;}\n        ]\n    }\n  ,\n   {\n        &quot;name&quot;: &quot;baseline&quot;,\n        &quot;url&quot;: {\n            &quot;%context%&quot;: true,\n            &quot;index&quot;: &quot;baselines*&quot;,\n            &quot;body&quot;: {\n                &quot;sort&quot;: &#x5B;{\n                    &quot;@timestamp&quot;: {\n                        &quot;order&quot;: &quot;desc&quot;\n                    }\n                }],\n                &quot;size&quot;: 1,\n                &quot;_source&quot;:&#x5B;&quot;@timestamp&quot;,&quot;Events.Summary.total loss&quot;]\n            }\n        }\n        ,&quot;format&quot;: { &quot;property&quot;: &quot;hits.hits&quot; }\n        ,&quot;transform&quot;:&#x5B;\n                {&quot;type&quot;: &quot;formula&quot;, &quot;as&quot;: &quot;pi&quot;, &quot;expr&quot;: &quot;PI&quot;}\n                , {&quot;type&quot;: &quot;lookup&quot;, &quot;from&quot;: &quot;scandata&quot;, &quot;key&quot;: &quot;pi&quot;, &quot;fields&quot;: &#x5B;&quot;pi&quot;], &quot;values&quot;: &#x5B;&quot;maxtime&quot;], &quot;as&quot;: &#x5B;&quot;maxtime&quot;]}\n        ]\n  }\n]      \n,\n    &quot;scales&quot;: &#x5B;\n      {\n        &quot;name&quot;: &quot;x&quot;,\n        &quot;type&quot;: &quot;point&quot;,\n        &quot;range&quot;: &quot;width&quot;,\n        &quot;domain&quot;: {&quot;data&quot;: &quot;scandata&quot;, &quot;field&quot;: &quot;transformedtimestamp&quot;}\n      },\n      {\n        &quot;name&quot;: &quot;y&quot;,\n        &quot;type&quot;: &quot;linear&quot;,\n        &quot;range&quot;: &quot;height&quot;,\n        &quot;nice&quot;: true,\n        &quot;zero&quot;: true,\n        &quot;domain&quot;: {&quot;data&quot;: &quot;scandata&quot;, &quot;field&quot;: &quot;_source.Events.Summary.total loss&quot;}\n      }\n    ],\n        &quot;axes&quot;: &#x5B;\n      {&quot;orient&quot;: &quot;bottom&quot;, &quot;scale&quot;: &quot;x&quot;},\n      {&quot;orient&quot;: &quot;left&quot;, &quot;scale&quot;: &quot;y&quot;}\n    ],\n     &quot;marks&quot;: &#x5B;\n                {\n            &quot;type&quot;: &quot;line&quot;,\n            &quot;from&quot;: {&quot;data&quot;: &quot;scandata&quot;},\n            &quot;encode&quot;: {\n              &quot;enter&quot;: {\n                &quot;x&quot;: { &quot;scale&quot;: &quot;x&quot;, &quot;field&quot;: &quot;transformedtimestamp&quot;, &quot;type&quot;: &quot;temporal&quot;,\n      &quot;timeUnit&quot;: &quot;yearmonthdatehourminute&quot;},\n                &quot;y&quot;: {&quot;scale&quot;: &quot;y&quot;,       &quot;type&quot;: &quot;quantitative&quot;,&quot;field&quot;: &quot;_source.Events.Summary.total loss&quot;},\n                &quot;strokeWidth&quot;: {&quot;value&quot;: 2},\n                &quot;stroke&quot;: {&quot;value&quot;: &quot;green&quot;}\n              }\n            }\n          }\n                 ,        {\n            &quot;type&quot;: &quot;rule&quot;,\n            &quot;from&quot;: {&quot;data&quot;: &quot;baseline&quot;},\n            &quot;encode&quot;: {\n              &quot;enter&quot;: {\n                &quot;stroke&quot;: {&quot;value&quot;: &quot;#652c90&quot;},\n                &quot;x&quot;: {&quot;scale&quot;: &quot;x&quot;, &quot;value&quot;: 0},\n                &quot;y&quot;: {&quot;scale&quot;: &quot;y&quot;,      &quot;type&quot;: &quot;quantitative&quot;,&quot;field&quot;: &quot;_source.Events.Summary.total loss&quot;},\n                &quot;x2&quot;: {&quot;scale&quot;: &quot;x&quot;,&quot;field&quot;: &quot;maxtime&quot;, &quot;type&quot;: &quot;temporal&quot;},\n                &quot;strokeWidth&quot;: {&quot;value&quot;: 4},\n                &quot;opacity&quot;: {&quot;value&quot;: 0.3}\n              }\n            }\n          }\n     ]         \n}\n<\/pre><\/div>","protected":false},"excerpt":{"rendered":"<p>There&#8217;s often a difference between hypothetical (e.g. the physics formula answer) and real results &#8212; sometimes this is because sciences will ignore &#8220;negligible&#8221; factors that can be, well, more than negligible, sometimes this is because the &#8220;real world&#8221; isn&#8217;t perfect. In transmission media, this difference is a measurable &#8220;loss&#8221; &#8212; hypothetically, we know we could &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1588],"tags":[931,1589,1591,1669],"class_list":["post-9192","post","type-post","status-publish","format-standard","hentry","category-elk","tag-data-visualization","tag-elk","tag-kibana","tag-vega"],"_links":{"self":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/9192","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=9192"}],"version-history":[{"count":2,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/9192\/revisions"}],"predecessor-version":[{"id":9196,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=\/wp\/v2\/posts\/9192\/revisions\/9196"}],"wp:attachment":[{"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=9192"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=9192"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rushworth.us\/lisa\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=9192"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}