Monday, 10 November 2025

305.FAMILY MALL PROMOTION ANALYSIS USING | PROC SQL | PROC MEANS | PROC UNIVARIATE | PROC SGPLOT | PROC SGPANEL | AND MACRO ROI EVALUATION FOR SEASONAL FOOTFALL UPLIFT AND RETURN ON INVESTMENT CALCULATION

FAMILY MALL PROMOTION ANALYSIS USING | PROC SQL | PROC MEANS | PROC UNIVARIATE | PROC SGPLOT | PROC SGPANEL | AND MACRO ROI EVALUATION FOR SEASONAL FOOTFALL UPLIFT AND RETURN ON INVESTMENT CALCULATION

options nodate nonumber nocenter;

1. Create the main dataset:

data work.mall_events;

    infile datalines dlm=',' dsd truncover;

    length MallID $4 MallType $12 Season $8 Event $30;

    input MallID $ MallType $ Season $ Event :$30. PreFootfall PostFootfall;

    datalines;

M001,Regional,Peak,WinterFête,12000,15000

M002,Community,OffPeak,BookFair,4200,5200

M003,Outlet,Peak,ClearanceBash,7000,9800

M004,Regional,OffPeak,FoodCarnival,9400,10100

M005,Community,Peak,MusicWeekend,5300,6100

M006,Outlet,OffPeak,NightMarket,4100,4500

M007,Regional,Peak,DiwaliFest,15000,19700

M008,Community,OffPeak,HandicraftExpo,3600,4300

M009,Outlet,Peak,SummerPromo,8200,8900

M010,Regional,OffPeak,KidsFestival,6800,7400

;

run;

proc print data=work.mall_events noobs;

    title "MALL_EVENTS (10 obs x 6 vars)";

run;

OUTPUT:

MALL_EVENTS (10 obs x 6 vars)

MallIDMallTypeSeasonEventPreFootfallPostFootfall
M001RegionalPeakWinterFête1200015000
M002CommunityOffPeakBookFair42005200
M003OutletPeakClearanceBash70009800
M004RegionalOffPeakFoodCarnival940010100
M005CommunityPeakMusicWeekend53006100
M006OutletOffPeakNightMarket41004500
M007RegionalPeakDiwaliFest1500019700
M008CommunityOffPeakHandicraftExpo36004300
M009OutletPeakSummerPromo82008900
M010RegionalOffPeakKidsFestival68007400

2. Create a separate promo cost table (one row per Mall)

data work.promo_costs;

    infile datalines dlm=',' dsd truncover;

    length MallID $4 PromoCostUSD 8;

    input MallID $ PromoCostUSD;

    datalines;

M001,25000

M002,4800

M003,12000

M004,9000

M005,7200

M006,3500

M007,39000

M008,3200

M009,6800

M010,5600

;

run;

proc print data=work.promo_costs noobs;

    title "PROMO_COSTS (promo spend per mall)";

run;

OUTPUT:

PROMO_COSTS (promo spend per mall)

MallIDPromoCostUSD
M00125000
M0024800
M00312000
M0049000
M0057200
M0063500
M00739000
M0083200
M0096800
M0105600

3. PROC SQL: join events and costs, compute uplift metrics

proc sql;

    create table work.mall_joined as

    select a.MallID, a.MallType, a.Season, a.Event,

           a.PreFootfall, a.PostFootfall,

           b.PromoCostUSD,

           /* Absolute uplift */

           (a.PostFootfall - a.PreFootfall) as UpliftAbs format=8.,

           /* Percent uplift: careful for zero baseline */

           case

             when a.PreFootfall > 0 then 

             (a.PostFootfall - a.PreFootfall)/a.PreFootfall * 100

             else .

           end as UpliftPct format=8.2

    from work.mall_events as a

    left join work.promo_costs as b

    on a.MallID = b.MallID

    ;

quit;

proc print data=work.mall_joined noobs;

    title "Joined table with uplift calculations";

run;

OUTPUT:

