5.4 Statistical Functions, Semi-Additive Measures, and Calculation Groups

Key Takeaways

  • Core statistical DAX: AVERAGE, MEDIAN, MIN/MAX, STDEV.P/STDEV.S, VAR.P/VAR.S, PERCENTILE.INC/EXC, RANKX.
  • Semi-additive measures (balances, inventory, headcount) can be summed across most dimensions but NOT across time.
  • LASTNONBLANK/LASTDATE return the most recent value; FIRSTNONBLANK returns the opening value, for snapshot measures.
  • DIVIDE() is preferred over the / operator because it returns BLANK() (or an alternate) instead of erroring on divide-by-zero.
  • Calculation groups define time-intelligence patterns once and apply them to any measure via SELECTEDMEASURE().
Last updated: June 2026

Quick Answer: Use DIVIDE() instead of / for safe division. Semi-additive measures (balances, inventory) should not be summed across dates; use LASTNONBLANK or LASTDATE for the most recent value. Calculation groups let you define time-intelligence patterns (YTD, PY, YoY) once and apply them to any measure automatically.

Aggregation Functions

FunctionDescription
SUMAdds all values
AVERAGEArithmetic mean
MIN / MAXSmallest / largest value
COUNTCount numeric non-blanks
COUNTACount non-blanks of any type
COUNTBLANKCount blank/null values
COUNTROWSCount rows in a table
DISTINCTCOUNTCount unique values

Advanced Statistical Functions

FunctionDescription
MEDIANMiddle value when sorted
STDEV.P / STDEV.SPopulation / sample standard deviation
VAR.P / VAR.SPopulation / sample variance
PERCENTILE.INC / PERCENTILE.EXCKth percentile, inclusive / exclusive
RANKXRank values in a table

Choose the .S (sample) variants when your data is a sample of a larger population and .P (population) when it represents the entire population. Iterator versions (SUMX, AVERAGEX, COUNTX, MINX, MAXX) evaluate an expression per row, then aggregate.

Total Revenue = SUMX(Sales, Sales[Quantity] * Sales[UnitPrice])
Avg Order Profit = AVERAGEX(Sales, Sales[Revenue] - Sales[Cost])

The DIVIDE Function

DIVIDE(numerator, denominator [, alternateResult]) is safe division.

Operation/ OperatorDIVIDE()
10 / 522
10 / 0Error/InfinityBLANK() (or alternate)
0 / 0Error/NaNBLANK() (or alternate)
Profit Margin = DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]))
Profit Margin = DIVIDE(SUM(Sales[Profit]), SUM(Sales[Revenue]), 0)

Best Practice: Always use DIVIDE() in measures; it handles divide-by-zero gracefully and avoids runtime errors.

Semi-Additive Measures

Measure TypeAdditive Across Time?Example
Fully AdditiveYesRevenue, Quantity, Cost
Semi-AdditiveNoAccount Balance, Inventory, Headcount
Non-AdditiveNo (across any dimension)Ratios, Percentages

Summing monthly inventory snapshots (100 + 120 + 90 = 310) is meaningless; you want the last known value (90).

Current Inventory =
CALCULATE(SUM(Inventory[Units]), LASTNONBLANK('Date'[Date], CALCULATE(COUNTROWS(Inventory))))

Ending Balance = CALCULATE(SUM(Accounts[Balance]), LASTDATE('Date'[Date]))

Opening Balance =
CALCULATE(SUM(Accounts[Balance]), FIRSTNONBLANK('Date'[Date], CALCULATE(COUNTROWS(Accounts))))

Note that semi-additive measures are still additive across non-time dimensions, summing balances across several accounts on the same date is correct.

Calculation Groups

Without calculation groups you must author a separate measure for every base x pattern combination. With 3 base measures and 5 patterns you get 15 measures; with 20 base measures you get 100. Calculation groups define the patterns once and apply them to any selected measure.

Calculation ItemLogic
CurrentSELECTEDMEASURE()
YTDCALCULATE(SELECTEDMEASURE(), DATESYTD('Date'[Date]))
PYCALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('Date'[Date]))
YoYSELECTEDMEASURE() - CALCULATE(SELECTEDMEASURE(), SAMEPERIODLASTYEAR('Date'[Date]))
QTDCALCULATE(SELECTEDMEASURE(), DATESQTD('Date'[Date]))

SELECTEDMEASURE() is a placeholder for whichever measure the user puts in the visual, so any base measure (Revenue, Cost, Profit) can be viewed through any pattern selected from a slicer. Calculation groups are authored in Tabular Editor or directly in Power BI Desktop (Model view > New calculation group), and items are ordered with an ordinal to control display order.

Exam Tip: Know the purpose of calculation groups and recognize the scenario, dozens of repetitive time-intelligence measures, where they are the right solution.

On the Exam

The PL-300 tests using DIVIDE() over /, identifying semi-additive measures and handling them with LASTNONBLANK/LASTDATE, choosing SUM vs. SUMX, and the purpose of calculation groups.

Additivity Is a Property of the Measure, Not the Column

The exam wants you to classify a measure by how it may be aggregated. Fully additive values (revenue, quantity, cost) can be summed across every dimension, including time. Semi-additive values (account balance, inventory on hand, headcount) can be summed across non-time dimensions but not across time, summing three monthly balances invents money. Non-additive values (ratios, percentages, averages, distinct counts) cannot be summed across any dimension and must be recomputed from their components in each context.

Misclassifying a measure produces totals that look plausible but are wrong, which is why scenarios about "the total at the bottom of the matrix is too high" usually hinge on additivity.

Picking the Right Snapshot Function

For semi-additive snapshots, the choice of function depends on whether your snapshot rows are dense or sparse. LASTDATE returns the value at the latest date in context, which works when every period has a row. LASTNONBLANK (and FIRSTNONBLANK) scan backward (or forward) for the most recent date where an expression is non-blank, which is safer when some periods are missing, for example a balance that is only recorded on business days. OPENINGBALANCE* and CLOSINGBALANCE* functions wrap these patterns for month, quarter, and year and are worth recognizing by name.

Sample vs. Population, and Inclusive vs. Exclusive

Two function-family distinctions appear regularly. Use STDEV.S/VAR.S (sample) when your rows are a sample drawn from a larger population and STDEV.P/VAR.P (population) when the rows are the entire population, the sample variants divide by n-1, the population variants by n. Similarly, PERCENTILE.INC includes the endpoints (0th and 100th percentile are valid) while PERCENTILE.EXC excludes them; Excel-compatible reports usually expect .INC. Knowing which variant a scenario calls for is a small but real source of exam points.

Test Your Knowledge

A table contains monthly account balances. Summing the Balance column across all months gives a misleadingly large result. What type of measure is this, and how should you handle it?

A
B
C
D
Test Your Knowledge

What does DIVIDE(100, 0) return by default?

A
B
C
D
Test Your Knowledge

You have 15 base measures and need Current, YTD, PY, and YoY versions of each. Without calculation groups, how many total measures would you need?

A
B
C
D
Test Your Knowledge

In a calculation group item, what does SELECTEDMEASURE() represent?

A
B
C
D