Forum Discussion

rcarmichael1990's avatar
rcarmichael1990
New Member
1 year ago
Solved

Removing Promotional Data From Sales

Good afternoon,

I have a data set in PQ which comprises of a list of dates, product codes, the sales quantity, and a flag (where an item on promotion is denoted by a number greater than 0) which shows whether the specific item was on promotion on the specific date:

DateProductSale QtyPromo Flag
22 Oct 2023Prod0180
23 Oct 2023Prod0190
24 Oct 2023Prod0140
25 Oct 2023Prod0150
26 Oct 2023Prod0190
27 Oct 2023Prod01100
28 Oct 2023Prod0180
29 Oct 2023Prod0160
30 Oct 2023Prod0150
31 Oct 2023Prod01113
1 Nov 2023Prod01176
2 Nov 2023Prod0161
3 Nov 2023Prod01194
4 Nov 2023Prod01153
5 Nov 2023Prod01145
6 Nov 2023Prod01164
7 Nov 2023Prod0120
8 Nov 2023Prod01125
9 Nov 2023Prod01224
10 Nov 2023Prod01195
11 Nov 2023Prod01275
12 Nov 2023Prod01123
13 Nov 2023Prod01304
14 Nov 2023Prod0101
15 Nov 2023Prod01124
16 Nov 2023Prod01114
17 Nov 2023Prod01225
18 Nov 2023Prod01246
19 Nov 2023Prod01173
20 Nov 2023Prod01160
21 Nov 2023Prod01100
22 Nov 2023Prod01110
23 Nov 2023Prod01130
24 Nov 2023Prod01440
25 Nov 2023Prod01780

I would like to derive baseline sales, ie replace the sales where the product was on promotion with the average of the previous non-promo qty, and next non-promo entry (preferably smoothing between them, but i'll settle for the average across each day).

 

For example, in the data set above I would have the 08 Nov through 19 Nov be the average of 2 and 16, so each of the dates between 08 Nov and 19 Nov would show 9; or even better, 8th would be 3, 9th would be 4... 18th would be 13, 19th would be 14.

 

I'm unsure of how to reference the previous & next  [Promo Flag] = 0 to average... or a better alternative.

Any assistance is greatly appreciated.

Sincere regards,

Ryan

  • let
        // duration step
        one_day = #duration(1, 0, 0, 0),
        // function to generate list of dates and sales
        calc = (x, y, n) => 
            [daily_sales = (y{1} - x{1}) / n, 
            lst = List.Zip({List.Dates(x{0} + one_day, n - 1, one_day), List.Numbers(x{1} + daily_sales, n - 1, daily_sales)})][lst],
        // Source data - replac with yours
        Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
        tp = Table.TransformColumnTypes(Source,{{"Date", type date}}),
        // filter out promo sales and get list from table
        nopromo_table = Table.SelectRows(tp, (x) => x[Promo Flag] = 0),
        nopromo = List.Buffer(Table.ToList(nopromo_table, (x) => {x{0}, x{2}})),
        // generate list of missing dates and sales 
        generate = List.Generate(
            () => [i = 0, s = nopromo{0}, n = 0, res = {}],
            (x) => x[i] < List.Count(nopromo),
            (x) => 
                [
                    i = x[i] + 1, 
                    s = nopromo{i}, 
                    n = Duration.Days(s{0} - x[s]{0}), 
                    res = if n <= 1 then {} else calc(x[s], s, n)
                ],
            (x) => x[res]
        ), 
        // generated list >> to table
        baseline_sales = Table.FromList(List.Combine(generate), (x) => x, {"Date", "Sale Qty"}),
        // combine and sort new sales table, fill down product column
        all = Table.Sort(nopromo_table & baseline_sales, "Date"), 
        fd = Table.FillDown(all, {"Product"})
    in
        fd

     

4 Replies

  • let
        // duration step
        one_day = #duration(1, 0, 0, 0),
        // function to generate list of dates and sales
        calc = (x, y, n) => 
            [daily_sales = (y{1} - x{1}) / n, 
            lst = List.Zip({List.Dates(x{0} + one_day, n - 1, one_day), List.Numbers(x{1} + daily_sales, n - 1, daily_sales)})][lst],
        // Source data - replac with yours
        Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
        tp = Table.TransformColumnTypes(Source,{{"Date", type date}}),
        // filter out promo sales and get list from table
        nopromo_table = Table.SelectRows(tp, (x) => x[Promo Flag] = 0),
        nopromo = List.Buffer(Table.ToList(nopromo_table, (x) => {x{0}, x{2}})),
        // generate list of missing dates and sales 
        generate = List.Generate(
            () => [i = 0, s = nopromo{0}, n = 0, res = {}],
            (x) => x[i] < List.Count(nopromo),
            (x) => 
                [
                    i = x[i] + 1, 
                    s = nopromo{i}, 
                    n = Duration.Days(s{0} - x[s]{0}), 
                    res = if n <= 1 then {} else calc(x[s], s, n)
                ],
            (x) => x[res]
        ), 
        // generated list >> to table
        baseline_sales = Table.FromList(List.Combine(generate), (x) => x, {"Date", "Sale Qty"}),
        // combine and sort new sales table, fill down product column
        all = Table.Sort(nopromo_table & baseline_sales, "Date"), 
        fd = Table.FillDown(all, {"Product"})
    in
        fd

     

    • rcarmichael's avatar
      rcarmichael
      Frequent Visitor

      Thank you! I'll need to read through it a few more times to understand how you acomplished some of the steps.

  • rcarmichael1990 , 

    Here’s a step-by-step guide:

    Create a Calculated Column for Non-Promo Sales: First, create a calculated column that only includes sales quantities for non-promotion days. This will help in identifying the previous and next non-promotion sales quantities.
    NonPromoSales = IF('Table'[Promo Flag] = 0, 'Table'[Sale Qty], BLANK())


    Create a Calculated Column for Previous Non-Promo Sales: Create a calculated column to get the previous non-promotion sales quantity.

    PreviousNonPromoSales =
    VAR CurrentDate = 'Table'[Date]
    RETURN
    CALCULATE(
    MAX('Table'[NonPromoSales]),
    FILTER(
    'Table',
    'Table'[Date] < CurrentDate && NOT(ISBLANK('Table'[NonPromoSales]))
    )

     

    Create a Calculated Column for Next Non-Promo Sales: Create a calculated column to get the next non-promotion sales quantity.
    NextNonPromoSales =
    VAR CurrentDate = 'Table'[Date]
    RETURN
    CALCULATE(
    MIN('Table'[NonPromoSales]),
    FILTER(
    'Table',
    'Table'[Date] > CurrentDate && NOT(ISBLANK('Table'[NonPromoSales]))
    )

     

    Create a Calculated Column for Baseline Sales: Finally, create a calculated column that calculates the baseline sales by averaging the previous and next non-promotion sales quantities.
    BaselineSales =
    IF(
    'Table'[Promo Flag] > 0,
    DIVIDE('Table'[PreviousNonPromoSales] + 'Table'[NextNonPromoSales], 2),
    'Table'[Sale Qty]
    )

    • rcarmichael's avatar
      rcarmichael
      Frequent Visitor

      Thank you for your reply!

      Unfortunately as the code wasn't what Power Query accepts in the "Custom Column" dialogue, I was unable to proceed with your solution; 

      ie RETURN, CALCULATE, MIN, & FILTER.