Skip to content

Compound.Violin

Generates 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 )
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 DOUBLE The horizontal padding percentage (0.0-1.0, e.g., 0.1 = 10% padding)
paddingY DOUBLE The vertical padding percentage (0.0-1.0, e.g., 0.1 = 10% padding)
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

STRING An SVG violin plot showing the probability density of data using kernel density estimation

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],       // measureVal
        MAX( Samples[Samples] ), // samples
        MAX( Bandwidth[Bandwidth] ), // bandwidth
        DaxLib.SVG.Colour.Theme(
            "Power BI",
            25
        )                   // color
    ),
    BLANK()
)
(
        x: INT64,
        y: INT64,
        width: INT64,
        height: INT64,
        paddingX: DOUBLE,
        paddingY: DOUBLE,
        axisRef: ANYREF EXPR,
        measureRef: NUMERIC EXPR,
        samples: INT64,
        bandwidth: NUMERIC,
        color: STRING
    ) =>

        // Apply padding to dimensions
        VAR _X =            x + ( width * ( COALESCE( paddingX, 0 ) / 2) )
        VAR _Y =            y + ( height * ( COALESCE( paddingY, 0 ) / 2))
        VAR _Width =        width * ( 1 - COALESCE( paddingX, 0 ) )
        VAR _Height =       height * ( 1 - COALESCE( paddingY, 0 ) )

        // For totals
        VAR _Data = 
            SELECTCOLUMNS(
                FILTER(
                    VALUES( axisRef ),
                    NOT ISBLANK( measureRef )
                ),
                "@Value", measureRef
            )

        VAR _NumValues =        COUNTROWS( _Data )
        VAR _InvNumValues =     1 / _NumValues
        VAR _Min =              MINX( _Data, [@Value] )
        VAR _Max =              MAXX( _Data, [@Value] )
        VAR _Range =            _Max - _Min
        VAR _RangePerSample =   _Range / samples
        VAR _XWidth =           _X + _Width
        VAR _YHeight =          _Y + _Height * 0.5

        // Calculate Kernel Density Estimation using Normal distribution
        VAR _KDEInput =
            ADDCOLUMNS(
                GENERATESERIES( 0, samples + 1, 1 ),
                "@InputX", _Min + _RangePerSample * [Value]
            )

        VAR _KDE = 
            ADDCOLUMNS(
                _KDEInput,
                "@KDE", _InvNumValues * SUMX( _Data, NORM.DIST( [@InputX], [@Value], bandwidth, FALSE ) )
            )

        VAR _MaxKDE =       MAXX( _KDE, [@KDE] )

        // Map KDE values to SVG coordinates using normalize function
        VAR _Points = 
            SELECTCOLUMNS(
                ADDCOLUMNS(
                    _KDE,
                    "@X", DaxLib.SVG.Scale.Normalize( [@InputX], _Min, _Max, _X, _XWidth),
                    "@Y", DaxLib.SVG.Scale.Normalize( [@KDE], 0, _MaxKDE, _YHeight, _Y )
                ),
                "Value", [Value],
                "@X", [@X],
                "@Y", [@Y]
            )

        // Previous point via NATURALLEFTOUTERJOIN
        VAR _PointsWithPrev = 
            NATURALLEFTOUTERJOIN(
                _Points,
                SELECTCOLUMNS(
                    _Points,
                    "Value", [Value] + 1,
                    "@PrevX", [@X],
                    "@PrevY", [@Y]
                )
            )

        // Control points + precompute segment strings once
        VAR _CenterY = _Y + (_Height * 0.5)

        VAR _Segs =
            ADDCOLUMNS(
                _PointsWithPrev,
                "@CX", [@PrevX] + ( ( [@X] - [@PrevX] ) / 2 ),
                "@CY", [@Y],
                "@TopSeg",
                    IF(
                        [Value] = 0,
                        "M " & [@X] & " " & _CenterY,
                        "S " & ([@PrevX] + ( ( [@X] - [@PrevX] ) / 2 )) & " " & [@Y] & ", " & [@X] & " " & [@Y]
                    ),
                "@BottomSeg",
                    VAR _MirroredY = _CenterY + (_CenterY - [@Y])
                    VAR _MirroredCY = _CenterY + (_CenterY - [@Y])
                    RETURN
                        IF(
                            [Value] = 0,
                            "",
                            "S " & ([@PrevX] + ( ( [@X] - [@PrevX] ) / 2 )) & " " & _MirroredCY & ", " & [@X] & " " & _MirroredY
                        )
            )

        VAR _TopCurve = CONCATENATEX( _Segs, [@TopSeg], " ", [Value], ASC )

        VAR _BottomCurve = CONCATENATEX( _Segs, [@BottomSeg], " ", [Value], DESC )          

        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 )

Comments