Compound.Violin¶
Creates a Violin Plot compound SVG Visual showing distribution density using Kernel Density Estimation (KDE)
Kernel Density Estimation (KDE)
KDE creates a smooth estimate of your data's probability density by placing a "kernel" (normal distribution curve) at each data point and summing them together. The violin plot displays this density as a symmetrical shape - wider areas indicate higher probability/frequency of values.
Key Parameters:
-
Samples: Controls the resolution of the density calculation (higher = smoother, but slower performance)
-
Bandwidth: Controls how much each data point influences nearby areas. Smaller bandwidth values create sharper, more detailed curves that closely follow individual data points. Larger bandwidth values create smoother, more generalized shapes that show overall trends
DaxLib.SVG.Compound.Violin( x, y, width, height, paddingX, paddingY, axisRef, measureRef, samples, bandwidth, color, orientation )
| Parameter | Type | Required | Description |
|---|---|---|---|
| x | INT64 | The x position of the compound | |
| y | INT64 | The y position of the compound | |
| width | INT64 | The width of the compound | |
| height | INT64 | The height of the compound | |
| paddingX | DECIMAL | Optional: The horizontal padding percentage (0.0-1.0, e.g., 0.1 = 10% padding). Defaults to 0 | |
| paddingY | DECIMAL | Optional: The vertical padding percentage (0.0-1.0, e.g., 0.1 = 10% padding). Defaults to 0 | |
| axisRef | ANYREF EXPR | The column that the measure will be evaluated against | |
| measureRef | NUMERIC EXPR | The measure to evaluate | |
| samples | INT64 | Number of density calculation points | |
| bandwidth | NUMERIC | Kernel bandwidth for smoothing | |
| color | STRING | Fill color for the violin shape | |
| orientation | STRING | Optional: "Horizontal" (default) or "Vertical" |
STRING SVG Violin Plot
DaxLib.SVG.SVG(
500,
100,
BLANK(),
DaxLib.SVG.Compound.Violin(
0, // x
0, // y
500, // width
100, // height
0.05, // paddingX
0.02, // paddingY
Dates[Date], // axisRef
[Total Cost], // measureRef
MAX( Samples[Samples] ), // samples
MAX( Bandwidth[Bandwidth] ), // bandwidth
"#EC008C", // color
"Horizontal" // orientation
),
BLANK()
)
function 'DaxLib.SVG.Compound.Violin' =
(
x: INT64,
y: INT64,
width: INT64,
height: INT64,
paddingX: DOUBLE,
paddingY: DOUBLE,
axisRef: ANYREF EXPR,
measureRef: NUMERIC EXPR,
samples: INT64,
bandwidth: NUMERIC,
color: STRING,
orientation: STRING
) =>
// Apply padding to dimensions
VAR _X = x + (width * (IF(ISBLANK(paddingX), 0, paddingX) / 2))
VAR _Y = y + (height * (IF(ISBLANK(paddingY), 0, paddingY) / 2))
VAR _Width = width * (1 - IF(ISBLANK(paddingX), 0, paddingX))
VAR _Height = height * (1 - IF(ISBLANK(paddingY), 0, paddingY))
// Check if Axis is numeric
VAR axisSample = MAX( axisRef )
VAR axisIsNumeric = ISNUMERIC( axisSample ) || ISDATETIME( axisSample )
// For totals
VAR _Data =
ADDCOLUMNS(
FILTER(
VALUES( axisRef ),
NOT ISBLANK( measureRef )
),
"@AxisIndex",
IF(
axisIsNumeric,
axisRef,
RANK( DENSE, CALCULATETABLE( VALUES( axisRef ), ALLSELECTED() ) )
),
"@Value", measureRef
)
VAR _NumValues = COUNTROWS( _Data )
VAR _Min = MINX( _Data, [@Value] )
VAR _Max = MAXX( _Data, [@Value] )
VAR _Range = _Max - _Min
VAR _RangePerSample = _Range / samples
// Calculate Kernel Density Estimation using Normal distribution
VAR _KDE =
ADDCOLUMNS(
GENERATESERIES( 0, samples + 1, 1 ),
"@InputX", _Min + _RangePerSample * [Value],
"@KDE",
( 1 / _NumValues ) *
SUMX(
_Data,
NORM.DIST(
_Min + _RangePerSample * [Value],
[@Value],
bandwidth,
FALSE
)
)
)
VAR _MaxKDE = MAXX( _KDE, [@KDE] )
VAR _Orientation = IF( orientation = "Vertical", "Vertical", "Horizontal" )
// Map KDE values to SVG coordinates using normalize function
VAR _Points =
ADDCOLUMNS(
_KDE,
"@X", IF( _Orientation = "Horizontal",
DaxLib.SVG.Scale.Normalize( [@InputX], _Min, _Max, _X, _X + _Width ),
DaxLib.SVG.Scale.Normalize( [@KDE], 0, _MaxKDE, _X + _Width * 0.5, _X + _Width )
),
"@Y", IF( _Orientation = "Horizontal",
DaxLib.SVG.Scale.Normalize( [@KDE], 0, _MaxKDE, _Y + _Height * 0.5, _Y ),
DaxLib.SVG.Scale.Normalize( [@InputX], _Min, _Max, _Y + _Height, _Y )
)
)
// Create control points for smooth Bézier curves
VAR _PointsWithPrev =
NATURALLEFTOUTERJOIN(
_Points,
SELECTCOLUMNS(
_Points,
"Value", [Value] + 1,
"@PrevX", [@X],
"@PrevY", [@Y]
)
)
VAR _WithControlPoints =
ADDCOLUMNS(
_PointsWithPrev,
"@CX", [@prevX] + ( ( [@x] - [@prevX] ) / 2 ),
"@CY", [@y]
)
// Create the violin shape as a single closed path
// Start at the center-left, go up the top curve, then down the bottom curve, and close
VAR _FirstPoint = MINX( _Points, [@X] )
VAR _LastPoint = MAXX( _Points, [@X] )
VAR _CenterY = _Y + (_Height * 0.5)
VAR _CenterX = _X + (_Width * 0.5)
// Top/Right half curve
VAR _TopCurve =
CONCATENATEX(
_WithControlPoints,
IF(
[Value] = 0,
IF( _Orientation = "Horizontal", "M " & [@X] & " " & _CenterY, "M " & _CenterX & " " & [@Y] ),
"S " & [@CX] & " " & [@CY] & ", " & [@X] & " " & [@Y]
),
" ",
[Value],
ASC
)
// Bottom/Left half curve (mirrored) – uses S Bézier for smooth mirror with control points
VAR _PointsWithNext =
NATURALLEFTOUTERJOIN(
_Points,
SELECTCOLUMNS(
_Points,
"Value", [Value] - 1,
"@NextX", [@X],
"@NextY", [@Y]
)
)
VAR _BottomCurve =
CONCATENATEX(
_PointsWithNext,
VAR _MirroredX = IF( _Orientation = "Horizontal", [@X], _CenterX + (_CenterX - [@X]) )
VAR _MirroredY = IF( _Orientation = "Horizontal", _CenterY + (_CenterY - [@Y]), [@Y] )
VAR _MCX = IF( _Orientation = "Horizontal",
[@NextX] + ( [@X] - [@NextX] ) / 2,
_CenterX + ( _CenterX - ( [@NextX] + ( [@X] - [@NextX] ) / 2 ) )
)
VAR _MCY = IF( _Orientation = "Horizontal",
_CenterY + ( _CenterY - [@Y] ),
[@NextY] + ( [@Y] - [@NextY] ) / 2
)
RETURN
IF(
ISBLANK( [@NextX] ),
"L " & _MirroredX & " " & _MirroredY,
IF(
[Value] = 0,
IF( _Orientation = "Horizontal",
"S " & _MCX & " " & _CenterY & ", " & [@X] & " " & _CenterY,
"S " & _CenterX & " " & _MCY & ", " & _CenterX & " " & [@Y]
),
"S " & _MCX & " " & _MCY & ", " & _MirroredX & " " & _MirroredY
)
),
" ",
[Value],
DESC
) // Create a single closed path for the violin shape
VAR _ViolinPath =
_TopCurve &
" " & _BottomCurve &
" Z" // Close the path
// Combined Elements
VAR _CombinedElements =
DaxLib.SVG.Element.Paths(
_ViolinPath, // d
DaxLib.SVG.Attr.Shapes(
color, // fill
0.5, // fillOpacity
BLANK(), // fillRule
color, // stroke
1, // strokeWidth
BLANK(), // strokeOpacity
BLANK() // opacity
),
BLANK() // transforms
)
RETURN
IF( NOT ISEMPTY( _Data ), _CombinedElements )