c

codeye

V1

2022/09/22阅读:5主题:默认主题

蒙特卡洛商业预测

用Python进行商业模拟 使用蒙特卡洛来探索客户终身价值和客户获取成本 当我写这篇关于数据科学家的商业策略的文章时,我意识到对一个营销活动和它所产生的新客户群进行模拟可能会很有趣。

我的很多职业生活都是在金融和投资领域度过的。我更习惯于把公司作为一个整体来考虑(在总体水平上分析它们),而不是在每天的水平上。但我确实发现更细微的细节非常有趣。毕竟,如果一个整体不是其各部分的总和,那么它是什么呢?

了解更多事情的一个好方法是模拟它(有合理的输入和假设),然后研究结果。你常常会惊讶于几个关键变量之间看似简单的相互作用会产生一些令人惊讶的结果。因此,让我们用Python来模拟一个营销活动,看看会发生什么。

我们将把模拟的重点放在客户终身价值(CLTV)和客户获取成本(CAC)上,这是许多初创公司(也包括上市公司)的关键指标。在我之前的商业策略文章中,我从概念上深入探讨了这些指标,所以如果你想了解更多背景,请参考它。

对营销活动进行编码 这个模拟的详细代码可以在我的GitHub上找到。但这里是我们要做的事情的高层次概述。

定义一个运行营销活动的函数--给定一些输入,如转换率、成本、预算等,我想返回一个新签约的客户列表。 定义一个模拟客户群的函数--给定一些关于流失率、消费频率和消费金额的假设,我想模拟这个客户群的表现,并最终计算出他们对我的业务的平均终身价值。 运行一堆模拟,看看结果的分布。 让我们先写一下运行营销活动的函数。我们可以用两种方法之一来做--我们用一个随机产生的数字来代表每个印象的结果(每个印象变成付费客户的概率很小),然后统计出我们的结果(使用for循环)。或者我们可以利用一个统计模型。让我们选择后者,因为数百万次的循环是相当慢的。相反,我们可以利用我们的老朋友,二项分布(这让我有借口链接到我的旧博文--如果你不熟悉二项分布,请阅读)。

引用我以前的说法。

二项分布是一连串实验的概率分布,其中每个实验产生一个二进制结果,每个结果都独立于所有其他结果。

每个印象的结果是否真的独立是值得商榷的,但对于我们的目的,我们可以安全地做出这个假设。这意味着我们可以使用一个二项分布的随机变量来模拟我们的营销活动。我们的预算和CPM(谷歌等广告商的每千次印象成本)决定了试验的数量。而每个印象的转换率是成功的概率。下面的函数做了我刚才描述的事情。

用于计算营销活动结果的函数

def run_campaign(spend, cpm, conversion_rate):
    return np.random.binomial(spend/cpm*1000, conversion_rate)

转换率本身只是一个估计值,所以我们也应该给它注入一些不确定性。对于第一遍,假设我们的转换率为正态分布是合理的。请注意,我为转换率设置了一个下限,因为负转换率是没有意义的。

从预期转换率和它的stdev,得到 实现的转换率 1

def get_conversion_rate(expected, stdev):
    conversion_rate = max(expected + np.random.normal()*stdev, 
                          0.000001)
    return conversion_rate

1 现在让我们给它一些输入--花费是我们计划在营销活动上花费的金额,cpm是1,000个印象的成本,而两个与转换率有关的变量是我们对转换率的预期值和标准差的估计。预期转换率看起来很小,但实际上并不小。另一种方式是,每提供20000个印象,我们期望得到一个客户。在你意识到多亏了谷歌,每千次印象的成本是2美元--所以2万次印象我们只需花费40美元,这听起来就很多了。

预算 花费 = 50000 每千人成本 cpm = 2 转换率 转换率_预期 = 0.00005 转换率 = 0.00002 现在让我们试一试我们的函数。变量cohort_size是我们所追求的--它是我们的营销活动所带来的新客户的数量。有了它,我们可以计算出我们的客户获取成本(CAC)。CAC是我们营销活动的总成本除以我们从该活动中获得的客户数量。

让我们调用它来获取我们的营销活动中的新客户数量

conversion_rate = get_conversion_rate(conversion_rate_expected, 
                                      conversion_rate_stdev)
cohort_size = run_campaign( spend, cpm, conversion_rate)

并计算我们的客户获取成本

CAC = spend/cohort_size
print('Customers Gained: ', cohort_size)
print('CAC: ', round(CAC, 2))

运行以上几行产生的结果如下。

获得的客户。1,309

CAC(客户获取成本)。 38

当然,如果我们再运行一次,我们会看到不同的数值,因为我们已经注入了两层随机性(转换率是一个正态分布的随机变量,而最终成为客户的人数是一个二项分布的随机变量)。

