RangeAvg(Above(Sum(Sales), 0, 3))
Moving averages with RangeAvg(Above(...)) in Power BI
RangeAvg(Above(Sum(Sales), 0, 3)) gives a 3-row moving average. In Power BI, use WINDOW with AVERAGEX, the MOVINGAVERAGE visual calculation, or a calendar-based DATESINPERIOD measure.
Short answer: AVERAGEX(WINDOW(-2, REL, 0, REL, ALLSELECTED('Date'[YearMonth]), ORDERBY('Date'[YearMonth])), [Sales]) is row-based like Qlik. In a visual, MOVINGAVERAGE([Sales], 3) does the same.
What it does in Qlik
RangeAvg averages its numeric arguments and ignores NULL and text. Feeding it Above(expr, 0, 3) (current row plus the two above) gives a trailing 3-row average. On the first rows, fewer values are averaged.
Qlik example
RangeAvg(Above(Sum(Sales), 0, 3))Power BI equivalent
Window function
Sales 3M avg =
AVERAGEX (
WINDOW ( -2, REL, 0, REL, ALLSELECTED ( 'Date'[YearMonth] ), ORDERBY ( 'Date'[YearMonth], ASC ) ),
[Sales]
)Calendar-based
Sales 3M avg (calendar) =
CALCULATE (
AVERAGEX ( VALUES ( 'Date'[YearMonth] ), [Sales] ),
DATESINPERIOD ( 'Date'[Date], MAX ( 'Date'[Date] ), -3, MONTH )
)Visual calculation
3M avg = MOVINGAVERAGE ( [Sales], 3 )Gotchas and behaviour differences
- Do not write
AVERAGEX(DATESINPERIOD(...), [Sales]). That averages per day. Iterate the month column as shown. AVERAGEXskips BLANK results, likeRangeAvgskips nulls. A month with no sales is therefore left out of the average, not counted as 0. Use[Sales] + 0if it should count as 0.- Put the column used in
ORDERBYon the visual's axis. If the axis shows another column from the same table instead, such as a month name sorted byYearMonthor a text key sorted by an index column, that column's filter stays on every row and the window shrinks to the current row. Fix both parts. UseSUMMARIZE(ALLSELECTED('Date'), 'Date'[YearMonth])as the relation, and remove the label column's filter withREMOVEFILTERS('Date'[MonthName])in the surroundingCALCULATE. Also don't callALLSELECTEDinside an iterator such asSUMX, where it returns only the rows being iterated. Compute it in a variable before the iterator. - Qlik's version moves over displayed rows. The calendar version moves over calendar months, whether or not they have data.
- Visual calculations have limits that matter when you migrate: you can't filter or sort a visual by them, change their aggregation, or reuse them in another visual. Data exports leave them out. They don't support dynamic format strings or Show items with no data. Slicers, custom visuals and paginated reports don't support them, and you can't pin a visual that uses them to a dashboard or publish it to the web. If any of these matter, use the model measure instead. (Microsoft's list)
Official documentation
- RangeAvg - script and chart function (Qlik Help)
- WINDOW function (DAX) (Microsoft Learn)
- MOVINGAVERAGE function (DAX) (Microsoft Learn)
- DATESINPERIOD (dax.guide)
Related pages
RangeSum(Above(Sum(Sales), 0, RowNo()))
Running totals with RangeSum(Above(...))
The Qlik running-total idiom RangeSum(Above(Sum(Sales), 0, RowNo())) maps to a cumulative DAX measure, a WINDOW function, or the RUNNINGSUM visual calculation.
Partial / different approach
Above(Sum(Sales))
Above, Below, Before and After
Above() reads the value from the previous row of the chart. Power BI offers three replacements: the OFFSET window function, the PREVIOUS visual calculation, and calendar-aware time intelligence. Each behaves differently when periods are missing.
Partial / different approach
Avg(Aggr(Sum(Sales), Customer))
Aggr (nested aggregation)
Aggr builds a virtual table of aggregated values, for example sales per customer, so you can aggregate again. In DAX that is an iterator (AVERAGEX, MAXX, COUNTROWS(FILTER(...))) over VALUES or SUMMARIZE that calls a measure.
Partial / different approach
Spotted an error or a better pattern? QlikToDax is maintained by a working BI consultant; corrections are welcome via the About page. Last reviewed 2026-10-25.