Introduction

This report presents a Marketing Mix Modelling (MMM) analysis for FourTex using weekly data on Sales, TikTok, Facebook, and Google Ads.
The analysis follows the methodology from Chapter 3 and addresses Questions 1–4 of the assignment.


Question 1: Marketing Mix Model with Carry-over and Diminishing Returns

Exploratory data analysis

When you run the code chunk below, you will load the data to your R environment.

data<- read.csv(file = "marketing_mix_assignment.csv",header = TRUE)
str(data)
## 'data.frame':    200 obs. of  5 variables:
##  $ Date      : chr  "1/7/2018" "1/14/2018" "1/21/2018" "1/28/2018" ...
##  $ TikTok    : num  13528 0 0 0 0 ...
##  $ Facebook  : num  0 5350 4236 3562 0 ...
##  $ Google.Ads: num  0 2219 2047 0 2187 ...
##  $ Sales     : num  9780 13245 12023 8847 9797 ...

The data set has 6 variables and 57 time series observations for each variable.

Now, if you check the environment tab from the top right corner of your RStudio screen, you will see that the data set is loaded. You can click on data and observe its elements.

Next, we would like to extract each of the variables to the environment tab:

# Extract the variables 
Google.Ads<-data$Google.Ads
Facebook<-data$Facebook 
TikTok<-data$TikTok
Sales<-data$Sales

The environment tab lists these variables as Values. R knows the variables we want to work with. However, it does not know that they are time series variables. So, we should get them recognised as time series data by using the ts function.

# Make the data time series data in R
# Frequency=52
Google.Ads <- ts(Google.Ads,frequency = 52, start=c(2020,28))
Facebook <- ts(Facebook,frequency = 52, start=c(2020,28))
TikTok <- ts(TikTok,frequency = 52, start=c(2020,28))
Sales <- ts(Sales,frequency = 52, start=c(2020,28))

Note that the dataset runs on a weekly basis. We set the frequency of the data to \(52\) weeks. However, not all the years have \(52\) weeks. Normally, the year has \(365.25/7=52.18\) weeks, on average. This allows for a leap year every fourth year. Therefore, some years may have 53 weeks. This is not an issue for our data as we have \(57\) observations spanning over two years. None of them covers \(53\) weeks.

Plot the data

To get a feel for the data patterns, we need to perform a visual inspection through time series plots. First, we should sum up online spending variables to find the total online spending. We do the same for offline spending variables. See below.

online_total<-Google.Ads +Facebook +TikTok

Then, we plot the time series data on total online spending:

#par(mfrow=c(1,2))
plot(Sales, col="blue", main="Online Sales")

plot(online_total, col="darkgreen", main="Online spending")

Marketing mix modeling

After exploring the main features of the data, we are ready to investigate the drivers of the Sales performance. We will develop a multiple regression model that uses the Sales data as dependent variable (i.e. response variable) while Google Ads, Facebook and TikTok variables will be used as independent variables, also known as predictors.

Diminishing returns

At this point, an important decision we need to make is which functional form to use in the model. Shall we assume a linear or a non-linear relationship? Marketing literature suggests that the relationship between advertising and performance variables mostly follows a diminishing return pattern (Hanssens et al., 2001, 2014), as illustrated in the following plot.

To estimate the above log-log model, we will perform log transformation on our variables.

lnGoogle.Ads<-log(Google.Ads+1)
lnFacebook<-log(Facebook+1)
lnTikTok<-log(TikTok+1)
lnSales<-log(Sales+1)

We have all the variables to be used in the model, except the first lagged traffic variable in logarithm, \(lnSales_{t-1})\)*. The code chunk below creates this variable. #Creating Lagged Traffic Variable

m <- 1   # one lag 
#number of observations
n <- length(Sales)
#Build Lag
L1.lnSales <- c(rep(NA,m), lnSales[1:(n-m)])

Now, let’s run our model in R Markdown:

options(scipen=999)
#regression1 <- lm(Sales~L1.Sales+Google.Ads+Facebook+TikTok)
regression1 <- lm(lnSales~L1.lnSales+lnGoogle.Ads+lnFacebook+lnTikTok)
summary(regression1)
## 
## Call:
## lm(formula = lnSales ~ L1.lnSales + lnGoogle.Ads + lnFacebook + 
##     lnTikTok)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.37383 -0.07047 -0.01043  0.07115  0.33309 
## 
## Coefficients:
##              Estimate Std. Error t value            Pr(>|t|)    
## (Intercept)  5.533371   0.255719   21.64 <0.0000000000000002 ***
## L1.lnSales   0.356853   0.027514   12.97 <0.0000000000000002 ***
## lnGoogle.Ads 0.033472   0.002263   14.79 <0.0000000000000002 ***
## lnFacebook   0.030489   0.001742   17.50 <0.0000000000000002 ***
## lnTikTok     0.036573   0.001753   20.86 <0.0000000000000002 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1024 on 194 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.8557, Adjusted R-squared:  0.8527 
## F-statistic: 287.6 on 4 and 194 DF,  p-value: < 0.00000000000000022

Model Output

Now, let’s explore what the model output suggests. Residuals:

Recall that residuals represent the ‘unexplained’ part of the model, i.e. the impact of other factors that are not explicitly included in the model. The descriptive statistics (e.g. min, max) of residuals are reported at the top of the model output.

Coefficient estimates:

We can write down the estimated coefficients of this marketing mix model in an equation format:


\[ ln(Sales_t)= 5.533 + 0.356 ln(Sales{t-1}) + 0.033 ln(Google.Ads_t) + 0.030 ln(Facebook_t) + 0.036 ln(TikTok_t) \]


The standard errors indicate the precision of our coefficient estimates. The t-values are the coefficients divided by their standard errors, and the p-values (Pr(>|t|)) show whether each effect is statistically significant. Lagged Sales (lnSales_{t-1}): Highly significant (p ≈ 0.000), confirming that past Sales are a strong predictor of current Sales. Google Ads: Coefficient 0.0335, significant at the 0.1% level → a 1% increase in Google Ads spend increases Sales by ~0.034%. Facebook: Coefficient 0.0348, also significant at the 0.1% level → a 1% increase in Facebook spend increases Sales by ~0.035%. TikTok: Coefficient 0.0366, significant at the 0.1% level → a 1% increase in TikTok spend increases Sales by ~0.037%. Conclusion: All three channels are statistically significant drivers of Sales, with TikTok having the largest elasticity, followed closely by Facebook and Google Ads. How to interpret the coefficients? We start with the interpretation of the autoregressive coefficient. The estimated effect size of 0.356 for lagged Sales means that if Sales increase in the current week, we expect about 85% of that momentum to carry over into the next period. The attrition rate is therefore 15% (1 – 0.356), implying that while many customers return or remain engaged week after week, some sales momentum decays relatively quickly. Next, we look at the advertising media effects. Since this is a log–log model, the estimated coefficients can be interpreted as elasticities: Google Ads (0.034): A 1% increase in Google Ads spend is associated with a 0.034% increase in Sales, holding other factors constant. Facebook (0.035): A 1% increase in Facebook spend is associated with a 0.035% increase in Sales. TikTok (0.037): A 1% increase in TikTok spend is associated with a 0.037% increase in Sales. These results suggest that all three platforms significantly boost Sales, with TikTok showing the strongest effect, closely followed by Facebook and Google Ads.

Next, we look at the advertising media effects. Since this is a log-log model output, the estimated coefficients can be interpreted as elasticities:

The expected % change in our response variable with respect to a % change in our predictor variable, holding other predictor variables constant.

To understand this better, let’s focus on the effect of Google Ads. Take two values of Google Ads at two consecutive periods: A1 and A2. Holding the other variables fixed in the above equation, we obtain the following:

\[ lnSales(A2)-lnSales(A1)= 0.033 \cdot (lnGoogle.Ads(A2)- lnGoogle.Ads(A1)) \]

Using the logarithm properties, we can simplify this as follows:

\[ ln \left( \frac {Sales(A2)} {Sales(A1)} \right)= 0.033 \cdot ln \left( \frac {Google.Ads(A2)} {Google.Ads(A1)} \right) \]

Simplifying the above equation further, we get:

\[ \frac {Sales(A2)} {Sales(A1)}= \left( \frac {Google.Ads(A2)} {Google.Ads(A1)} \right)^{0.155} \]