Joined table with uplift calculations

MallIDMallTypeSeasonEventPreFootfallPostFootfallPromoCostUSDUpliftAbsUpliftPct
M001RegionalPeakWinterFête120001500025000300025.00
M002CommunityOffPeakBookFair420052004800100023.81
M003OutletPeakClearanceBash7000980012000280040.00
M004RegionalOffPeakFoodCarnival94001010090007007.45
M005CommunityPeakMusicWeekend53006100720080015.09
M006OutletOffPeakNightMarket4100450035004009.76
M007RegionalPeakDiwaliFest150001970039000470031.33
M008CommunityOffPeakHandicraftExpo36004300320070019.44
M009OutletPeakSummerPromo8200890068007008.54
M010RegionalOffPeakKidsFestival6800740056006008.82

4. Summarize uplift by MallType and Season

proc means data=work.mall_joined n mean median std min max;

    class MallType Season;

    var PreFootfall PostFootfall UpliftAbs UpliftPct PromoCostUSD;

    title "Descriptive stats of footfall and uplift by MallType and Season";

run;

OUTPUT:

Descriptive stats of footfall and uplift by MallType and Season

The MEANS Procedure

MallTypeSeasonN ObsVariableNMeanMedianStd DevMinimumMaximum
CommunityOffPeak2
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
2
2
2
2
2
3900.00
4750.00
850.0000000
21.6269841
4000.00
3900.00
4750.00
850.0000000
21.6269841
4000.00
424.2640687
636.3961031
212.1320344
3.0865772
1131.37
3600.00
4300.00
700.0000000
19.4444444
3200.00
4200.00
5200.00
1000.00
23.8095238
4800.00
 Peak1
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
1
1
1
1
1
5300.00
6100.00
800.0000000
15.0943396
7200.00
5300.00
6100.00
800.0000000
15.0943396
7200.00
.
.
.
.
.
5300.00
6100.00
800.0000000
15.0943396
7200.00
5300.00
6100.00
800.0000000
15.0943396
7200.00
OutletOffPeak1
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
1
1
1
1
1
4100.00
4500.00
400.0000000
9.7560976
3500.00
4100.00
4500.00
400.0000000
9.7560976
3500.00
.
.
.
.
.
4100.00
4500.00
400.0000000
9.7560976
3500.00
4100.00
4500.00
400.0000000
9.7560976
3500.00
 Peak2
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
2
2
2
2
2
7600.00
9350.00
1750.00
24.2682927
9400.00
7600.00
9350.00
1750.00
24.2682927
9400.00
848.5281374
636.3961031
1484.92
22.2479938
3676.96
7000.00
8900.00
700.0000000
8.5365854
6800.00
8200.00
9800.00
2800.00
40.0000000
12000.00
RegionalOffPeak2
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
2
2
2
2
2
8100.00
8750.00
650.0000000
8.1351690
7300.00
8100.00
8750.00
650.0000000
8.1351690
7300.00
1838.48
1909.19
70.7106781
0.9734887
2404.16
6800.00
7400.00
600.0000000
7.4468085
5600.00
9400.00
10100.00
700.0000000
8.8235294
9000.00
 Peak2
PreFootfall
PostFootfall
UpliftAbs
UpliftPct
PromoCostUSD
2
2
2
2
2
13500.00
17350.00
3850.00
28.1666667
32000.00
13500.00
17350.00
3850.00
28.1666667
32000.00
2121.32
3323.40
1202.08
4.4783429
9899.49
12000.00
15000.00
3000.00
25.0000000
25000.00
15000.00
19700.00
4700.00
31.3333333
39000.00

5. Nonparametric tests and effect size

Strategy:

- For within-mall pre/post comparison (paired), produce difference = Post - Pre

- Test if median difference differs from 0 using Wilcoxon signed-rank via PROC UNIVARIATE (SIGN test & signrank)

- Compute a common nonparametric effect size r = Z / sqrt(N) when Z is available from Wilcoxon.


/* Create differences and a long-format dataset for tests */