模拟客户群的时间 我们的下一步是编写一些函数,模拟我们的客户群如何随时间变化。每年,队列中的一些成员会流失(不再是客户),而剩下的成员会从我们这里购买商品。

我们可以使用一个随机数生成器(生成0到1之间的浮点数,均匀分布)来模拟一个特定的客户每年是否会流失。其逻辑是这样的--对于某一年队列中的每个客户,我们生成一个随机数。如果我们生成的数字低于一个最小的阈值,那么这个客户就取消了。为了简单起见,我们假设所有的取消都发生在年初,所以流失的客户在他们离开的那一年从我们这里购买的东西为零。

用来模拟一个群组随时间变化的过程的函数

# Function that models the progression of a cohort over time
def simulate_cohort(cohort_size, churn_rate, transactions, price, 
                    retention_cost, yrs=5):
    customers_left = []
    spending = []
    profit = []
    for i in range(yrs):
        for customer in range(cohort_size):
            # Assume cancels happen at the start of the year
            # 假设取消发生在这一年的年初 
            # (为了简单起见)(for simplicity)
            churn_random_num = np.random.random()
            # 生成一个0到1之间的随机数,如果小于 
            # 小于churn_rate,那么客户就已经流失了,并且我们 
            # 从cohort_size中减去1
            if churn_random_num <= churn_rate:
                cohort_size += -1
            # Calculate and record cohort's data
        customers_left.append(cohort_size)
        spending.append(cohort_size*transactions*price)
        profit.append(cohort_size*(transactions*price -\
                                   retention_cost))
    return customers_left, spending, profit

接下来,让我们收集我们的输入并运行我们的函数。为了更加现实,我们应该假设每个客户的年度保留成本。这是我们每年需要花费的金额,以保持一个特定的客户对我们的服务满意和忠诚。请注意,我只预测了5年的利润--我们剩下的客户可能在第6年仍然向我们购买,但为了保守起见,我将假设一个特定的客户群最多只有5年的寿命。

churn_rate = 0.20
# Number of annual transactions per average cohort member
transactions = 6
# Price of goods sold per average transaction
price = 10
# Annual cost of retaining/servicing customer
retention_cost = 20
# Run the function
customers_left, spending, profit =\
    simulate_cohort(cohort_size, churn_rate, transactions, 
                    price, retention_cost, yrs=5)

译文👇


流失率 = 0.20
每个平均群组成员的年度交易数量
交易 = 6
每笔平均交易的商品售价
价格 = 10
保留/服务客户的年度成本
retention_cost = 20
运行函数
customers_left, spending, profit=客户的支出,利润=
    simulate_cohort(cohort_size, churn_rate, transactions, 
                    价格, 保留成本, 年数=5)

我们最感兴趣的输出是利润,即由我们的客户群产生的业务收入,因为这是客户终身价值的主要驱动因素。还有其他间接驱动因素,如满意客户的推荐,但我们今天不会在此建立模型。

在我们计算客户终身价值(CLTV)之前,我们需要写一个现值函数。客户从我们这里购买的时间长(只要他们是客户,每年都会在我们的产品上花一点钱)。这意味着很多回报都发生在未来。而由于通货膨胀、机会成本和不确定性,未来的美元比今天的美元价值低。我们可以通过对我们在未来收到的任何美元进行折算来近似地调整这些影响(我们收到的钱越远,需要折算的就越多)。下面的函数就是这样做的。

def present_value(cashflows, rate):
    pvs = []
    for i, val in enumerate(cashflows):
        pvs.append(val/(1 + rate)**(i+1))
    return pvs

我们现在有了计算CLTV所需要的东西。我们将应用10%的贴现率,将未来收到的利润转化为现值(以今天的美元计)。对于那些好奇的人来说,贴现率是很复杂的,因为我们试图通过一个单一的数字来表达众多的因素(通货膨胀、风险、我们的希望和梦想等等)--关于如何正确估计贴现率,外面有大量的文献,所以我在这里就不多说了。

计算CLTV

率=0.10

得到利润的PV

pvs = present_value(profit, rate) 以今天的美元计算的群组的价值是PV的总和

cohort_value = sum(pvs)
print('队列总价值: ', int(cohort_value))
print('CLTV: ', int(cohort_value/cohort_size))
print('CLTV-CAC Spread: ', int(cohort_value/cohort_size - CAC))

让我们来看看利润及其现值是如何比较的。

利润和它的现值 随着越来越多的客户流失,队列的利润(蓝色)随着时间的推移而下降。利润的现值(橙色)以更快的速度下降,因为在未来更远的利润被折现得更严重了。