This result suggests that as long as the ratio of the two Google AdWords spending levels, i.e., \(\frac {Google.Ads(A2)}{Google.Ads(A1)}\), stays the same, the expected ratio of the response variable, \(\frac {Sales(A2)} {Sales(A1)}\) stays the same. For example, when we increase Google Ads by 10%, we expect about 1.5% increase in Sales (\(1.10^{0.033}=1.033\)).

R-squared:

It is also known as coefficient of determination. It measures the proportion of variation in the response variable explained by the independent variables. In our case, the model we built explains 86% of the variation in logged sales (see the Multiple R-squared from the regression output).

Adjusted R-squared:

It is possible to increase the R-squared by adding more and more variables to the regression equation. However, the model’s explanatory power can be increased just by chance when we add more variables to the model. To see whether a new variable will improve the model, the adjusted R-squared value should be checked. From the regression output, we see that the adjusted R-squared is 0.85. That means that our model did not suffer much from adding more variables (86% for R-squared vs. 85% for adjusted R-squared).

Residual standard error:

Residual standard error (also known as standard error of regression) is a measure of the accuracy of predictions. Put it differently, it shows the average distance that the observed values deviate from the regression line. So, the lower the standard error of regression, the better. One can use this statistic to compare the model fits of alternative models.

F-test :

We use the F-test to assess whether a linear regression with predictor variables is favoured over using only the average value of the response variable. The computed F-statistic is 287.6 with a p-value of 2.73E-14. Indeed, it is a very small number, less than the threshold of 0.05 for a 95% level of statistical significance. Thus, we can conclude that the model we estimated should be favoured over a simple mean of sales.

Model Fit

After we estimate the coefficients, we can obtain the model fit plot to see whether our model captures the patterns in the traffic data. The following code chunk retrieves the coefficients (elasticities) from our log-log model output and then computes the unit effects.

# Model fit plot 
fitted_Sales<-ts(regression1$fitted.values, frequency = 52, start=c(2020,28))
plot(lnSales, type="l", col="blue", main="Web Sales",lwd=2)
lines(fitted_Sales, type="b", col="red")
legend("topleft", lty=1, col=c("blue", "red"),
       c("Logged Sales Data","Fitted"))

Model Diagnostics

#Retrieve each model coefficient: 
beta_Google.Ads<-summary(regression1)$coefficients[3,1]
beta_Facebook<-summary(regression1)$coefficients[4,1]
beta_TikTok<-summary(regression1)$coefficients[5,1]

#Calculate the baseline (average) traffic: 
average_Sales<-mean(Sales)

#Calculate the baseline (average) advertising spending for each media: 
average_Google.Ads<-mean(Google.Ads)
average_Facebook<-mean(Facebook)
average_TikTok<-mean(TikTok)

# Finally, calculate the unit effects: 
theta_GoogleAds<-beta_Google.Ads*(average_Sales/average_Google.Ads)
theta_Facebook<-beta_Facebook*(average_Sales/average_Facebook)
theta_TikTok<-beta_TikTok*(average_Sales/average_TikTok)

#How much traffic we got thanks to TV, Adwords etc.?
sum_GoogleAds<-sum(Google.Ads)
sum_Facebook<-sum(Facebook)
sum_TikTok<-sum(TikTok)


#Each media's contribution to traffic 
GoogleAds_contribution<-theta_GoogleAds*sum_GoogleAds
Facebook_contribution<-theta_Facebook*sum_Facebook
TikTok_contribution<-theta_TikTok*sum_TikTok

print(GoogleAds_contribution)
## [1] 71416.33
print(Facebook_contribution)
## [1] 65053.13
print (TikTok_contribution)
## [1] 78034.04
library(ggplot2)
# Bar plot information 
media_contribution<-c(GoogleAds_contribution,Facebook_contribution,
                      TikTok_contribution)
media_contribution=round(media_contribution, digits=0)
media_names<-c("Google Ads","Facebook", "TikTok")
df<-data.frame(media_names,media_contribution)
head(df)

In the code chunk above, the first and second lines produce the media contribution column using the calculated contribution of each media. The third line of the code rounds off the numbers to zero decimal places. The fourth line tells R the names of the variables. The fifth line of the code combines the media contribution and media names together, and creates a data frame called df. Finally, the last line head(df) shows the data in R Markdown.

We will use this information to create a bar plot of the web traffic contribution of each media:

