Dynamic Group with a twist Part 2

Harsh has helped me out greatly with my problem and I now have each customer categorised by year/qtr/month (see attached

The next part is to get the Totals for each period into a card so I can chart it. Ideally in a waterfall chart like this for each year


waterfall

but any ability to chart the totals for each category would be great.

The chart would always be filtered by Year and the Totals for each category would be the category YTD so if I was charting with a report date of 30th June 2022 the YoY Diff would be comparing Jan-June 22 v Jan-June 21

2021 Base would be the Total Sales for 2021

Each Category would be the Total YoY Difference YTD

If I was running this for 2021 then 2020 Base (Total Sales 2020) would be used as the starting point

customeranalysis - Part2 - Copy.pbix (446.8 KB)

Hello @AliB,

Thank You for posting your query onto the Forum.

The results that you’re trying to analyze based on the solution which I had provided in the previous post is not applicable in the current scenario. The reason being is, you’re trying to use the results of “Measures” from previous scenario as “Fields” in the current scenario which cannot happen.

Power BI doesn’t allow to drag the “Measures” into the “Fields or Dimensions” section. And therefore, in order to achieve the results based on the condition that you’re specifying, you’ll be required to perform the transformations inside the Power Query thereby allotting the criteria’s to each and every row level which in turn can be used as a field. Below is the M Code alongwith the screenshot of the final results provided for the reference -

let
    Source = Excel.Workbook(File.Contents("HBS:\Forum\Quber\Doc\DataModel - Copy.xlsx"), null, true),
    #"Sales Data_Sheet" = Source{[Item="Sales Data",Kind="Sheet"]}[Data],
    #"Promoted Headers" = Table.PromoteHeaders(#"Sales Data_Sheet", [PromoteAllScalars=true]),
    #"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",{{"Group", Int64.Type}, {"Name", type text}, {"SalesDate", type date}, {"Sales", type number}}),
    #"Grouped Rows" = Table.Group(#"Changed Type", {"Group"}, {{"FullTable", each _, type table [Group=nullable text, Name=nullable text, SalesDate=nullable date, Sales=nullable number]}}),
    #"Added Custom" = Table.AddColumn(#"Grouped Rows", "Custom", each 

    let
        AllDataTable = [FullTable],

        Prev = 
        Table.AddColumn(
            Table.AddColumn(
                Table.AddColumn(AllDataTable, "Month & Year", each Date.ToText([SalesDate], "MMM yyyy")), 
            "PreviousSalesDate", each Date.AddYears([SalesDate], -1)), 
        "Previous Month & Year", each Date.ToText([PreviousSalesDate], "MMM yyyy"), type text), 

        NestedJoin = 
        Table.NestedJoin(Prev, {"Group", "Previous Month & Year"}, Prev, {"Group", "Month & Year"}, "AddedColumn", JoinKind.LeftOuter)

    in
        NestedJoin),

    Custom1 = Table.Combine(#"Added Custom"[Custom], {"Group", "Name", "SalesDate", "Sales", "AddedColumn"}),
    #"Expanded AddedColumn" = Table.ExpandTableColumn(Custom1, "AddedColumn", {"Sales"}, {"Previous Sales"}),
    #"Sorted Rows" = Table.Sort(#"Expanded AddedColumn",{{"Group", Order.Ascending}, {"SalesDate", Order.Ascending}}),
    #"Merged Queries" = Table.NestedJoin(#"Sorted Rows", {"Group"}, Customers, {"Group"}, "Customers", JoinKind.LeftOuter),
    #"Expanded Customers" = Table.ExpandTableColumn(#"Merged Queries", "Customers", {"OnboardingDate", "End Date"}, {"OnboardingDate", "End Date"}),
    #"Grouped Rows1" = Table.Group(#"Expanded Customers", {"Group"}, {{"FullTable", each _, type table [Group=nullable number, Name=nullable text, SalesDate=nullable date, Sales=nullable number, Previous Sales=nullable number, OnboardingDate=nullable date, End Date=nullable date]}}),
    Custom2 = Table.TransformColumns(#"Grouped Rows1", {{"FullTable", each Table.AddIndexColumn(_, "Index")}}),
    #"Added Custom1" = Table.AddColumn(Custom2, "Custom", each 
    
    let 
        AllDataTable = [FullTable], 

        Prev = 
        Table.AddColumn(
            Table.AddColumn(AllDataTable, "Status", each 
                if Date.Year([SalesDate]) = Date.Year([OnboardingDate]) and Date.Year([SalesDate]) = Date.Year([End Date]) then "Lost" 
                else if Date.Year([SalesDate]) = Date.Year([OnboardingDate]) then "Won" 
                else if Date.ToText([SalesDate], "MMM yyyy") = Date.ToText([End Date], "MMM yyyy") then "Lost" else null), 
            "Date", each 
            if Date.ToText([SalesDate], "MMM yyyy") = Date.ToText([OnboardingDate], "MMM yyyy") then [OnboardingDate] 
            else if Date.ToText([SalesDate], "MMM yyyy") = Date.ToText([End Date], "MMM yyyy") then [End Date] else [SalesDate]),

        LostCalc = 
        Table.AddColumn( Prev, "Lost Calc", each
            if Date.ToText([SalesDate], "yyyy") = Date.ToText([End Date], "yyyy") then "Lost" else [Status]),

        PrevStatus = 
        Table.AddColumn(
            Table.FillDown(
                Table.AddColumn(
                    Table.AddColumn(
                        Table.AddColumn(LostCalc, "Prev Status", each 
                            try LostCalc [Lost Calc] {[Index]-1} otherwise null), 
                        "Not A Customer Status", each if [Lost Calc] = null and [Prev Status] = "Lost" then "Not A Customer" else [Lost Calc]), 
                    "Abc", each 
                    try if ([Sales] - [Previous Sales]) <= 0 then "Shrinkage" 
                    else if ([Sales] - [Previous Sales]) > 0 then "Growth" else null otherwise null), 
                {"Not A Customer Status"}), 
            "Customer Status", each if [Abc] = null and [Not A Customer Status] = "Won" then "Won"
            else if [Abc] = null and [Not A Customer Status] = "Lost" then "Lost"
            else if [Not A Customer Status] = "Lost" then "Lost" 
            else if [Not A Customer Status] = "Not A Customer" then "Not A Customer"
            else [Abc])

    in 
        PrevStatus),

    Custom3 = 
    Table.TransformColumnTypes(
        Table.Combine(#"Added Custom1"[Custom], {"Group", "Name", "Date", "Sales", "Customer Status"}), 
    {{"Group", type number}, {"Name", type text}, {"Date", type date}, {"Sales", type number}, {"Customer Status", type text}})
in
    Custom3

After performing this level of transformation, this is how the Sales Table will look like with the additional column of “Customer Status” -

Once the transformation is done then you can create Monthly or YTD measures as per the requirements to analyze the results, as desired. Below is the screenshot of the final results provided -

I’m also attaching the working of the PBIX file as well as the link of the courses on M language or Power Query for the reference purposes.

Hoping you find this useful and meets your requirements that you’ve been looking for.

Important Notes:

1). Before you proceed ahead with the above M code or transformations, please merge the Customers and Leave table information. I’ve brought the End Date from Leavers table inside the Customers table.

2). I’ve created couple of base measures to check or cross-verify the results. You can go ahead and create YTD or any measures as per your requirements.

