英语轻松读发新版了,欢迎下载、更新

通过Python中的营销组合模型优化预算

2025-01-26 12:03:14 英文原文

作者:Ryan O'Sullivan

动手指南的第3部分,可帮助您掌握PYMC的MMM

Ryan O'Sullivan

Towards Data Science

摄影作品拖曳BarbhuiyaUnplash

欢迎使用我的营销组合建模系列(MMM)的第3部分,这是一份动手指南,可帮助您掌握MMM。在整个系列中,我们将涵盖关键主题,例如模型培训,验证,校准和预算优化,所有这些都使用强大PYMC营销Python包。无论您是新手MMM还是希望提高自己的技能,该系列都将为您提供实用的工具和见解,以改善营销策略。

如果您错过了第2部分,请在此处查看:

在该系列的第三部分中,我们将通过涵盖以下领域来介绍如何从营销组合模型中获得业务价值:

  • 组织为什么要优化其营销预算?
  • 我们如何使用营销组合模型的产出来优化预算?
  • Python演练,演示了如何使用PYMC营销

完整的笔记本可以在此处找到:

用户生成的图像

这名著名的报价(来自约翰·瓦纳纳克(John Wanamaker),我想?!)既说明了营销的挑战和机会。尽管现代分析已经走了很长一段路,但挑战仍然相关:了解营销预算的哪些部分具有价值。

由于几个因素,营销渠道在其性能和投资回报率方面可能有很大差异:

  • 观众覆盖和订婚有些渠道更有效地达到与目标受众保持一致的特定潜在客户。
  • 获取成本到达前景的成本之间有所不同。
  • 通道饱和过度使用营销渠道会导致回报率降低。

这种可变性创造了一个机会,提出可以改变您的营销策略的关键问题:

用户生成的图像

有效的预算优化是现代营销策略的关键组成部分。通过利用MMM的产出,企业可以就何处分配资源以最大程度地影响做出明智的决定。MMM提供了有关各种渠道如何促进整体销售的见解,从而使我们能够确定改进和优化的机会。在以下各节中,我们将探讨如何将MMM输出转化为可行的预算分配策略。

2.1响应曲线

响应曲线可以将MMM的产出转化为一种全面的形式,以表明销售对每个营销渠道的响应方式。

用户生成的图像

仅响应曲线非常强大,使我们能够运行什么情况。以上面的响应曲线为例,我们可以估计,随着我们花费更多,社会变化的销售贡献如何。我们还可以在视觉上查看降低的回报开始生效的地方。但是,如果我们想尝试回答更复杂的情况,什么场景(例如在固定的总体预算中优化渠道水平预算)怎么办?这是线性编程的来源,让我们在下一节中探讨这一点!

2.2线性编程

线性编程是一种优化方法,可用于在给定某些约束的情况下找到线性函数的最佳解决方案。这是运营研究领域的一种非常多功能的工具,但并没有得到应有的认可。它用于解决调度,运输和资源分配问题。我们将探索如何使用它来优化营销预算。

让我们尝试使用简单的预算优化问题理解线性编程:

  • 决策变量(x):这些是我们要估计的未知数量,例如营销支出在每个渠道上。
  • 目标功能(z):我们试图最小化或最大化的线性方程,例如最大化每个渠道的销售贡献之和。
  • 约束:对决策变量的一些限制,通常由线性不等式表示,例如总营销预算等于5000万英镑的渠道水平预算在500万英镑至1500万英镑之间。
用户生成的图像

所有约束的交集形成了一个可行的区域,这是满足给定约束的所有可能解决方案的集合。线性编程的目的是找到优化目标函数的可行区域内的点。

鉴于我们应用于每个营销渠道的饱和转换,优化渠道级别的预算实际上是一个非线性编程问题。顺序最小二乘编程(SLSQP)是一种用于解决非线性编程问题的算法。它允许平等和不平等约束,使其成为我们用例的明智选择。

  • 平等约束例如总营销预算等于5000万英镑
  • 不平等约束例如渠道水平预算在500万英镑至1500万英镑之间

Scipy对SLSQP的实现非常好:

下面的示例说明了我们如何使用它:

从scipy.ipimize进口最小化

