Kibana Vega - Showing tooltip on parallel coordinates

Hi,

Quick summary of what I want the visualization to be like ...
I have multiple IHS proxys which route requests to a bunch of servers. I want to draw a parallel coordinates graph that would show the number of requests getting routed from each IHS proxy to each server. That way, if there is an imbalance in the routing logic, we would come to know. The thickness as well as opacity of the lines indicate the number of requests. This is all working fine. What is not working fine is: when I hover over a line, I want to show the number of requests represented by the line. I don't know how to achieve this.

My data:
All the IHS logs are sent to elastic. For easy sharing and simulation, I have uploaded the output of the elastic query to gist.github.com and running the visualization on that.

High Level Logic:

  • dataset ihsdata - Extract IHS and AppServer fields from elastic index (now gist.github json) and form a dataset.

  • dataset ihssummary - On ihsdata, apply group by on IHS and AppServer fields

  • Now ihssummary has data IHS-AppServer-count.

  • dataset distinct-ihs - On ihssummary, apply group by on IHS

  • Now distinct-ihs has IHS-count (I am interested only in IHS column - this will be used for ordinal scale later)

  • dataset distinct-appserver - On ihssummary, apply group by on AppServer

  • Now distinct-appserver has AppServer-count (I am interested only in AppServer column - this will be used for ordinal scale later)

  • dataset fields - hardcoded values IHS and AppServer

  • scale ord - scale on "fields" dataset - to be used to draw the parallel axes

  • scale IHS - mapping all distinct IHS values to a range 20 to Height - to be used to plot points on IHS axis

  • scale AppServer - mapping all distinct AppServer values to a range 20 to Height - to be used to plot points on AppServer axis

  • scale StrokeWidth - mapping ihssummary.count to range [1-10] - to be used for strokeWidth of the lines

  • scale StrokeOpacity - mapping ihssummary.count to range [0-1] - to be used for strokeOpacity of the lines

  • draw two vertical axes (IHS and AppServer)

  • marks -

    • "group" mark over dataset ihssummary
      • "line" mark over dataset fields
        • enter: draw line by passing appropriate x and y values by picking parent's IHS/AppServer values and mapping them with appropriate scale.
        • hover: make line red
        • update: make line back to steelblue

Problem:
The above code all works fine. Now, I want to add a text that shows the number of requests on top-right when someone hovers on the line.
So, I tried to add a mouseover signal on the line.

  "signals": [
    {
      "name": "tooltip",
      "value": {},
      "on": [
        {"events": "line:mouseover", "update": "datum"},
        {"events": "line:mouseout",  "update": "{}"}
      ]
    }
  ]

But the line datum has nothing useful. All it has is:

VEGA_DEBUG.view.signal("tooltip")
{data: "IHS", Symbol(vega_id): 171852}
data: "IHS"
Symbol(vega_id): 171852
__proto__: Object

I really need to get the ihssummary data into that signal. I tried "update": "parent" and also "update": {"parent": "count"}, but none of those worked.

How do I get this working?

My Vega Code:
(Attaching it into first reply due to post size limit)

Thanks a lot.

-- Parag

My vega code:

{
  "$schema": "https://vega.github.io/schema/vega/v3.json",
  "width": 700,
  "height": 400,
  "padding": 5,
  "autosize": "pad",

  "config": {
    "axisY": {
      "titleX": -2,
      "titleY": 410,
      "titleAngle": 0,
      "titleAlign": "right",
      "titleBaseline": "top"
    }
  },

  "data": [
    {
      "name": "ihsdata",
      "url": "https://gist.githubusercontent.com/tanmaydeuskar/630fbc30e94322f7442ea1f2042c53ec/raw/889bf99786e7463afbafa1c2eb8278278c08e166/ihs_vega_es_output.json",
      "format": {"property": "hits.hits"},
      "transform": [
        {"type": "formula", "as": "IHS", "expr": "datum._source.IHS"},
        {"type": "formula", "as": "AppServer", "expr": "datum._source.AppServer"}
      ]
    },
    {
      "name": "ihssummary", "source": "ihsdata",
      "transform": [
        { "type": "aggregate", "groupby": ["IHS", "AppServer"] }
      ]
    },
    {
      "name": "distinct-ihs", "source": "ihssummary",
      "transform": [
        { "type": "aggregate", "groupby": ["IHS"] },
        { "type": "collect", "sort": {"field": "IHS"} }
      ]
    },
    {
      "name": "distinct-appserver", "source": "ihssummary",
      "transform": [
        { "type": "aggregate", "groupby": ["AppServer"] },
        { "type": "collect", "sort": {"field": "AppServer"} }
      ]
    },
    {
      "name": "fields",
      "values": [
        "IHS",
        "AppServer"
      ]
    }
  ],

  "signals": [
    {
      "name": "tooltip",
      "value": " ",
      "on": [
        {"events": "line:mouseover", "update": {"value": "How to show the number of requests represented by the HOVERED LINE here"}},
        {"events": "line:mouseout",  "update": {"value": " "}}
      ]
    }
  ],

  "scales": [
    {
      "name": "ord", "type": "point",
      "range": "width", "round": true,
      "domain": {"data": "fields", "field": "data"}
    },
    {
      "name": "IHS", "type": "point",
      "range": [20, {"signal": "height"}], "round": true,
      "domain": {"data": "distinct-ihs", "field": "IHS"}
    },
    {
      "name": "AppServer", "type": "point",
      "range": [20, {"signal": "height"}], "round": true,
      "domain": {"data": "distinct-appserver", "field": "AppServer"}
    },
    {
      "name": "StrokeWidth", "type": "linear",
      "range": [1,10],
      "domain": {"data": "ihssummary", "field": "count"}
    },
    {
      "name": "StrokeOpacity", "type": "linear",
      "range": [0,1],
      "domain": {"data": "ihssummary", "field": "count"}
    }
  ],

  "axes": [
    {
      "orient": "left", "zindex": 1,
      "scale": "IHS", "title": "IHS",
      "offset": {"scale": "ord", "value": "IHS", "mult": -1},
      "labelFontSize": 20, "labelColor": "red", "labelAngle": 45
    },
    {
      "orient": "left", "zindex": 1,
      "scale": "AppServer", "title": "AppServer",
      "offset": {"scale": "ord", "value": "AppServer", "mult": -1}
    }
  ],

  "marks": [
    {
      "type": "group",
      "from": {"data": "ihssummary"},
      "marks": [
        {
          "type": "line",
          "from": {"data": "fields"},
          "encode": {
            "enter": {
              "x": {"scale": "ord", "field": "data"},
              "y": {
                "scale": {"datum": "data"},
                "field": {"parent": {"datum": "data"}}
              },
              "stroke": {"value": "steelblue"},
              "strokeWidth": {
                "scale": "StrokeWidth",
                "field": {"parent": "count"}
              },
              "strokeOpacity": {
                "scale": "StrokeOpacity",
                "field": {"parent": "count"}
              }
            },
            "update": {
              "stroke": {"value": "steelblue"}
            },
            "hover": {
              "stroke": {"value": "red"}
            }
          }
        },
        {
          "type": "text",
          "encode": {
            "enter": {
              "x": {"signal": "width"},
              "y": {"value": 0},
              "align": {"value": "right"},
              "fill": {"value": "blue"},
              "font": {"value": "sans-serif"},
              "fontSize": {"value": 12}
            },
            "update": {
              "text": {"signal": "tooltip"}
            }
          }
        }
      ]
    }
  ]
}

I feel that this will be impossible by using a line type mark. With line type mark, each datum needs to only represent one point on the line. Now, there would be no way to keep the count on such kind of data. (At least in a single-level data -- maybe it would work out if we have multi-level/nested data, but I am not familiar with that). So, the only way I think this will be possible is if I operate on ihssummary data (which already has "count") and somehow compute svg path for two points (from given IHS value to given AppServer value from that datum) and add that svg path to the datum. That would need an ability to match the IHS value to IHS scale, get the x-y, match AppServer value to AppServer scale, get the x-y, convert this to svg path. Once that is done, I can use path type mark and set a tooltip signal on that mark. Now the data behind that would contain the count, which I would be able to show as tooltip. But don't know how to do such a transformation.

Or maybe there is another approach too?

Thanks.

-- Parag

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

There are a few changes I think you should change to your graph:

  1. add a name to your line mark, e.g. "name": "myline", right before the "type": "line",
  2. the data is not part of the line, it is part of the group, so use "update": {"expr": "group().datum.count"} for the signal value
  3. (unrelated to the above, but highly recommended) - do not use any positioning values inside the enter encoding. Move x and y to the update section. This way it will continue working if you resize the graph/window.
1 Like

Oops, forgot -- don't use line:mouseover. Use mark's name instead: {"events": "@myline:mouseover", "update": {"expr": "group().datum.count"}} (and same for other similar events)

2 Likes

Note that group() function can be used to get any parent group by its name.

@nyuriks, thanks a lot.