import type { TopLevelSpec } from 'vega-lite/build/vega-lite' ; import { euiLightVars as euiThemeLight } from '@kbn/ui-shared-deps-src/theme' ; import { euiPaletteColorBlind , euiPaletteNegative , euiPalettePositive } from '@elastic/eui' ; import { i18n } from '@kbn/i18n' ; import { LegendType , LEGEND_TYPES } from '../vega_chart/common' ; export const OUTLIER_SCORE_FIELD = 'outlier_score' ; const SCATTERPLOT_SIZE = 125 ; export const DEFAULT_COLOR = euiPaletteColorBlind ( ) [ 0 ] ; export const COLOR_OUTLIER = euiPaletteNegative ( 2 ) [ 1 ] ; export const COLOR_RANGE_NOMINAL = euiPaletteColorBlind ( { rotations : 2 } ) ; export const COLOR_RANGE_QUANTITATIVE = euiPalettePositive ( 5 ) ; export const getColorSpec = ( euiTheme , escapedOutlierScoreField ? , color ? , legendType ? ) => { if ( typeof escapedOutlierScoreField === 'string' ) { return { condition : { value : COLOR_OUTLIER , test : `(datum['${ escapedOutlierScoreField }'] >= mlOutlierScoreThreshold.cutoff)` , } , value : euiTheme . euiColorMediumShade , } ; } if ( color !== undefined && legendType !== undefined ) { return { field : getEscapedVegaFieldName ( color ) , type : legendType , scale : { range : legendType === LEGEND_TYPES . NOMINAL ? COLOR_RANGE_NOMINAL : COLOR_RANGE_QUANTITATIVE , } , } ; } return { value : DEFAULT_COLOR } ; } ; function getEscapedVegaFieldName ( fieldName ) { return fieldName . replace ( /([\.|\[|\]])/g , '\\$1' ) ; } type VegaValue = Record < string , string | number > ; export const getScatterplotMatrixVegaLiteSpec = ( values , columns , euiTheme , resultsField ? , color ? , legendType ? , dynamicSize ? ) : TopLevelSpec => { const vegaValues = values ; const vegaColumns = columns . map ( getEscapedVegaFieldName ) ; const outliers = resultsField !== undefined ; const escapedOutlierScoreField = `${ resultsField }\\.${ OUTLIER_SCORE_FIELD }` ; const colorSpec = getColorSpec ( euiTheme , resultsField && escapedOutlierScoreField , color , legendType ) ; return { $schema : 'https://vega.github.io/schema/vega-lite/v4.17.0.json' , background : 'transparent' , view : { fill : 'transparent' , stroke : euiTheme . euiColorLightestShade } , padding : 10 , config : { axis : { domainColor : euiTheme . euiColorLightShade , gridColor : euiTheme . euiColorLightestShade , tickColor : euiTheme . euiColorLightestShade , labelColor : euiTheme . euiTextSubduedColor , titleColor : euiTheme . euiTextSubduedColor , } , legend : { orient : 'right' , labelColor : euiTheme . euiTextSubduedColor , titleColor : euiTheme . euiTextSubduedColor , } , } , repeat : { column : vegaColumns , row : vegaColumns . slice ( ) . reverse ( ) , } , spec : { data : { values : [ ... vegaValues ] } , mark : { ... ( outliers && dynamicSize ? { type : 'circle' , strokeWidth : 1.2 , strokeOpacity : 0.75 , fillOpacity : 0.1 , } : { type : 'circle' , opacity : 0.75 , size : 8 } ) , } , encoding : { color : colorSpec , ... ( dynamicSize ? { stroke : colorSpec , opacity : { condition : { value : 1 , test : `(datum['${ escapedOutlierScoreField }'] >= mlOutlierScoreThreshold.cutoff)` , } , value : 0.5 , } , } : { } ) , ... ( outliers ? { order : { field : escapedOutlierScoreField } , size : { ... ( ! dynamicSize ? { condition : { value : 40 , test : `(datum['${ escapedOutlierScoreField }'] >= mlOutlierScoreThreshold.cutoff)` , } , value : 8 , } : { type : LEGEND_TYPES . QUANTITATIVE , field : escapedOutlierScoreField , scale : { type : 'linear' , range : [ 8 , 200 ] , domain : [ 0 , 1 ] , } , } ) , } , } : { } ) , x : { type : LEGEND_TYPES . QUANTITATIVE , field : { repeat : 'column' } , scale : { zero : false } , } , y : { type : LEGEND_TYPES . QUANTITATIVE , field : { repeat : 'row' } , scale : { zero : false } , } , tooltip : [ ... ( color !== undefined ? [ { type : colorSpec . type , field : getEscapedVegaFieldName ( color ) } ] : [ ] ) , ... vegaColumns . map ( ( d ) => ( { type : LEGEND_TYPES . QUANTITATIVE , field : d , } ) ) , ... ( outliers ? [ { type : LEGEND_TYPES . QUANTITATIVE , field : escapedOutlierScoreField , format : '.3f' } ] : [ ] ) , ] , } , ... ( outliers ? { selection : { mlOutlierScoreThreshold : { type : 'single' , fields : [ 'cutoff' ] , bind : { input : 'range' , max : 1 , min : 0 , name : i18n . translate ( 'xpack.ml.splomSpec.outlierScoreThresholdName' , { defaultMessage : 'Outlier score threshold: ' , } ) , step : 0.01 , } , init : { cutoff : 0.99 } , } , } , } : { } ) , width : SCATTERPLOT_SIZE , height : SCATTERPLOT_SIZE , } , } ; } ;