结果=最小化(
fun = Objective_function,#在此处定义您的ROI功能
x0 = initial_guess,#支出的初始猜测
bounds =界限,#频道级预算约束
约束=约束,#平等和不平等约束
方法='SLSQP'

打印(结果)

从头开始编写预算优化代码是一个复杂但非常有意义的练习。幸运的是,PYMC营销团队进行了繁重的工作,为运行预算优化方案提供了强大的框架。在下一节中,我们将探讨他们的包装如何简化预算分配过程并使分析师更容易访问。

现在,我们了解如何使用MMM的输出来优化预算,让我们看看使用上一篇文章中使用模型可以驾驶的价值!在本演练中,我们将介绍:

  • 模拟数据
  • 训练模型
  • 验证模型
  • 响应曲线
  • 预算优化

3.1模拟数据

我们将重新使用第一篇文章中的数据生成过程。如果您想提醒数据生成过程,请查看第一篇文章,我们进行了详细的演练:

np.random.seed(10)

#设置数据生成器的参数
start_date =“ 2021-01-01”
周期= 52 * 3
频道= [“电视”,“社交”,“搜索”]
adstock_alphass = [0.50,0.25,0.05]
饱和_lamdas = [1.5,2.5,3.5]
beta = [350,150,50]
speed_scalars = [10,15,20]

df = dg.data_generator(start_date,oferders,channels,spend_scalars,adstock_alphas,ataperation_lamdas,betas)

#使用最大销售价值的比例BETA-这与PYMC的拟合beta相当(PYMC确实具有和目标缩放使用Sklearn的MaxAbsScaler)
betas_scaled = [
((df [“ tv_sales”] / df [“ sales”]。max()) / df [“ tv_satatrated”])。
((DF [“ Social_Sales”] / df [“ sales”]。max()) / df [“ social_satured”])。
((df [“ search_sales”] / df [“ sales”]。max()) / df [“ search_satured”])。这是给出的
#计算贡献

贡献= np.Asarray([[
((df [“ tv_sales”]。sum() / df [“ sales”]。
((df [“ socile_sales”]。sum() / df [“ sales”]。
((df [“ search_sales”]。sum() / df [“ sales”]。
((df [“需求”]。sum() / df [“ sales”]。sum()),2
)))
df [[“日期”,“需求”,“ demand_proxy”,“ tv_spend_raw”,“ social_spend_raw”,“ search_spend_raw”,“ sales”]]

用户生成的图像

3.2训练模型

现在,我们将重新培训第一篇文章的模型。

我们将以与上次相同的方式准备培训数据:

  • 将数据分为特征和目标。
  • 为火车和超时切片创建索引。

但是,由于本文的重点不是模型校准,因此我们将包括需求作为控制变量,而不是需求_proxy。这意味着该模型将经过很好的校准,尽管这是不切实际的,它将为我们提供一些好的结果,以说明如何优化预算。

#设置日期列
date_col =“日期”

#设置成果列
y_col =“销售”

#设置营销变量
channel_cols = [“ tv_spend_raw”,
“ socile_spend_raw”,
“ search_spend_raw”]

#设置控制变量
control_cols = [“需求”]

#创建数组
x = df [[date_col] + channel_cols + control_cols]
y = df [y_col]

#设置测试(样本外)长度
test_len = 8

#创建火车和测试索引
train_idx =切片(0,len(df) - test_len)
out_of_time_idx = slice(len(df)-test_len,len(df))

mmm_default = mmm(
adstock =几何adstock(l_max = 8),
饱和=物流饱和(),
date_column = date_col,
channel_columns = channel_cols,
control_columns = control_cols,

fit_kwargs = {
“曲调”:1_000,
“链”:4,
“绘制”:1_000,
“ target_accept”:0.9,
}

mmm_default.fit(x [train_idx],y [train_idx],** fit_kwargs)

3.3验证模型

在进行优化之前,让我们检查模型良好。首先,我们检查真正的贡献:

频道= np.array([“电视”,“社交”,“搜索”,“需求”])

true_contributions = pd.dataframe({'channels':channels,'贡献':贡献})
true_contributions = true_contributions.sort_values(by ='贡献',casting = false).Reset_index(drop = true)
true_contributions = true_contributions.style.bar(subset = ['贡献'],color ='lightblue')true_contributions

用户生成的图像

正如预期的那样,我们的模型与真正的贡献非常紧密:

mmm_default.plot_waterfall_components_decomposition(figsize =(10,6));

用户生成的图像
3.4响应曲线

在进行预算优化之前,让我们看看响应曲线。

有两种方法可以查看响应曲线PYMC营销包裹:

  1. 直接响应曲线
  2. 成本共享响应曲线

让我们从直接响应曲线开始。在直接响应曲线中,我们只需在每周的每周供款中创建一个每周支出的散点图。

下面我们绘制直接响应曲线:

无花果= mmm_default.plot_direct_contribution_curves(show_fit = true,xlim_max = 1.2)
[ax.set(xlabel =“ onding”)用于图轴中的ax];
用户生成的图像

成本份额响应曲线是比较渠道有效性的另一种方法。当= 1.0时,频道支出保持与培训数据相同的水平。当®= 1.2时,渠道支出增加了20%。

下面我们绘制成本份额响应曲线:

mmm_default.plot_channel_contributions_grid(start = 0,stop = 1.5,num = 12,figsize =(15,7));
用户生成的图像

我们还可以更改X轴以显示绝对的支出价值:

mmm_default.plot_channel_contributions_grid(start = 0,stop = 1.5,num = 12,absolute_xrange = true,figsize =(15,7));
用户生成的图像

响应曲线是帮助考虑在渠道级别计划未来营销预算的绝佳工具。接下来,让它们采取行动并运行一些预算优化方案!

3.5预算优化

首先,让我们设置一些参数:

  • perc_change:这用于将约束设置围绕最小值设置,并在每个频道上支出最大支出。这种约束有助于我们保持场景现实,这意味着我们不会超出模型在训练中看到的响应曲线。
  • budgeb_len:这是几周内预算方案的时间。

我们将首先使用预算方案的所需长度来选择最新数据。

perc_change = 0.20
budgeb_len = 12
budged_idx = slice(len(df)-test_len,len(df))
erash_period = x [bucked_idx] [channel_cols]最近_period

用户生成的图像

然后,我们使用最近的时期将总体预算限制和渠道约束设置为每周的水平:

#设置总体预算约束(到最接近1k)

预算= round(erast_period.sum(axis = 0).sum() / budgeb_len,-3)
#记录当前预算按频道分配

current_budget_split = round(erash_period.mean() / erase_period.mean()。sum(),2)
#设置频道级别约束

lower_bounds = round(erast_period.min(axis = 0) *(1- perc_change))
upper_bounds = round(最近_period.max(axis = 0) *(1 + perc_change))
budgeb_bounds = {

频道:[lower_bounds [channel],upper_bounds [channel]]
用于channel_cols中的频道
}
打印(f'overall All预算约束:{预算}')

打印('频道约束:')
对于渠道,bugds_bounds.items()中的界限:
print(f'{channel}:下限= {bounds [0]},上限= {bounds [1]}')
用户生成的图像

现在是时候运行我们的场景了!

我们以相关的数据和参数为食,并恢复最佳支出。我们将其与获取总预算并按照当前的预算分配比例(我们称为实际支出)进行比较。

model_granularity =“每周”

#运行方案
Allocation_strategy,importization_result = mmm_default.optimize_budget(
预算=预算,
num_periods = buckit_len,
budgeb_bounds = budgeb_bounds,
minimize_kwargs = {
“方法”:“ SLSQP”,
“选项”:{“ ftol”:1e-9,“ maxiter”:5_000},},,

响应= mmm_default.sample_response_distribution(

分配_strategy =分配_strategy,
time_granularity = model_granularity,
num_periods = buckit_len,
noings_level = 0.05,

#提取最佳支出

opt_spend = pd.Series(salocation_strategy,index = erstic_period.mean()。index).to_frame(name =“ opt_spend”)
opt_spend [“ avg_spend”] =预算 * current_budget_split
#绘制实际和最佳支出

图,ax = plt.subplots(figsize =(9,4))
opt_spend.plot(bink ='barh',ax = ax,color = ['蓝色','橙色'])
plt.xlabel(“支出”)

plt.ylabel(“通道”)
plt.title(“频道的实际和最佳支出”)
plt.Legend([“最佳支出”,“实际支出”])
plt.legend([“最佳支出”,“实际支出”],loc ='右',bbox_to_anchor =(1.5,0.0))
plt.show()

用户生成的图像

我们可以看到建议是将预算从数字渠道转移到电视。

但是对销售有什么影响?

为了计算最佳支出的贡献,我们需要以每个通道的新支出值以及模型中的任何其他变量为食。我们只有需求,因此我们以最近时期的平均价值为食。我们还将以相同的方式计算平均支出的贡献。

#以最佳支出创建数据框
last_date = mmm_default.x [“ date”]。max()
new_dates = pd.date_range(start = last_date,oferts = 1 + budgect_len,freq =“ w-mon”)[1:]
budged_scenario_opt = pd.dataframe({“ date”:new_dates,})
budged_scenario_opt [“ tv_spend_raw”] = opt_spend [“ opt_spend”] [tv_spend_raw']
budged_scenario_opt [“ socile_spend_raw”] = opt_spend [“ opt_spend”] [social_spend_raw']
budged_scenario_opt [“ search_spend_raw”] = opt_spend [“ opt_spend”] [“ search_spend_raw”]
budged_scenario_opt [“需求”] = x [budgect_idx] [control_cols] .mean()[0]

#计算总体贡献
方案_contrib_opt = mmm_default.sample_posterior_predictive(
X_PRED = budged_scenario_opt,Extend_idata = false

opt_contrib = scenario_contrib_opt.mean(dim =“ sample”)。sum()[“ y”]。值

#用AVG支出创建数据框
last_date = mmm_default.x [“ date”]。max()
new_dates = pd.date_range(start = last_date,oferts = 1 + budgect_len,freq =“ w-mon”)[1:]
budged_scenario_avg = pd.dataframe({“ date”:new_dates,})
budged_scenario_avg [“ tv_spend_raw”] = opt_spend [“ avg_spend”] [tv_spend_raw']
budged_scenario_avg [“ social_spend_raw”] = opt_spend [“ avg_spend”] [“ social_spend_raw”]
budged_scenario_avg [“ search_spend_raw”] = opt_spend [“ avg_spend”] [“ search_spend_raw”]
budged_scenario_avg [“需求”] = x [budgect_idx] [control_cols] .mean()[0]

#计算总体贡献
方案_contrib_avg = mmm_default.sample_posterior_predictive(
X_PRED = budgeb_scenario_avg,estext_idata = false

avg_contrib = saceario_contrib_avg.mean(dim =“ sample”)。sum()[y“”]。值

#计算销售额增加%
打印(f'%增加销售额:{((opt_contrib / avg_contrib) - 1,2)}')

用户生成的图像

最佳支出使我们的销售额增加了6%!这是令人印象深刻的,特别是考虑到我们已经确定了总体预算!

今天,我们看到了预算优化的强大优化。它可以帮助组织每月/季度/年度预算计划和预测。与往常一样,提出好建议的关键又回到了一个强大的校准模型。

希望您喜欢第三部分!这就是本系列掌握MMM的系列。但是,如果您想了解衡量长期品牌建设效果的复杂主题,请继续关注!

关于《通过Python中的营销组合模型优化预算》的评论


暂无评论

发表评论

摘要

当然!示例中所示的预算优化过程显示了如何利用多点触摸归因(MTA)模型,尤其是营销组合模型(MMMS)来做出数据驱动的决策。这是一个简明的摘要和有关该主题的其他一些见解:###预算优化过程摘要1。**数据准备**: - 将数据集分为培训和验证集。 - 定义输入功能(`X`),目标变量(Y')和其他参数。2。**模型培训**: - 使用历史数据训练MMM模型,以确保其在销售驱动因素(例如营销渠道,季节性)方面得到了很好的校准。3。**定义约束和目标**: - 为每个渠道建立预算限制。 - 定义优化目标(通常使投资回报率或销售收入最大化)。4。**方案模拟**: - 基于当前的支出模式或假设场景模拟不同的分配策略。 - 根据历史数据评估这些方案以确定其有效性。5。**优化和贡献计算**: - 使用优化算法找到符合限制的最佳预算分配,同时最大程度地提高销售贡献。 - 从优化和实际支出方案中计算出贡献,以衡量绩效差异。6。**结果解释**: - 可视化和解释结果,表明重新分配资金如何改善整体业务成果(例如,销售增长6%)。###其他见解1。**模型校准**: - 通过包括经济指标,竞争活动和季节性等必要的控制变量,确保您的MMM得到很好的校准。 - 定期重新训练模型以合并新的数据和趋势。2。**方案灵活性**: - 考虑多种情​​况(例如不同的预算水平,市场状况)。 - 评估各种经济气候或竞争者行动中建议的鲁棒性。3。**与利益相关者的沟通**: - 清楚地使用诸如条形图和百分比增加的视觉辅助辅助仪清楚地传达了优化结果。 - 突出显示关键的收获和可行的见解,供利益相关者有效理解和实施变化。4。**连续改进**: - 实施一个反馈循环,其中比较实际绩效与预测贡献进行了比较。 - 使用从实际成果中获得的见解来完善未来的模型和优化策略。5。**长期效果(品牌建设)测量**: - 如前所述,向前迈进,重要的是要衡量营销活动的长期品牌建设效果。 - 诸如滞后影响建模之类的技术可以帮助确定当前的投资如何影响未来的ROI超越未来的销售。###示例增强 - **可视化**:使用Matplotlib或Plotly之类的工具来创建更多引人入胜的可视化(例如,线图显示销售趋势)。 - **方案分析**:在分析中包括多种方案,以全面了解潜在影响。 - **高级优化技术**:探索SLSQP以外的高级优化方法,例如遗传算法或模拟退火。通过遵循这些步骤并结合其他增强功能,您可以创建更强大且可操作的MMM驱动预算优化策略。