data work.diff;

    set work.mall_joined;

    Diff = PostFootfall - PreFootfall;

    UpliftPct_num = UpliftPct; /* explicit numeric var */

run;

proc print data=work.diff;

run;

OUTPUT:

ObsMallIDMallTypeSeasonEventPreFootfallPostFootfallPromoCostUSDUpliftAbsUpliftPctDiffUpliftPct_num
1M001RegionalPeakWinterFête120001500025000300025.00300025.0000
2M002CommunityOffPeakBookFair420052004800100023.81100023.8095
3M003OutletPeakClearanceBash7000980012000280040.00280040.0000
4M004RegionalOffPeakFoodCarnival94001010090007007.457007.4468
5M005CommunityPeakMusicWeekend53006100720080015.0980015.0943
6M006OutletOffPeakNightMarket4100450035004009.764009.7561
7M007RegionalPeakDiwaliFest150001970039000470031.33470031.3333
8M008CommunityOffPeakHandicraftExpo36004300320070019.4470019.4444
9M009OutletPeakSummerPromo8200890068007008.547008.5366
10M010RegionalOffPeakKidsFestival6800740056006008.826008.8235


/* Paired test across all malls: Wilcoxon signed-rank (paired) */

proc univariate data=work.diff;

    var Diff;

    ods select TestsForLocation;

    title "Paired (pre vs post) — Signed Rank & Sign Test for median difference";

run;

OUTPUT:

Paired (pre vs post) — Signed Rank & Sign Test for median difference

The UNIVARIATE Procedure

Variable: Diff

Tests for Location: Mu0=0
TestStatisticp Value
Student's tt3.365545Pr > |t|0.0083
SignM5Pr >= |M|0.0020
Signed RankS27.5Pr >= |S|0.0020

6. Prepare data for plotting (long format: 'Period'=Pre/Post)

data work.plotdata;

    set work.mall_joined;

    Period = 'Pre'; Footfall = PreFootfall; output;

    Period = 'Post'; Footfall = PostFootfall; output;

    keep MallID MallType Season Event Period Footfall;

run;

proc print data=work.plotdata;

run;

OUTPUT:

ObsMallIDMallTypeSeasonEventPeriodFootfall
1M001RegionalPeakWinterFêtePre12000
2M001RegionalPeakWinterFêtePos15000
3M002CommunityOffPeakBookFairPre4200
4M002CommunityOffPeakBookFairPos5200
5M003OutletPeakClearanceBashPre7000
6M003OutletPeakClearanceBashPos9800
7M004RegionalOffPeakFoodCarnivalPre9400
8M004RegionalOffPeakFoodCarnivalPos10100
9M005CommunityPeakMusicWeekendPre5300
10M005CommunityPeakMusicWeekendPos6100
11M006OutletOffPeakNightMarketPre4100
12M006OutletOffPeakNightMarketPos4500
13M007RegionalPeakDiwaliFestPre15000
14M007RegionalPeakDiwaliFestPos19700
15M008CommunityOffPeakHandicraftExpoPre3600
16M008CommunityOffPeakHandicraftExpoPos4300
17M009OutletPeakSummerPromoPre8200
18M009OutletPeakSummerPromoPos8900
19M010RegionalOffPeakKidsFestivalPre6800
20M010RegionalOffPeakKidsFestivalPos7400


7. Plot pre/post trends

proc sgpanel data=work.plotdata;

    panelby MallType / columns=1 spacing=10 novarname;

    series x=Period y=Footfall / group=MallID markers lineattrs=(thickness=2);

    scatter x=Period y=Footfall / group=MallID;

    rowaxis label="Footfall (visitors)";

    colaxis discreteorder=data;

    title "Pre vs Post footfall for each mall, panelled by MallType";

run;

OUTPUT:

The SGPanel Procedure


proc sql;

    create table work.mean_by_type as

    select MallType, Period, mean(Footfall) as MeanFootfall

    from work.plotdata

    group by MallType, Period;

