Skip to content

Compound.Boxplot

Creates a Box Plot compound SVG Visual showing statistical distribution

DaxLib.SVG.Compound.Boxplot( x, y, width, height, paddingX, paddingY, axisRef, measureRef, fillColor, strokeColor, showOutliers, 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
fillColor STRING Color for the box fill
strokeColor STRING Color for lines, whiskers, and median
showOutliers BOOLEAN Whether to show outlier points beyond whiskers
orientation STRING Optional: "Horizontal" (default) or "Vertical"

STRING SVG Box Plot

DaxLib.SVG.SVG(
    500,
    100,
    BLANK(),
    DaxLib.SVG.Compound.Boxplot(
        0,                  // x
        0,                  // y
        500,                // width
        100,                // height
        0.05,               // paddingX
        0.02,               // paddingY
        Dates[Date],        // axisRef
        [Total Cost],       // measureRef
        "#EC008C",          // fillColor
        "#605E5C",          // strokeColor
        TRUE,               // showOutliers
        "Horizontal"        // orientation
    ),
    BLANK()
)
function 'DaxLib.SVG.Compound.Boxplot' =
        (
            x: INT64,
            y: INT64,
            width: INT64,
            height: INT64,
            paddingX: DOUBLE,
            paddingY: DOUBLE,
            axisRef: ANYREF EXPR,
            measureRef: NUMERIC EXPR,
            fillColor: STRING,
            strokeColor: STRING,
            showOutliers: BOOLEAN,
            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 _XMin =             MINX( _Data, [@Value] )
            VAR _XMax =             MAXX( _Data, [@Value] )

            VAR _Count =            COUNTROWS( _Data )
            VAR _Min =              MINX( _Data, [@Value] )
            VAR _Max =              MAXX( _Data, [@Value] )

            // Calculate quartiles using standard definitions
            VAR _Q1 =               PERCENTILEX.INC( _Data, [@Value], 0.25 )
            VAR _Median =           PERCENTILEX.INC( _Data, [@Value], 0.5 )
            VAR _Q3 =               PERCENTILEX.INC( _Data, [@Value], 0.75 )

            // Calculate IQR and whisker boundaries (1.5 * IQR rule)
            VAR _IQR =              _Q3 - _Q1
            VAR _LowerWhisker =     MAX( _Min, _Q1 - 1.5 * _IQR )
            VAR _UpperWhisker =     MIN( _Max, _Q3 + 1.5 * _IQR )

            VAR _Orientation = IF( orientation = "Vertical", "Vertical", "Horizontal" )

            // Primary axis (data-mapped) and cross axis
            VAR _PrimLow = IF( _Orientation = "Horizontal", _X, _Y + _Height )
            VAR _PrimHigh = IF( _Orientation = "Horizontal", _X + _Width, _Y )
            VAR _CrossStart = IF( _Orientation = "Horizontal", _Y, _X )
            VAR _CrossLen = IF( _Orientation = "Horizontal", _Height, _Width )

            // Scale statistical values to primary axis coordinates
            VAR _Q1P = DaxLib.SVG.Scale.Normalize( _Q1, _XMin, _XMax, _PrimLow, _PrimHigh )
            VAR _MedianP = DaxLib.SVG.Scale.Normalize( _Median, _XMin, _XMax, _PrimLow, _PrimHigh )
            VAR _Q3P = DaxLib.SVG.Scale.Normalize( _Q3, _XMin, _XMax, _PrimLow, _PrimHigh )
            VAR _LowerWhiskerP = DaxLib.SVG.Scale.Normalize( _LowerWhisker, _XMin, _XMax, _PrimLow, _PrimHigh )
            VAR _UpperWhiskerP = DaxLib.SVG.Scale.Normalize( _UpperWhisker, _XMin, _XMax, _PrimLow, _PrimHigh )

            // Cross-axis dimensions
            VAR _BoxCross = _CrossStart + _CrossLen * 0.2
            VAR _BoxCrossLen = _CrossLen * 0.6
            VAR _CenterCross = _CrossStart + _CrossLen * 0.5

            // Create outlier points beyond whiskers if enabled
            VAR _Outliers = 
                IF(
                    showOutliers,
                    CONCATENATEX(
                        FILTER(
                            _Data,
                            [@Value] < _LowerWhisker || [@Value] > _UpperWhisker
                        ),
                        DaxLib.SVG.Element.Circle(
                            IF( _Orientation = "Horizontal", DaxLib.SVG.Scale.Normalize( [@Value], _XMin, _XMax, _PrimLow, _PrimHigh ), _CenterCross ),
                            IF( _Orientation = "Horizontal", _CenterCross, DaxLib.SVG.Scale.Normalize( [@Value], _XMin, _XMax, _PrimLow, _PrimHigh ) ),
                            2,
                            DaxLib.SVG.Attr.Shapes( strokeColor, BLANK(), BLANK(), BLANK(), BLANK(), BLANK(), BLANK() ),
                            BLANK()
                        ),
                        ""
                    )
                )

            // Lower whisker line (along primary axis)
            VAR _LowerWhiskerLine = 
                DaxLib.SVG.Element.Line(
                    IF( _Orientation = "Horizontal", _LowerWhiskerP, _CenterCross ),
                    IF( _Orientation = "Horizontal", _CenterCross, _LowerWhiskerP ),
                    IF( _Orientation = "Horizontal", _Q1P, _CenterCross ),
                    IF( _Orientation = "Horizontal", _CenterCross, _Q1P ),
                    DaxLib.SVG.Attr.Shapes( BLANK(), BLANK(), BLANK(), strokeColor, 1, BLANK(), BLANK() ),
                    BLANK()
                )

            // Upper whisker line (along primary axis)
            VAR _UpperWhiskerLine = 
                DaxLib.SVG.Element.Line(
                    IF( _Orientation = "Horizontal", _Q3P, _CenterCross ),
                    IF( _Orientation = "Horizontal", _CenterCross, _Q3P ),
                    IF( _Orientation = "Horizontal", _UpperWhiskerP, _CenterCross ),
                    IF( _Orientation = "Horizontal", _CenterCross, _UpperWhiskerP ),
                    DaxLib.SVG.Attr.Shapes( BLANK(), BLANK(), BLANK(), strokeColor, 1, BLANK(), BLANK() ),
                    BLANK()
                )

            // Lower whisker cap (across cross axis)
            VAR _LowerCap = 
                DaxLib.SVG.Element.Line(
                    IF( _Orientation = "Horizontal", _LowerWhiskerP, _BoxCross ),
                    IF( _Orientation = "Horizontal", _BoxCross, _LowerWhiskerP ),
                    IF( _Orientation = "Horizontal", _LowerWhiskerP, _BoxCross + _BoxCrossLen ),
                    IF( _Orientation = "Horizontal", _BoxCross + _BoxCrossLen, _LowerWhiskerP ),
                    DaxLib.SVG.Attr.Shapes( BLANK(), BLANK(), BLANK(), strokeColor, 1, BLANK(), BLANK() ),
                    BLANK()
                )

            // Upper whisker cap (across cross axis)
            VAR _UpperCap = 
                DaxLib.SVG.Element.Line(
                    IF( _Orientation = "Horizontal", _UpperWhiskerP, _BoxCross ),
                    IF( _Orientation = "Horizontal", _BoxCross, _UpperWhiskerP ),
                    IF( _Orientation = "Horizontal", _UpperWhiskerP, _BoxCross + _BoxCrossLen ),
                    IF( _Orientation = "Horizontal", _BoxCross + _BoxCrossLen, _UpperWhiskerP ),
                    DaxLib.SVG.Attr.Shapes( BLANK(), BLANK(), BLANK(), strokeColor, 1, BLANK(), BLANK() ),
                    BLANK()
                )

            // Main box (Q1 to Q3)
            VAR _Box = 
                DaxLib.SVG.Element.Rect(
                    IF( _Orientation = "Horizontal", MIN( _Q1P, _Q3P ), _BoxCross ),
                    IF( _Orientation = "Horizontal", _BoxCross, MIN( _Q1P, _Q3P ) ),
                    IF( _Orientation = "Horizontal", ABS( _Q3P - _Q1P ), _BoxCrossLen ),
                    IF( _Orientation = "Horizontal", _BoxCrossLen, ABS( _Q3P - _Q1P ) ),
                    2, 2,
                    DaxLib.SVG.Attr.Shapes( fillColor, 0.5, BLANK(), strokeColor, 1, BLANK(), BLANK() ),
                    BLANK()
                )

            // Median line (across cross axis)
            VAR _MedianLine = 
                DaxLib.SVG.Element.Line(
                    IF( _Orientation = "Horizontal", _MedianP, _BoxCross ),
                    IF( _Orientation = "Horizontal", _BoxCross, _MedianP ),
                    IF( _Orientation = "Horizontal", _MedianP, _BoxCross + _BoxCrossLen ),
                    IF( _Orientation = "Horizontal", _BoxCross + _BoxCrossLen, _MedianP ),
                    DaxLib.SVG.Attr.Shapes( BLANK(), BLANK(), BLANK(), strokeColor, 2, BLANK(), BLANK() ),
                    BLANK()
                )

            // Combined elements
            VAR _CombinedElements = 
                _LowerWhiskerLine &
                _UpperWhiskerLine &
                _LowerCap &
                _UpperCap &
                _Box &
                _MedianLine &
                _Outliers

            RETURN

                IF( NOT ISEMPTY( _Data ), _CombinedElements )