A nice usage scenario for chart annotation (e.g. heatmap) is to merge two values (e.g. local riskscore, global riskscore) into a single color. Of course the same could be achieved in coming up with an overall score first and mapping this into a colorcode.
But since splunk can do, let's take the more colorful approach of "mapping two values into a 2D color space" ("Bilinear Interpolate Color Gradient").
As referenced in slide 26 of conf19's: SEC1374 - Augment Your Security Monitoring Use Cases with Splunk's Machine Learning Toolkit (@time 41:04)
Colorize Graph using two riskscores
bilinearInterpolateColorGradient(inputVal1, inputVal2, colorspaceCol00, colorspaceColX0, colorspaceCol0Y, colorspaceColXY, "outcolor")
Goal:
- Input two values in the range of 0..1
- Map into a 2D colorspace defined by the four colorcodes in the corners
- Output a single colorcode in the format "#AABBCC"
Result:
The two input values will get mapped into a 2D colorspace like this one:
2DColorspace at HarmonicCode: harmoniccode.blogspot.com
For a great explanation, refer to this article:
Bilinear color interpolation at HarmonicCode: harmoniccode.blogspot.com
Code: (4 Macros)
bilinearInterpolateColorGradient(7)
`comment("
Maps two values into a color space with four colors.
Outputs a final color, smoothly interpolated between these four color,
on basis of the two input values.
https://harmoniccode.blogspot.com/2011/04/bilinear-color-interpolation.html
x: Input value 1 (x axis in color space. left->right)
y: Input value 2 (y axis in color space. bottom->top)
col00: color in the bottom left corner of the color space.
colX0: color in the bottom right corner of the color space.
col0Y: color in the top left corner of the color space.
colXY: color in the top right corner of the color space.
outcolorfieldname: name of field in which the final color is written.
")`
| eval _x = $x$
| eval _y = $y$
| eval _col00 = $col00$
| eval _colX0 = $colX0$
| eval _col0Y = $col0Y$
| eval _colXY = $colXY$
`interpolateColor(_col00, _colX0, _x, "_outcolorB")`
`interpolateColor(_col0Y, _colXY, _x, "_outcolorT")`
`interpolateColor(_outcolorB, _outcolorT, _y, "_outcolor")`
| eval $outcolorfieldname$ = _outcolor
interpolateColor(4)
| eval _col1 = $col1$
| eval _col2 = $col2$
| eval _lerpt = $lerpt$
| eval _outcolor = "#ZZZZZZ"
| eval _lerpt = if(_lerpt > 1, 1.0, _lerpt)
| eval _lerpt = if(_lerpt < 0.0, 0.0, _lerpt)
`splitColorIntoComponents(_col1, "_col1r", "_col1g", "_col1b")`
`splitColorIntoComponents(_col2, "_col2r", "_col2g", "_col2b")`
| eval _deltaRed = _col2r - _col1r, _deltaGreen = _col2g - _col1g, _deltaBlue = _col2b - _col1b
| eval _outRed = _col1r + _lerpt * _deltaRed, _outGreen = _col1g + _lerpt * _deltaGreen, _outBlue = _col1b + _lerpt * _deltaBlue
`genColorFromComponents(_outRed, _outGreen, _outBlue, "_outcolor")`
| eval $outcolorfieldname$ = _outcolor
splitColorIntoComponents(4)
| eval _color = $color$
| rex field=_color "^#(?<_red>[0-9a-zA-Z]{2,2})(?<_green>[0-9a-zA-Z]{2,2})(?<_blue>[0-9a-zA-Z]{2,2})$"
| eval _red = tonumber(_red, 16), _green = tonumber(_green, 16), _blue = tonumber(_blue, 16)
| eval $oufieldname_red$ = _red
| eval $oufieldname_green$ = _green
| eval $oufieldname_blue$ = _blue
genColorFromComponents(4)
| eval _red = $red$
| eval _green = $green$
| eval _blue = $blue$
| eval _outcolor = "#" + upper(printf("%02x", _red)) + upper(printf("%02x", _green)) + upper(printf("%02x", _blue))
| eval $outcolorfieldname$ = _outcolor
Testbed / Usage example
| makeresults count=484
| eval count = 22
| streamstats count as id
| eval id = id - 1
| eval x = floor(id / count) / count, y = (id % count) / count, z = 0
| eval col00 = "#00AA00"
| eval colX0 = "#FFA500"
| eval col0Y = "#FFFF00"
| eval colXY = "#FF0000"
`bilinearInterpolateColorGradient(x, y, col00, colX0, col0Y, colXY, "outcolor")`
| eval clusterId=outcolor, clusterColor=outcolor, z=0
| table clusterId, x, y, z, clusterColor
**-> Render the generated events into MLTK's 3D Scatterplot**
... View more