这些现值的总和就是我们队列的价值,而我们的CLTV是队列价值除以队列的初始规模。

队列总价值:113,285美元

CLTV(客户终身价值)。 86

CLTV-CAC价差:86美元-38美元=48美元

我们真正关心的价值是CLTV和CAC之间的价差--如果它是负的,那么我们就不会有长久的生意了。当然,在某些情况下,负价差是可以的。例如,急于扩大规模的早期创业公司会花费大量资金来吸引甚至是低价值的客户。这是因为他们的结果分布是相当二元的(而且他们的风险/回报是相当不对称的)。如果他们的规模足够快,他们就会建立一个成功的企业并变得富有。如果他们没有达到所需的规模,并且失败了,那么无论如何,这都是别人的钱(这种态度是多年来廉价和可用的资金的副产品,特别是来自风险资本家的资金)。

当然,我们的期望是未来客户群的利差将是积极的--随着公司和品牌的发展,CLTV应该增加,而CAC下降。

运行1,000种情景 最后,我们有了所有必要的部分,现在可以运行我们的蒙特卡洛模拟。我们将模拟1000个营销活动,这样我们就可以看一下结果的分布。

模拟1000次,看一下分布情况

cohort_size_list = []
CAC_list = []
CLTV_list = []
for i in range(1000):
    
    # 运行营销活动模拟
    conversion_rate = get_conversion_rate(conversion_rate_expected, 
                                      conversion_rate_stdev)
    cohort_size = run_campaign(spend, cpm, conversion_rate)
    CAC = 花费/群组大小
    
    # 模拟产生的群组
    # 客户留存,支出,利润
    customers_left, spending, profit = 
        simulate_cohort(cohort_size, churn_rate, transactions, 
                     price, retention_cost, yrs = 5)
                    #价格, 保留成本, 年数
    
    cohort_value = sum(present_value(profit, rate))
    
    cohort_size_list.append(cohort_size)
    CAC_list.append(CAC)
    CLTV_list.append(cohort_value/cohort_size)

将模拟结果存储在一个数据框中

results_df = pd.DataFrame()
results_df['initial_cohort_size'] = cohort_size_list
results_df['CLTV'] = CLTV_list
results_df['CAC'] = CAC_list
results_df['Spread'] = results_df['CLTV'] - results_df['CAC']

我们的客户群的起始规模可以有很大的变化,因为我们有两层随机性(我们允许每个活动的 "真实 "转换率变化,而且每个出去的印象本身就是一个随机结果的试验)。

初始队列规模分布 如果队列规模可以有很大的变化,那么CAC也可以有很大的变化(我们预计它的中值在40美元左右(因为或我们的输入假设)。哇,这是很广泛的。CAC的中值是40美元,但有可能经历可怕的活动回报(很少有客户注册),并获得三位数的CAC--超过6%的模拟结果是CAC为100美元或以上。因此,不要期待最坏的情况,但要做好准备。

分布的形状很有趣。从CAC的角度来看,它是不对称的--如果我们签了很多客户,我们会得到一个低的CAC,但它能低到什么程度有一个现实的限制(我称它为我们上升的软上限)。但如果我们没有签下很多客户,我们就有可能看到非常高的CAC,这对我们公司来说可能是灾难性的。

CAC分布 现在我们来看看收入方面的情况,即CLTV。由于我的建模方式,这里的变数明显较少。我本可以通过在年度流失率或客户交易中注入随机性(通过改变每笔交易的频率和价值)来允许更多的变数。如果我这样做,钟形曲线会更宽(由于更多的变化和不确定性的来源),但仍会有相同的平均值和一般形状。

CLTV分布 最后,让我们看一下CLTV和CAC之间的分布。在这里,我们再次看到一个长尾和软帽,但这次它是翻转的(因为我们在计算价差时从CLTV中减去CAC)。这意味着两方面的问题。

即使我们有一个非常成功的活动,我们能够实现的每个客户的上升空间(以利差衡量)也是有限的。这个限制的存在是因为CAC只能降到这么低(例如,在我运行的1000个方案中,遇到的最低CAC是16美元),每个客户只能买这么多。 另一方面,如果我们有一个灾难性的活动,价差很容易为负数,这意味着由此产生的客户很可能永远无法赚回他们的获取成本。根据公司的阶段和营销活动的规模,这可能不是一个交易的破坏者(它可能只是一个临时和一次性的小插曲)。但是,这应该劝阻我们不要把我们公司的资产或预算的很大一部分押在一个大规模的活动上--那里的失败可能意味着公司的末日。

CLTV-CAC价差分布 希望这对我们有帮助,欢呼吧!

分类:

后端

标签:

后端

作者介绍

c
codeye
V1