How can I calculate a rolling average in DAX/PowerBI? - powerbi

After going through several posts on StackOverflow and the PowerBI forums, I still can't figure out how to calculate a rolling average based on a given period- in my case, a 30-day rolling average.
Most of the posts I've seen advocate something either identical or really similar to this:
Rolling Sum :=
CALCULATE (
[Sales],
FILTER (
ALL ( Sales ),
[Date]
>= MAX ( Sales[Date] ) - 365
&& [Date] <= MAX ( Sales[Date] )
)
)
(code taken from this post)
...and yet, I can't seem to get the proper values.
In my case, I have the following:
"closing date" for a given loan (column)
loan count (measure)
closing length (column)- length of time (in days) to close a loan
What I'd like to calculate is the rolling 30 day average for any given day. I coded the following:
Rolling Average =
CALCULATE (
SUM(Query1[Closing_Length])/[Loan Count],
FILTER (
ALL ( Query1 ),
[Closing Date].[Date]
>= MAX ( Query1[Closing Date] ) - 30
&& [Closing Date] <= MAX ( Query1[Closing Date] )
)
)
To check the results, I used a visual filter to examine one month's worth of data and these are the results:
Note the totals row at the bottom; for this given period, there are 102 loans and it took an aggregate of 3922 days for them to close. The average I'd like to calculate is 3922/102, which should equal approximately 38.45 days. Instead, we see 42.
How can I fix this?

Measure based solution:
Rolling Average Measure =
VAR A =
SUMX (
FILTER (
ALL ( 'Query' ),
'Query'[Closing Date] <= MAX ( 'Query'[Closing Date] )
),
ROUND ( 'Query'[Closing Length], 2 )
)
VAR B =
SUMX (
FILTER (
ALL ( 'Query' ),
'Query'[Closing Date] <= MAX ( 'Query'[Closing Date] )
),
ROUND ( 'Query'[Loan Count], 2 )
)
RETURN
A / B
Calculated column based solution:
Rolling Average =
VAR CurrentDate = 'Query'[Closing Date]
VAR T =
FILTER ( 'Query', [Closing Date] <= CurrentDate )
RETURN
ROUND ( SUMX ( T, 'Query'[Closing Length] ) / SUMX ( T, [Loan Count] ), 2 )
Print Screen:

Related

Cumulative sum by months in Powerbi DAX

I want to show the cumulative sum per month, I have the number per month but need to show the sum up of previous months in each month..
I used the following measure:
RT FAC =
CALCULATE (
CALCULATE(SUM('Injuries'[Total]), 'Injuries'[Classification] = "FAC"),
FILTER(ALLSELECTED('Calendar Table'),
'Calendar Table'[Date]<= 'Calendar Table'[Date]))
But it gave me the total in all the months:
How can I show the running total such as:
What IF you try this with small change?
RT FAC =
VAR MaxDate =
MAX ( 'Calendar Table'[Date] )
RETURN
CALCULATE (
SUM ( 'Injuries'[Total] ),
FILTER (
ALL ( 'Injuries'[Classification] ),
'Injuries'[Classification] = "FAC"
),
FILTER ( ALLSELECTED ( 'Calendar Table' ), 'Calendar Table'[Date] <= MaxDate )
)

count distinct value filtered based on date diff measure