quit;

proc print data=work.mean_by_type;

run;

OUTPUT:

ObsMallTypePeriodMeanFootfall
1CommunityPos5200.00
2CommunityPre4366.67
3OutletPos7733.33
4OutletPre6433.33
5RegionalPos13050.00
6RegionalPre10800.00

proc sgplot data=work.mean_by_type;

    series x=Period y=MeanFootfall / group=MallType markers;

    yaxis label="Mean Footfall";

    title "Average Pre/Post Footfall by MallType";

run;

OUTPUT:

The SGPlot Procedure


8. Macro: Evaluate ROI of promotions across MallType

- Inputs: revenue_per_visitor (USD), conversion_rate (fraction of extra visitors who translate to spend),

          optionally list of MallType (defaults to all)

- ROI formula (simple marketing ROI): ROI = (IncrementalRevenue - PromoCost) / PromoCost

  where IncrementalRevenue = IncrementalVisitors * conversion_rate * revenue_per_visitor

- We'll loop by MallType and print ROI and related metrics.


%macro ROI_by_type(rev_per_visitor=2.50, conv_rate=0.05);

/* rev_per_visitor: expected average revenue per visiting customer (USD)

   conv_rate: proportion of additional visitors who make a purchase (0-1) */

    proc sql;

        create table work.roi as

        select MallType,

               sum(PreFootfall) as SumPre format=8.,

               sum(PostFootfall) as SumPost format=8.,

               sum(PostFootfall - PreFootfall) as SumUplift format=8.,

               sum(PromoCostUSD) as TotalPromoCost format=dollar12.2

        from work.mall_joined

        group by MallType;

    quit;


    data work.roi_calc;

        set work.roi;

        RevenuePerVisitor = &rev_per_visitor;

        ConversionRate = &conv_rate;

        IncrementalVisitors = SumUplift;

        IncrementalRevenue = IncrementalVisitors * ConversionRate * RevenuePerVisitor;

        NetGainUSD = IncrementalRevenue - TotalPromoCost;

        /* Avoid divide-by-zero */

        if TotalPromoCost > 0 then ROI = NetGainUSD / TotalPromoCost;

        else ROI = .;

        /* ROI percent for readability */

        ROI_pct = ROI * 100;

    run;


    title "ROI Summary by MallType (rev_per_visitor=&rev_per_visitor, conv_rate=&conv_rate)";

    proc print data=work.roi_calc noobs label;

        var MallType SumPre SumPost IncrementalVisitors TotalPromoCost IncrementalRevenue NetGainUSD ROI_pct;

        label SumPre = "Sum PreFootfall"

              SumPost = "Sum PostFootfall"

              IncrementalVisitors = "Incremental Visitors"

              TotalPromoCost = "Total Promo Cost (USD)"

              IncrementalRevenue = "Incremental Revenue (USD)"

              NetGainUSD = "Net Gain (USD)"

              ROI_pct = "ROI (%)";

    run;

%mend ROI_by_type;


%ROI_by_type(rev_per_visitor=3.00, conv_rate=0.07);

OUTPUT:

ROI Summary by MallType (rev_per_visitor=3.00, conv_rate=0.07)

MallTypeSum PreFootfallSum PostFootfallIncremental VisitorsTotal Promo Cost (USD)Incremental Revenue (USD)Net Gain (USD)ROI (%)
Community13100156002500$15,200.00525-14675-96.5461
Outlet19300232003900$22,300.00819-21481-96.3274
Regional43200522009000$78,600.001890-76710-97.5954





To Visit My Previous Proc  Means And Nway Option:Click Here
To Visit My Previous Proc Means And CharType Option:Click Here
To Visit My Previous SAS Functions:Click Here
To Visit My Previous Length Statement Using In Many Ways:Click Here






Follow Us On : 


 


--- FOLLOW OUR BLOG FOR MORE INFORMATION.

--->PLEASE DO COMMENTS AND SHARE OUR BLOG.

No comments:

Post a Comment