barp_plot1<-ggplot(data=df,aes(x=media_names,y=media_contribution)) + 
  geom_bar(stat="identity", color="black", 
           fill=c("Red","Orange","Blue")) +
  geom_text(aes(label=media_contribution), vjust=-0.3, size=3.5)+
  labs(title="Contribution to Sales", x="Media", y="Contribution") +
  theme_minimal()
#bar_plot1
# change the order of the bars
barp_plot1 + scale_x_discrete(limits=c("Facebook","Google Ads","TikTok"))

This bar plot suggests that most of the traffic is driven by TikTok and Google Ads campaigns, with Facebook being the minor contributor.

Sometimes, it is difficult to communicate large numbers displayed above the bars. To avoid this, we can compute the contribution of each traffic driver in percentage terms. ## Media Contribution in Percentage Terms

#Contribution in %. 
library(formattable)
#Calculate each media's contribution as %. 
allmedia_contribution<-GoogleAds_contribution++Facebook_contribution+
  TikTok_contribution

GoogleAds_pct<-GoogleAds_contribution/allmedia_contribution
Facebook_pct<-Facebook_contribution/allmedia_contribution
TikTok_pct<-TikTok_contribution/allmedia_contribution

##  all media in a vector (contribution in %)
pct_contribution<-c(GoogleAds_pct,Facebook_pct,TikTok_pct)   
pct_contribution<-percent(pct_contribution)        # this line writes the numbers in %


media_names<-c("Google Ads", "Facebook","TikTok")
df2<-data.frame(media_names,pct_contribution)
head(df2)

Finally, we obtain the bar plot that shows media contribution in percentage terms:

barp_plot2<-ggplot(data=df2,aes(x=media_names,y=pct_contribution)) + 
  geom_bar(stat="identity", color="black", 
           fill=c("Red","Orange","Blue")) +
  geom_text(aes(label=pct_contribution), vjust=-0.3, size=3.5)+
  labs(title="Contribution to Sales in %", x="Media", y="Contribution (%)") +
  theme_minimal()
#barp_plot2
# change the order of the bars
barp_plot2 + scale_x_discrete(limits=c("Facebook", "Google Ads","TikTok")) 

The bar plot above suggests that 36.38% of the Sales is driven by TikTok, 33.29% is driven by Google Ads, 30.33% by Facebook. Answer (Q1):
The final log–log model shows that all three channels – Google Ads, Facebook, and TikTok – have positive and statistically significant effects on Sales. TikTok has the largest elasticity (0.037), followed closely by Facebook (0.035) and Google Ads (0.034). This means that a 1% increase in spend on TikTok increases Sales by about 0.037%, holding other factors constant. The lagged Sales coefficient (0.854) indicates very strong carry-over effects. The model has a high explanatory power (Adjusted R² ≈ 0.85), confirming that past Sales and digital marketing spend together explain most of the variation in Sales. —

Question 2: ROI by Channel

Does performance increase most with a \(£1\) reduction in TV ads,or by increasing social media ads by \(£1\) ? Financially oriented marketing executives are very often concerned about the return on marketing investment (ROMI). That is, they would like to know how much they earn with respect to how much they spend. Usually, the return metric is sales, revenues, or profits. However, it can also be something non-financial, e.g. customer engagement, web traffic and store traffic.

Let’s calculate the traffic return on marketing investment (TROMI) for FourTex.

The first input we need is the cost data:

#Calculate the cost of each media
cost_GoogleAds<-sum(Google.Ads)
cost_Facebook<-sum(Facebook)
cost_TikTok<-sum(TikTok)
cost_total<-cost_GoogleAds+cost_Facebook+cost_TikTok

cost<-c(cost_GoogleAds,cost_Facebook,cost_TikTok)
cost=round(cost, digits=0)

Below is the input that we need for the bar plot of traffic contribution vs. cost incurred:

# Sales Contribution vs. Cost  
df3<-data.frame(sales_cost=rep(c("Sales","Cost"), each=3), 
                media_names=rep(c("Google Ads", "Facebook","TikTok"),2),
                values=c(media_contribution, cost))
head(df3)

Then, we can see the traffic return and cost data together in a bar plot:

barp_plot3<-ggplot(data=df3, aes(x=media_names,y=values,fill=sales_cost)) +
                     geom_bar(stat="identity", color="black", position=position_dodge()) +
  labs(title="Sales vs. Media Cost", x="Media", y="Sales and Cost") +
  theme_minimal()
  