I have the following dataset
user_id, login_date
111, 01/02/2021
222, 02/15/2021
444, 02/20/2021
555, 01/15/2021
222, 03/10/2021
444, 03/11/2021
I want to count of the number of unique active user_id in the last 90 days based on the max date of my date slicer. I'd like to solve this without using filters. This also needs to be dynamic as max date can be change from the date slicer.
From what I have understand so far I will need to evaluate for each row if the date difference between the current date of the row and the max date of the slicer is less than 90 days. Then for all the rows where the date diff is less than 90 days I will want to count the distinct number of users.
so basically I will have three layer in my final formula
evaluate the date diff.
filter out rows where the date diff is superior to 90 days.
count the distinct users in the remaining rows.
I've tried many approach and formula. I think that this one is closed to something that could work:
Measure test = CALCULATE(SUMX(DISTINCT(mytable[user_id]),filter(mytable,DATEDIFF(SELECTEDVALUE(mytable[login_date]),[Max range date],DAY)>90)))
this formula return me the following error :
The expression refers to multiple columns. Multiple columns cannot be converted to a scalar value.
I've also tried applying a if statement to output the date diff as 0 and 1 and hopefully being able to sum this for each unique id with something like this
SUMX( VALUES(my_table[user_id]), IF(DATEDIFF(SELECTEDVALUE(mytable[login_date]),[Max range date],DAY)>90,0,1)
anyway I'm kind of stuck. hopefully my question is clear enough.
You will have to create a disconnected table from which you will use the date column in the slicer, I have prepared a power BI file which included 2 very common scenarios, I hope that helps you.
File - Simon.pbix
Screenshot of the report - https://ibb.co/xjrjVv5
Screenshot of the model - https://ibb.co/Ws1z8D7
DAX Code -
for simon =
IF (
ISINSCOPE ( simon[Login Date] )
|| ISINSCOPE ( simon[User ID] ),
VAR LastVisibleDate =
CALCULATE (
MAX ( 'Simon Date Table'[Login Date] ),
ALLSELECTED ( 'Simon Date Table' )
)
VAR CurrentDate =
MAX ( simon[Login Date] )
VAR TimeJump = 90
VAR Result =
CALCULATE (
DISTINCTCOUNT ( simon[User ID] ),
simon[Login Date] <= LastVisibleDate,
simon[Login Date] > LastVisibleDate - TimeJump,
ALLSELECTED ( simon )
)
RETURN
Result
)
second version:
for simon 2 =
IF (
ISINSCOPE ( simon[Login Date] )
|| ISINSCOPE ( simon[User ID] ),
VAR LastVisibleDate =
CALCULATE (
MAX ( 'Simon Date Table'[Login Date] ),
ALLSELECTED ( 'Simon Date Table' )
)
VAR CurrentDate =
MAX ( simon[Login Date] )
VAR TimeJump = 90
VAR Result =
IF (
CurrentDate <= LastVisibleDate,
CALCULATE (
DISTINCTCOUNT ( simon[User ID] ),
simon[Login Date] <= CurrentDate,
simon[Login Date] > CurrentDate - TimeJump,
ALLSELECTED ( simon )
)
)
RETURN
Result
)

Running total of a measure which use DATESINPERIOD and months from fact table

I am using a measure below to display the months from fact table as described here:
Billings12Months =
CALCULATE (
SUM ( 'Datatable'[Allowable] ),
DATESINPERIOD ( DimDate[Date], MIN ( DimDate[Date] ), +12, MONTH )
)
My attempt to get the running total of above measure is failing:
BillingsRunningTotal =
CALCULATE (
[Billings12Months],
FILTER ( ALLSELECTED ( DimDate ), DimDate[Date] <= MAX ( DimDate[Date] ) )
)
BillingsRunningTotal2 =
SUMX (
FILTER (
ALLSELECTED ( DimDate[Date] ),
DimDate[Date] <= MAX ( ( DimDate[Date] ) )
&& YEAR ( DimDate[Date] ) = YEAR ( MIN ( DimDate[Date] ) )
),
[Billings12Months]
)
[BillingsRunningTotal] return same values as [Billings12Months] (please see screen 1 attached) and
[BillingsRunningTotal2] return wrong values and month start from Jan, 17 instead of May, 17 (please see screen-2)
Please help me to calculate the running total. If possible please describe how your solution is working so that I can be better in DAX.
Update:
Please see the screen-3 below for the output when I use the measure suggested by Kosuke:
BillingsRunningTotal =
CALCULATE (
SUM ( Datatable[Allowable] ),
FILTER ( ALLSELECTED ( DimDate ), DimDate[Date] <= MAX ( DimDate[Date] ) )
)
The months are from fact table (not from a Date table) and I think DATESINPERIOD plays a role to calculate and display the months. When we use SUM ( Datatable[Allowable] ), there would be a single month as dictated by the slicer. So we need to use DATESINPERIOD with rolling month calculation logic (DimDate[Date] <= MAX ( DimDate[Date] )) or virtually sum the [Billings12Months], It is where I am failing.
Thanks
You are almost there with the first attempt, however what to calculate is not [Billings12Months], but SUM( Datatable[Allowable] ).
BillingsRunningTotal =
CALCULATE (
SUM ( Datatable[Allowable] ),
FILTER ( ALLSELECTED ( DimDate ), DimDate[Date] <= MAX ( DimDate[Date] ) )
)
Essentially, [Billings12Months] and [BillingsRunningTotal] are same in calculating the sum of Datatable[Allowable], but the only difference is each measure calculates for different scope of period. Therefore, the right way of thinking is to wrap SUM ( Datatable[Allowable] ) in CALCULATE, with different filter parameters.

DAX Measure to Calculate Percentage of Customers with New Purchases Within One Year Sliding Window

I need a measure that will give me the percentage of customers during a calendar year who had new purchases within one year of their last purchase.
Normally I would just create a calculated column that captured the date of each customer's last purchase for each year, and then check to see if each customer from a given year had any purchases within a year of their respective last date, and then sum those up, but I'm using a live connection to a cube and can't create calculated columns.
Here's some pseudo-code of what I'm looking for:
One Year Return =
VAR Cohort =
SUMMARIZECOLUMNS (
Customer[ID],
FILTER (
VALUES ( Sales[Sales Date] ),
YEAR ( Sales[Sales Date] )
< ( YEAR ( TODAY () ) - 1 )
)
)
VAR Returners =
SUMMARIZECOLUMNS (
Customer[ID],
FILTER (
VALUES ( Sales[Sales Date] ),
Sales[Sales Date] > Sales[Old Sales Date] //<--Need help here
),
FILTER (
VALUES ( Customer[ID] ),
Customer[ID] IN Cohort
)
)
VAR Rate =
CALCULATE ( DISTINCTCOUNT ( Customer[ID] ), Customer[ID] IN Returners ) /
CALCULATE ( DISTINCTCOUNT ( Customer[ID] ), Customer[ID] IN Cohort )
RETURN
Rate
The main difficulty is that I need a different time window for each customer, but can't make a calculated column. I've been looking at using EARLIER or DATESBETWEEN or PARALLELPERIOD, but haven't been able to get any of them to work so far.
First I think you are looking for this
VAR last_year = DATE ( YEAR ( TODAY () ) - 1; MONTH ( TODAY () ); DAY ( TODAY () ) )
Second. The best way to get this done you need a bit of data modeling. The only way to make time intelligence work is to have a Date table. This table should have a row by day without missing days. You can search online how to create one or check my blog here.
Once you have this table releated on your model with your fact table you will be able to build that measure on a selected date. The selected one will be a value and last year of selected value the other one.
The crux is in the fact that you need to compare for each customer without a calculated column. It is possible by using the an iterator like COUNTX or SUMX. This enables you to loop through your customers in the cohort sub-table and create selections of your sales table by using the customer ID in the current iteration to filter your sales table using EARLIER. For each customer in the cohort you then select their last purchase date and the one before that and compare the two to see if they followed within the year.
In DAX it looks a bit complex but I tried to make it spacier so it is easier to follow. It also contains a little workaround for the fact that you cannot simply loop over the _cohort variable as you won't have access to the customer ID in the current iteration in that case
One year return =
var _now = TODAY()
var _cohort =
SUMMARIZECOLUMNS (
Sales[CustomerID] ;
FILTER (
Sales ;
DATEDIFF( Sales[SalesDate] ; _now ; YEAR ) <= 1
)
)
var _countCohort = COUNTROWS( _cohort )
var _countReturns =
SUMX (
SUMMARIZECOLUMNS (
Sales[CustomerID] ; FILTER ( Sales ; Sales[CustomerID] IN _cohort )
) ;
var _lastPurchase =
CALCULATE (
MAX ( Sales[SalesDate] ) ;
ALLSELECTED ( Sales ) ;
Sales[CustomerID] = EARLIER( Sales[CustomerID] )
)
var _preLastPurchase =
CALCULATE (
MAX ( Sales[SalesDate] ) ;
ALLSELECTED ( Sales ) ;
Sales[SalesDate] < _lastPurchase ;
Sales[CustomerID] = EARLIER( Sales[CustomerID] )
)
RETURN
IF ( DATEDIFF( _preLastPurchase ; _lastPurchase ; YEAR ) <= 1 ; 1 ; 0 )
)
RETURN
_countReturns / _countCohort
I ended up importing a dataset from the cube and creating calculated columns to get me there after all. #jelle-hoekstra's answer looks close to what I ended up doing, but I couldn't figure out how to get his measure to run.
I made three calculated columns and did a distinct count on the ReturnCustomerID column to get the number of customers who returned within one year of their last purchase:
Last Purchase Date =
MINX (
FILTER (
Sales,
Sales[Sales Year]+1 = EARLIER ( Sales[Sales Year] ) && Sales[CustomerID] = EARLIER ( Sales[CustomerID] )
),
MAXX (
FILTER (
Sales,
Sales[Sales Year] = EARLIER ( Sales[Sales Year] ) && Sales[CustomerID] = EARLIER ( Sales[CustomerID] )
),
Sales[Sales Date]
)
)
One Year Date =
MINX (
FILTER (
Sales,
Sales[Sales Year]+1 = EARLIER ( Sales[Sales Year] ) && Sales[CustomerID] = EARLIER ( Sales[CustomerID] )
),
MAXX (
FILTER (
Sales,
Sales[Sales Year] = EARLIER ( Sales[Sales Year] ) && Sales[CustomerID] = EARLIER ( Sales[CustomerID] )
),
EDATE (Sales[Sales Date], 12 )
)
)
ReturnCustomerID =
IF (
Sales[Sales Date] > Sales[Last Purchase Date] && Sales[Sales Date] < Sales[One Year Date], Sales[CustomerID], BLANK()
)

DAX - Calculate sum of the last 365 days

I want to calculate the sum of sales of the last 365 days.
The following function works very well if there is a day filter in the visual (chart, table, etc.).
But - and this is where the problem lies - if there's not a day filter, but a month for example, then the measure returns the sales of the last 12 months.
So, today (9.11.2017), I should get the sum of the sales from 9.11.2016 until 8.11.2017 - regardless of what filter is applied.
And not the sum of the sales from 1.12.2016 until 30.11.2017.
SalesTTM =
IF (
TODAY () >= FIRSTDATE ( 'calendar'[date] );
CALCULATE (
'order'[Sales];
DATESBETWEEN (
'calendar'[date];
NEXTDAY ( SAMEPERIODLASTYEAR ( LASTDATE ( 'calendar'[date] ) ) );
LASTDATE ( 'calendar'[date] )
)
)
)
In the DATESBETWEEN function above I can't find a way to express "tomorrow less one year" until "today".
How can I do that?
You need to put something in there to ignore the filtering on the month. Try inserting ALL('calendar'[date]); before your DATESBETWEEN ( line.
SalesTTM =
VAR LastDate = LASTDATE ( 'calendar'[date] )
RETURN IF (
TODAY () >= FIRSTDATE ( 'calendar'[date] );
CALCULATE (
'order'[Sales];
ALL('calendar'[date]);
DATESBETWEEN (
'calendar'[date];
NEXTDAY ( SAMEPERIODLASTYEAR ( LastDate ) );
LastDate
)
)
)