3). The solution is provided based on the original query. In case of additional requirements, please modify the code as per the business scenario’s.

Thanks and Warm Regards,
Harsh

Data Model - Copy - Harsh.xlsx (1.3 MB)

Dynamic Grouping - Power Query Transformations - Harsh.pbix (415.2 KB)

2 Likes

Hi @AliB

Hope You doing good , This one sounds interesting problem , I have solved this one before but from Data warehouse perspective

I have tried this one from DAX perspective , just to see if its possible ( Just me trying dax stuff :slight_smile: )

I have attached the same pbix file

Basically I have created won , lost, Shrinkage etc …measures separately with their own logic ( Hidden measures )
And then use switch statement on selected value of category to show value of measures based on selected category (Also a hidden measure called categorysales)

Fixed a total in matrix view ( measure name :- categorysalestotal)
You can refer Edna video below for the same

The data model is very small though to test the performance, if you have large model and transactions you probably need to fine tune the individual measures and optimize them or else move the Status Tag in M Query as Harsh Shared
DataModel - Copy.xlsx (1.3 MB)

Let me know if that helps
customeranalysis - Solving (Puneet ).pbix (431.1 KB)

1 Like

Thanks Harsh. I’ll let you know if it works when it finishes running… 3hrs and counting Would you expect it to be taking this long - only 124000 sales rows

Thanks ppunvij
I’ll give it a go soon

I’d be interested to know how you solved it from a DW perspective as based on how long the power query version is taking I don’t think its a viable option. Surprising how a simple SumIF in excel takes so much processing power in PowerBI.

Hi @AliB 124K Sales Transaction rows doesnt sound a big Number to be honest , Let me know if DAX solution works for the same

From DWH prespective we marked the sales in to 4 categories in Fact sales , based on transaction date and customer onboarding date/ leaving date i.e if that is new sale , Current Customer , Not a customer or Lost Customer , these catergories were based on when the transaction happens and when customer was on-boarded or left
As fact table consist of historical granularity which can be used for the same category grouping

The growth and shinkage can be done dax level to see when yoy diff in time period when he is in category Current customer

Then it just loading of data from DWH to PBI , you also try to do the same power query as well as numbers doesnt seem too large

1 Like

Hi @AliB, did the response provided by @ppunvij help in solving your query? If not, how far did you get and what kind of help you need further? If yes, kindly mark as solution the answer that solved your query.

Hi @AliB, due to inactivity, a response on this post has been tagged as “Solution”. If you have a follow question or concern related to this topic, please remove the Solution tag first by clicking the three dots beside Reply and then untick the check box.