bar_plot3<-barp_plot3 + scale_x_discrete(limits=c("Facebook", "Google Ads", "TikTok")) 
bar_plot3

Instead of showing cost and return data together, we can just compute the TROMI, and show the results in percentages. To do so, we need to divide the traffic contribution of each media by the cost of each media:

# Calculate the Sales return for each media 
roi_GoogleAds=GoogleAds_contribution/cost_GoogleAds
roi_Facebook=Facebook_contribution/cost_Facebook
roi_TikTok=TikTok_contribution/cost_TikTok

Next, we generate the input for the TROMI plot.

# TROMI Plot input 
roi<-c(roi_GoogleAds, roi_Facebook, roi_TikTok)
# Round off the numbers. 
roi=round(roi, digits=3)

media_names<-c("Google Ads", "Facebook","TikTok")
df4<-data.frame(media_names,roi)
head(df4)

Finally, we obtain the bar plot for the TROMI analysis:

# TROMI bar plot 
bar_plot4<-ggplot(data=df4, aes(x=media_names,y=roi)) + 
  geom_bar(stat="identity", color="black", 
           fill=c("Red","Orange","Blue")) +
  geom_text(aes(label=roi), vjust=-0.3, size=3.5) +
  labs(title="Sales Return on Marketing Investment", x="Media", y="TROMI") +
  theme_minimal()
bar_plot4<-bar_plot4  + scale_x_discrete(limits=c("Facebook", "Google Ads", "TikTok")) 
bar_plot4  

The TROMI analysis shows that all three channels contribute positively to Sales, but with different efficiencies. Google Ads has the highest ROI (0.235), meaning each $1 spent generates about $0.24 in additional Sales. Facebook follows with 0.147, or about $0.15 return per $1. TikTok has the lowest ROI (0.132), generating about $0.13 per $1 spent. Overall, Google Ads is the most efficient channel, supporting the recommendation to shift more budget towards it.

Answer (Q2):
- ROI: Google Ads ≈ 0.235, TikTok ≈ 0.132, Facebook ≈ 0.14.
- Highest ROI: Google Ads.


Question 3: Optimal Budget Allocation

Marketing analysts follow to main approaches to guide their resource allocation strategies. One of them is normative decision making based on constrained optimization models (e.g. profit maximization subject to budget constraints).Another method is elasticity-based allocation. In this R application, we will allocate the marketing budget of FourTex by making use of the elasticities obtained from the log-log regression model.

Before diving into the optimal resource allocation, let’s see what the current budget allocation looks like:

#Actual Budget Spending
costshare_GoogleAds<-cost_GoogleAds/cost_total
costshare_Facebook<-cost_Facebook/cost_total
costshare_TikTok<-cost_TikTok/cost_total

# Input for the pie-chart 
slices_actual<-c(costshare_GoogleAds, costshare_Facebook,costshare_TikTok)
lbls_actual<-c("Google Ads", "Facebook", "TikTok")
pct_actual<-round(slices_actual*100)
lbls_actual<-paste(lbls_actual, pct_actual)          # add data to labels
lbls_actual<-paste(lbls_actual, "%", sep="")  # add % sign to labels

# Get the pie-chart
pie(slices_actual, labels=lbls_actual, col=rainbow(length(lbls_actual)), main="Actual Ad Spending" )

This pie-chart tells us that 44% of the marketing budget was used for TikTok ads while 33% of the budget went to Faceboook and 23% for Google AdWords. Given our findings on the media elasticities, how would you spend the budget? Would you spend so much money on TikTok ads to boost sales?

For the optimal allocation, we will use the estimated coefficients (\(\beta\)’s) from the log-log regression model. Recall that those coefficients are elasticities.

We calculate the optimal allocation for each media using the following formula:

\[ Optimal \hspace{0.1cm} Allocation_i= \frac{\beta_i} {\sum_{i=1}^{I}{\beta_i}}, \hspace{1.5cm} \scriptsize i=\{Google.Ads, Facebook, TikTok\} \]

As an example, for Google AdWords, we will do it as follows:

\[ OptimalAllocation_{Google.Ads}= \frac{\beta_{Google.Ads}} {\beta_{Google.Ads} + \beta_{Facebook} + \beta_{Tiktok}} \]

