Register now to learn Fabric in free live sessions led by the best Microsoft experts. From Apr 16 to May 9, in English and Spanish.
Hi Community
This one has baffled me for a while now.
I'm trying to come up with the right method and DAX for calculating the following:
I have a date table at the ready, and all of my Sales Data is in one table. So the model is very simple. I'm using SSAS Tabular Live Connection, so any changes would be back end.
Any other posts I've found online are looking at number of customers, and aren't specific to 'Item and Customer' combination.
Appreciate your help.
Thanks
Martin
Solved! Go to Solution.
// Assumptions:
// 1. Dates table marked as the Data table in the model.
// 2. Sales as the fact table.
// 3. Customers as a dimension.
// 4. Products as a dimension.
// You should always, without exceptions, create correct
// dimensional models. Only then will you be sure that
// your measures work correctly in all circumstances.
// You don't aim at a model that stores everything in one
// table. Rather, you aim at a proper dimensional design.
// 1) New Business - Total sales for unique visible combinations
// (CustomerId, ProductId) such that the product sells for
// the first time in a rolling 12-month period, meaning the
// combination is not present in the past beyond the period,
// whatever the past contains (might be empty).
// 2) Returning Business - Total sales in 24 months for unique
// combinations (CustomerId, ProductId) that are present
// also in the 12-month period preceding the 24 months.
// The 12-month period must be present in the calendar in full.
// 3) Lost Business - Total sales for the unique visible combinations
// (CustomerId, ProductId) which are not present in the past 12
// months but are present in the prior 12 months. The prior 12 months
// must be present in the calendar.
//
// Let's assume that there is a measure that returns the total sales
// for any slicing:
[Total Sales] = SUM( Sales[Amount] )
// Based on the above:
[New Business] =
var __lastVisibleDate = MAX( Dates[Date] )
var __rolling12Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-12,
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 12 months,
// not fewer. It might not be 12 months
// if we are too close to the left end of
// the calendar.
CALCULATE(
DISTINCTCOUNT( Dates[MonthID] ) = 12,
__rolling12Months
)
var __result =
if( __shouldCalculate,
var __periodPriorTo12Months =
EXCEPT(
FILTER(
ALL( Dates[Date] ),
Dates[Date] <= __lastVisibleDate
),
__rolling12Months
)
var __customerProductIn12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling12Months
)
var __customerProductPriorTo12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo12Months
)
var __effectiveCustomerProduct =
EXCEPT(
__combinationsIn12Months,
__combinationsPriorTo12Months
)
return
// You have to decide what you want
// to calculate here for the set
// of pairs (CustomerId, ProductId).
// Especially, what period of time
// you want... I assume you want
// the 12-month rolling period.
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
__rolling12Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
// I'll post the other measures when I get
// a chance to write them... Stay tuned!
[Returning Business] =
var __lastVisibleDate = LASTDATE( Dates[Date] )
var __rolling24Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-24,
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 24 + 12 months,
// not fewer.
MAX(
NEXTDAY( DATEADD( Dates[Date], -(24 + 12), MONTH ) ),
DATEADD( NEXTDAY( Dates[Date] ), -(24 + 12), MONTH ))
) > BLANK()
var __result =
if( __shouldCalculate,
var __periodPriorTo24Months =
EXCEPT(
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(24 + 12),
MONTH
)
__rolling24Months
)
var __customerProductIn24Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling24Months
)
var __customerProductPriorTo24Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo24Months
)
var __effectiveCustomerProduct =
INTERSECT(
__customerProductIn24Months,
__customerProductPriorTo24Months
)
return
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
__rolling24Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
// 3) Lost Business - Total sales for the unique visible combinations
// (CustomerId, ProductId) which are not present in the past 12
// months but are present in the prior 12 months. The prior 12 months
// must be present in the calendar.
[Lost Business] =
var __lastVisibleDate = LASTDATE( Dates[Date] )
var __rolling12Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(12),
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 24 + 12 months,
// not fewer.
MAX(
NEXTDAY( DATEADD( Dates[Date], -(12 + 12), MONTH ) ),
DATEADD( NEXTDAY( Dates[Date] ), -(12 + 12), MONTH ))
) > BLANK()
var __result =
if( __shouldCalculate,
var __periodPriorTo12Months =
EXCEPT(
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(12 + 12),
MONTH
)
__rolling12Months
)
var __customerProductIn12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling12Months
)
var __customerProductPriorTo12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo12Months
)
var __effectiveCustomerProduct =
EXCEPT(
__customerProductPriorTo12Months,
__customerProductIn12Months
)
return
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
// If we filter by the last 12 months,
// we'll obviously get BLANK as these
// combinations do not exist in the
// last 12 months. We then need to use
// the 12-month period before the rolling
// one.
__periodPriorTo12Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
// 3) Lost Business - Total sales for the unique visible combinations
// (CustomerId, ProductId) which are not present in the past 12
// months but are present in the prior 12 months. The prior 12 months
// must be present in the calendar.
[Lost Business] =
var __lastVisibleDate = LASTDATE( Dates[Date] )
var __rolling12Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(12),
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 24 + 12 months,
// not fewer.
MAX(
NEXTDAY( DATEADD( Dates[Date], -(12 + 12), MONTH ) ),
DATEADD( NEXTDAY( Dates[Date] ), -(12 + 12), MONTH ))
) > BLANK()
var __result =
if( __shouldCalculate,
var __periodPriorTo12Months =
EXCEPT(
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(12 + 12),
MONTH
)
__rolling12Months
)
var __customerProductIn12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling12Months
)
var __customerProductPriorTo12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo12Months
)
var __effectiveCustomerProduct =
EXCEPT(
__customerProductPriorTo12Months,
__customerProductIn12Months
)
return
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
// If we filter by the last 12 months,
// we'll obviously get BLANK as these
// combinations do not exist in the
// last 12 months. We then need to use
// the 12-month period before the rolling
// one.
__periodPriorTo12Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
@Anonymous
This is why I'm thankful to the PBI community here. Thank you so much.
Just one thing: I've implemented the measures and with a few tweaks, works perfectly. Except one thing I overlooked.
The Returning Business was wrong in my original spec. I was imagining that I'd require 24 months in my measure but I actually just need 12 months. It's the lost business that would need to look in the 12-24 month period to work out the sum of sales lost.
How would I adapt the measure for Returning Business to a 12 month period?
The Shouldcalculate variable confuses me slightly, var __shouldCalculate =
MAX(
NEXTDAY( DATEADD( 'Primary Date Table'[Date], -(24 + 12), MONTH ) ),
DATEADD( NEXTDAY( 'Primary Date Table'[Date]), -(24 + 12), MONTH ))
> BLANK()
Why would I need to add 24 + 12? Would I need to in this case?
Thanks in advance.
Thanks for your response.
So for the new definition:
Returning Business=
Total Sales Amount in 12 months on Item & Customer combination that has ordered in the previous 12 months and has in the prior 12 months.
Returning Business:=
var __lastVisibleDate = LASTDATE( 'Primary Date Table'[Date] )
var __rolling12Months = //Changed from __rolling24Months
DATESINPERIOD(
'Primary Date Table'[Date],
__lastVisibleDate,
-12, //Changed from -24
MONTH
)
var __shouldCalculate =
MAX(
NEXTDAY( DATEADD( 'Primary Date Table'[Date], -(24 + 12), MONTH ) ),
DATEADD( NEXTDAY( 'Primary Date Table'[Date]), -(24 + 12), MONTH ))
> BLANK()
var __result =
if( __shouldCalculate,
var __periodPriorTo24Months =
EXCEPT(
DATESINPERIOD(
'Primary Date Table'[Date],
__lastVisibleDate,
-(24 + 12),
MONTH
),
__rolling24Months
)
var __customerProductIn24Months =
CALCULATETABLE(
SUMMARIZE(
'Sales Flash',
Customer[CustomerID],
'Item'[ItemID]
),
__rolling24Months
)
var __customerProductPriorTo24Months =
CALCULATETABLE(
SUMMARIZE(
'Sales Flash',
Customer[CustomerID],
'Item'[ItemID]
),
__periodPriorTo24Months
)
var __effectiveCustomerProduct =
INTERSECT(
__customerProductIn24Months,
__customerProductPriorTo24Months
)
return
CALCULATE(
[Total Sales (R)],
__effectiveCustomerProduct,
__rolling12Months,//Changed from __rolling24Months
ALL( 'Customer' ),
AlL( 'Item' ),
ALL( 'Primary Date Table' )
)
)
return
__result
Hi @Anonymous
Your help here to identify new, returning and lost Item+Customer sales has been so powerful.
I've now been asked to help apply the same logic to just the customers, without including the item in the calcuation.
So instead of New Customers+Item, Returning Customer+Item, or Lost Customer+Item, it'll be just customer counts with the same logic around the date periods.
I tried to modify your code here but SUMMARIZE function springs an error on Argument 3.
Your help is very appreciated.
Thanks
@Anonymous Thank you Daxer, this is where I get the Summarize error when removing the ItemNo.
Are you saying that you get an error from:
SUMMARIZE(
'Sales Flash',
Customer[CustomerID]
)
If you do (but I seriously doubt it), then 'Sales Flash' is just not connected via *:1 to Customer.
You were right. I missed something. Thank you
@Anonymous , refer if this can help
new vs repeat customer
https://community.powerbi.com/t5/Desktop/Churn-Rate-lost-Customer/m-p/1173754#M529196
https://blog.enterprisedna.co/new-vs-existing-customers-advanced-analytics-w-dax/
https://www.sqlbi.com/articles/computing-new-customers-in-dax/
HI @Anonymous ,
See if this helps.
https://www.youtube.com/watch?v=GK-W25RM87Q
Regrads,
Harsh Nathani
Hi, thanks, yes this is also something I've already looked at and what I'm really looking for is Sales value for item+customer combination.
// Assumptions:
// 1. Dates table marked as the Data table in the model.
// 2. Sales as the fact table.
// 3. Customers as a dimension.
// 4. Products as a dimension.
// You should always, without exceptions, create correct
// dimensional models. Only then will you be sure that
// your measures work correctly in all circumstances.
// You don't aim at a model that stores everything in one
// table. Rather, you aim at a proper dimensional design.
// 1) New Business - Total sales for unique visible combinations
// (CustomerId, ProductId) such that the product sells for
// the first time in a rolling 12-month period, meaning the
// combination is not present in the past beyond the period,
// whatever the past contains (might be empty).
// 2) Returning Business - Total sales in 24 months for unique
// combinations (CustomerId, ProductId) that are present
// also in the 12-month period preceding the 24 months.
// The 12-month period must be present in the calendar in full.
// 3) Lost Business - Total sales for the unique visible combinations
// (CustomerId, ProductId) which are not present in the past 12
// months but are present in the prior 12 months. The prior 12 months
// must be present in the calendar.
//
// Let's assume that there is a measure that returns the total sales
// for any slicing:
[Total Sales] = SUM( Sales[Amount] )
// Based on the above:
[New Business] =
var __lastVisibleDate = MAX( Dates[Date] )
var __rolling12Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-12,
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 12 months,
// not fewer. It might not be 12 months
// if we are too close to the left end of
// the calendar.
CALCULATE(
DISTINCTCOUNT( Dates[MonthID] ) = 12,
__rolling12Months
)
var __result =
if( __shouldCalculate,
var __periodPriorTo12Months =
EXCEPT(
FILTER(
ALL( Dates[Date] ),
Dates[Date] <= __lastVisibleDate
),
__rolling12Months
)
var __customerProductIn12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling12Months
)
var __customerProductPriorTo12Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo12Months
)
var __effectiveCustomerProduct =
EXCEPT(
__combinationsIn12Months,
__combinationsPriorTo12Months
)
return
// You have to decide what you want
// to calculate here for the set
// of pairs (CustomerId, ProductId).
// Especially, what period of time
// you want... I assume you want
// the 12-month rolling period.
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
__rolling12Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
// I'll post the other measures when I get
// a chance to write them... Stay tuned!
Of course, this condition
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 12 months,
// not fewer. It might not be 12 months
// if we are too close to the left end of
// the calendar.
CALCULATE(
DISTINCTCOUNT( Dates[MonthID] ) = 12,
__rolling12Months
)
works correctly only for the days that are the ends of months. The beginning of the code should be changed to this
[New Business] =
var __lastVisibleDate = LASTDATE( Dates[Date] ) // LINE CHANGED
var __rolling12Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-12,
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 12 months,
// not fewer. It might not be 12 months
// if we are too close to the left end of
// the calendar.
MAX(
NEXTDAY( DATEADD( Dates[Date], -1, YEAR ) ),
DATEADD( NEXTDAY( Dates[Date] ), -1, YEAR ))
) <> BLANK()
var __result = ... // same stuff as above
WOW! I'm blown away by this. It worked!
A few changes to __customerProductIn12Months,
__customerProductPriorTo12Months and linking to my dim customer and dim products table and we're good!
Thank you so much!
Can't wait for the rest.
[Returning Business] =
var __lastVisibleDate = LASTDATE( Dates[Date] )
var __rolling24Months =
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-24,
MONTH
)
var __shouldCalculate =
// We have to make sure that the rolling
// period does indeed have 24 + 12 months,
// not fewer.
MAX(
NEXTDAY( DATEADD( Dates[Date], -(24 + 12), MONTH ) ),
DATEADD( NEXTDAY( Dates[Date] ), -(24 + 12), MONTH ))
) > BLANK()
var __result =
if( __shouldCalculate,
var __periodPriorTo24Months =
EXCEPT(
DATESINPERIOD(
Dates[Date],
__lastVisibleDate,
-(24 + 12),
MONTH
)
__rolling24Months
)
var __customerProductIn24Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__rolling24Months
)
var __customerProductPriorTo24Months =
CALCULATETABLE(
SUMMARIZE(
Sales,
Customers[CustomerID],
Products[ProductID]
),
__periodPriorTo24Months
)
var __effectiveCustomerProduct =
INTERSECT(
__customerProductIn24Months,
__customerProductPriorTo24Months
)
return
CALCULATE(
[Total Sales],
__effectiveCustomerProduct,
__rolling24Months,
ALL( Customers ),
AlL( Products ),
ALL( Dates )
)
)
return
__result
Thank you, but these are the posts I was refering to as not specific to Customer+Item, and using Sales Amount with the logic I've mentioned above.
Covering the world! 9:00-10:30 AM Sydney, 4:00-5:30 PM CET (Paris/Berlin), 7:00-8:30 PM Mexico City
Check out the April 2024 Power BI update to learn about new features.
User | Count |
---|---|
41 | |
21 | |
21 | |
14 | |
13 |
User | Count |
---|---|
43 | |
32 | |
30 | |
18 | |
18 |