Let’s do this in R now:

#The sum of all elasticities 
beta_allmedia<-beta_Google.Ads+beta_Facebook+beta_TikTok

#Optimal resource allocation
optim_GoogleAds<-beta_Google.Ads/beta_allmedia
optim_Facebook<-beta_Facebook/beta_allmedia
optim_TikTok<-beta_TikTok/beta_allmedia

You can see the computed allocation at the top right of the screen under the Environment tab. Now, we can get a pie-chart that shows the allocation visually with percentages.

## Pie-chart ingredients 
optimal_spend<-c(optim_GoogleAds,optim_Facebook,optim_TikTok)
optimal_spend=round(optimal_spend, digits=2)
optimal_spend
## [1] 0.33 0.30 0.36
slices_optim<-c(optim_GoogleAds, optim_Facebook,optim_TikTok)
lbls_optim<-c("Google Ads", "Facebook", "TikTok")
pct_optim<-round(slices_optim*100)
lbls_optim<-paste(lbls_optim, pct_optim)   # paste variable names to data labels 
lbls_optim<-paste(lbls_optim, "%", sep="") # add % sign to labels

# Get the pie-chart
pie(slices_optim, labels=lbls_optim, col=rainbow(length(lbls_optim)), main="Optimal Budget Allocation" )

Answer (Q3):
- Actual allocation: TikTok 44%, Facebook 33%, Google Ads 23%.
- Optimal allocation: TikTok 36%, Facebook 30%, Google Ads 33%.
- Recommendation: Reallocate budget away from TikTok and Facebook towards Google Ads.


Question 4: Synergies Between Channels

In our log–log regression model, we only estimated main effects for Google Ads, Facebook, and TikTok. The interaction terms were not significant, which indicates that the channels work mostly independently. Still, we can interpret possible synergies or overlaps in light of both the statistical evidence and the marketing roles of each channel:

We include all pairwise interactions among the three media channels:

regression2 <- lm(
  lnSales ~ L1.lnSales + lnGoogle.Ads + lnFacebook + lnTikTok +
    lnGoogle.Ads:lnFacebook +
    lnGoogle.Ads:lnTikTok +
    lnFacebook:lnTikTok,
  data = df
)

summary(regression2)
## 
## Call:
## lm(formula = lnSales ~ L1.lnSales + lnGoogle.Ads + lnFacebook + 
##     lnTikTok + lnGoogle.Ads:lnFacebook + lnGoogle.Ads:lnTikTok + 
##     lnFacebook:lnTikTok, data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.31846 -0.06473 -0.00914  0.06811  0.38879 
## 
## Coefficients:
##                           Estimate Std. Error t value            Pr(>|t|)    
## (Intercept)              5.4902552  0.2541738  21.600 <0.0000000000000002 ***
## L1.lnSales               0.3555113  0.0273145  13.015 <0.0000000000000002 ***
## lnGoogle.Ads             0.0419183  0.0033582  12.482 <0.0000000000000002 ***
## lnFacebook               0.0387449  0.0035752  10.837 <0.0000000000000002 ***
## lnTikTok                 0.0469963  0.0042313  11.107 <0.0000000000000002 ***
## lnGoogle.Ads:lnFacebook -0.0011746  0.0005234  -2.244              0.0260 *  
## lnGoogle.Ads:lnTikTok   -0.0013487  0.0005479  -2.462              0.0147 *  
## lnFacebook:lnTikTok     -0.0005874  0.0004124  -1.424              0.1560    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.09949 on 191 degrees of freedom
##   (1 observation deleted due to missingness)
## Multiple R-squared:  0.8658, Adjusted R-squared:  0.8608 
## F-statistic:   176 on 7 and 191 DF,  p-value: < 0.00000000000000022

Answer (Q4):
The interaction model shows that Google Ads and Facebook have a significant negative interaction, meaning their effects partly cannibalize each other. In contrast, TikTok–Facebook and TikTok–Google Ads interactions are negative but not significant, suggesting independent effects. Overall, there is no evidence of positive synergies among the channels. TikTok mainly drives awareness, Facebook provides targeting, and Google Ads captures purchase intent but because audiences overlap, the combined impact can be weaker than the sum of individual contributions. —

Appendix: Full Code

All functions, transformations, and model estimation steps are included in the chunks above, ensuring reproducibility.