小言_互联网的博客

数据分析36计(21):Uber、Netflix 常用倍差法模型量化营销活动、产品改版影响效果...

564人阅读  评论(0)

1 案例背景

目前 Uber、Netflix 在商业分析中的因果推断常用模型主要是倍差法(Difference in Difference)和匹配(Matching),目前已在其平台中建立相关方法的自主分析工具。这篇文章将介绍倍差法和普通回归法在效应量评估上的真实差异。

Figure 1. Uber’s Experimentation Platform

这里以一个简单的经济学案例来讲述倍差法 DID 的原理,根据美国联邦法规,每个州的劳工赔偿计划在赔偿受伤的工人时,其赔偿范围从一定的“补偿率”(通常是受伤前工资的三分之二)到某个特定的最高额。对理性决策者而言,继续伤残所能获得的补偿越高,重返工作的动力就越小。

1980年,肯塔基州提高了每周工伤赔偿的最高限额,这里检验赔偿额的变化是否对重返工作的决策有显著影响。我们关心的主要结果变量是 log_duration(在数据中为 ldurat ),或记录的工伤补偿期限(以周为单位)。这里该变量取 log 是因为该变量存在较大的偏差,大多数人失业了几周,而有些人失业了很长时间。制定该政策的目的是使上限增加不会影响低收入工人,但会影响高收入工人,因此我们将低收入工人作为我们的对照组,将高收入工人作为我们的处理组。

数据集包含在 wooldridge R 软件包中的 injury:

  • durat(duration):失业救济金的持续时间,以周为单位

  • ldurat(log_duration):log(durat)

  • after_1980(after_1980):指标变量,观察是在1980年政策更改之前(0)还是之后(1)进行,时间变量:before/after

  • highearn:指示变量,用于标记观察值是低(0)还是高(1)收入,分组变量:处理/对照

2 加载和清理数据

首先下载数据集并加载相关库:


   
  1. library(tidyverse)  # ggplot(), %>%, mutate(), and friends
  2. library(broom)  # Convert models to data frames
  3. library(scales)  # Format numbers with functions like comma(), percent(), and dollar()
  4. library(modelsummary)  # Create side-by-side regression tables
  5. # Load the data. 
  6. # It 'd be a good idea to click on the "injury_raw" object in the Environment
  7. # panel in RStudio to see what the data looks like after you load it
  8. injury_raw <- read_csv("data/injury.csv")
  9. injury <- injury_raw %>% 
  10.   filter(ky == 1) %>% 
  11.   # The syntax for rename is `new_name = original_name`
  12.   rename(duration = durat, log_duration = ldurat,
  13.          after_1980 = afchnge)

3 探索性数据分析

首先可以看看高低收入者(对照和处理组)中失业补偿的分布:


   
  1. ggplot(data = injury, aes(x = duration)) +
  2.   # binwidth =  8 makes each column represent  2 months ( 8 weeks) 
  3.   # boundary =  0  make it so the  0 -8 bar starts at  0 and isn 't -4 to 4
  4.   geom_histogram(binwidth = 8, color = "white", boundary = 0) +
  5.   facet_wrap(vars(highearn))

分配情况确实存在偏差,两组中的大多数人都可以享受 0-8 周的福利(还有少数可以享受 180 周以上的福利!这就是 3.5 年!)

如果使用持续时间的对数,则可以得到较少偏斜的分布,该分布更适合回归模型:


   
  1. ggplot(data = injury, mapping = aes(x = log_duration)) +
  2.   geom_histogram(binwidth =  0.5, color =  "white", boundary =  0) + 
  3.   # Uncomment this line  if you want to exponentiate the logged values on the
  4.   # x-axis. Instead of showing  123, etc., it 'll show e^1, e^2, e^3, etc. and
  5.   # make the labels more human readable
  6.   # scale_x_continuous(labels = trans_format("exp", format = round)) +
  7.   facet_wrap(vars(highearn))

我们还应该检查政策改变前后的失业情况:


   
  1. ggplot(data = injury, mapping = aes(x = log_duration)) +
  2.   geom_histogram(binwidth =  0.5, color =  "white", boundary =  0) + 
  3.   facet_wrap(vars(after_1980))

分布看上去很正常,但很难轻易看出处理前后,即对照组与处理组之间的差异。我们可以绘制平均值。使用 stat_summary() 图层让 ggplot 计算汇总平均值等汇总统计信息。这里我们只计算平均值:


   
  1. ggplot(injury, aes(x = factor(highearn), y = log_duration)) +
  2.   geom_point(size =  0.5, alpha =  0.2) +
  3.   stat_summary(geom =  "point", fun =  "mean", size =  5, color =  "red") +
  4.   facet_wrap(vars(after_1980))

计算平均值和 95% 置信区间:


   
  1. ggplot(injury, aes(x = factor(highearn), y = log_duration)) +
  2.   stat_summary(geom =  "pointrange", size =  1, color =  "red",
  3.                fun.data =  "mean_se", fun.args = list(mult =  1.96)) +
  4.   facet_wrap(vars(after_1980))

可以开始看到经典的差异比较图!看起来 1980 年以后的高收入者平均失业时间更长。在将数据发送到 ggplot 之前,还可以使用 group_by() 和 summarize() 找出分组均值:


   
  1. ggplot(plot_data, aes(x = after_1980, y = mean_duration, color = highearn)) +
  2.   geom_pointrange(aes(ymin = lower, ymax = upper), size =  1) + 
  3.   # The group = highearn here makes it so the lines  go across categories
  4.   geom_line(aes(group = highearn))

4 倍差法 DID 的计算原理

我们可以通过填写 2x2 来找到对照组和处理组间的真实差异:


1980前 1980后 差异
低收入 A B B-A
高收入 C D D-C
差异 C-A D-B (D-C)-(B-A)


   
  1. diffs <- injury %>% 
  2.   group_by(after_1980, highearn) %>% 
  3.   summarize(mean_duration = mean(log_duration),
  4.     # Calculate average with regular duration too, just  for fun
  5.     mean_duration_for_humans = mean(duration))
  6. diffs
  7. ## # A tibble:  4 x  4
  8. ## # Groups:   after_1980 [ 2]
  9. ##   after_1980 highearn mean_duration mean_duration_for_humans
  10. ##        <dbl>    <dbl>         <dbl>                    <dbl>
  11. ##  1           0         0           1.13                      6.27
  12. ##  2           0         1           1.38                     11.2 
  13. ##  3           1         0           1.13                      7.04
  14. ##  4           1         1           1.58                     12.9
  15. before_treatment <- diffs %>% 
  16.   filter(after_1980 ==  0, highearn ==  1) %>% 
  17.   pull(mean_duration)
  18. before_control <- diffs %>% 
  19.   filter(after_1980 ==  0, highearn ==  0) %>% 
  20.   pull(mean_duration)
  21. after_treatment <- diffs %>% 
  22.   filter(after_1980 ==  1, highearn ==  1) %>% 
  23.   pull(mean_duration)
  24. after_control <- diffs %>% 
  25.   filter(after_1980 ==  1, highearn ==  0) %>% 
  26.   pull(mean_duration)
  27. diff_treatment_before_after <- after_treatment - before_treatment
  28. diff_control_before_after <- after_control - before_control
  29. diff_diff <- diff_treatment_before_after - diff_control_before_after
  30. diff_diff
  31. ##  0.19

对照组和处理组的差异估计为 0.19,这意味着该政策计划导致失业时间增加 19%。

5 倍差法 DID 回归

像上面的手工计算很繁琐,因此我们可以使用回归来完成!请记住,我们需要包括用于处理/对照组、1980前/后以及两者之间交互作用的指标变量:

的系数是我们最终关心的效果,即 DID 估计量。


   
  1. model_small <- lm(log_duration ~ highearn + after_1980 + highearn * after_1980,data = injury)
  2. tidy(model_small)
  3. ## # A tibble:  4 x  5
  4. ##   term                estimate std.error statistic   p.value
  5. ##   <chr>                  <dbl>     <dbl>     <dbl>     <dbl>
  6. ##  1 (Intercept)           1.13        0.0307     36.6    1.62e-263
  7. ##  2 highearn              0.256       0.0474      5.41   6.72e-8
  8. ##  3 after_1980            0.00766     0.0447      0.171  8.64e-1
  9. ##  4 highearn:after_1980   0.191       0.0685      2.78   5.42e-3

highearn:after_1980 的系数应该与我们手动计算的系数相同!它具有统计意义,因此我们可以确信高低收入之间的失业时长存在显著差异。

6 加入控制变量的 DID 回归

DID 回归的优点之一是,可以包括控制变量。例如,与其他行业的工人相比,建筑或制造业工人的工人索赔期限往往更长。我们可能还希望控制工人的人口统计信息,例如性别,婚姻状况和年龄。

让我们用以下附加变量估算基本回归模型:

  • male

  • married

  • age

  • hosp (1=住院)

  • indust (1=制造商,2=建筑,3=其他)

  • injtype (1-8;不同类型伤害的类别)

  • lprewage (提出索赔之前的工资记录)

提示:indust 和 injtype 在数据集中以数字(1-3和1-8)表示,但实际上它们是类别。在 R 中必须将它们视为类别(或因子)。


   
  1. # Convert industry and injury  type to categories/factors
  2. injury_fixed <- injury %>% 
  3.   mutate(indust = as.factor(indust),
  4.          injtype = as.factor(injtype))
  5.          
  6. model_big <- lm(log_duration ~ highearn + after_1980 + highearn * after_1980 + 
  7.                   male + married + age + hosp + indust + injtype + lprewage,
  8.                 data = injury_fixed)
  9. tidy(model_big)
  10. # Extract just the diff-in-diff estimate
  11. diff_diff_controls <- tidy(model_big) %>% 
  12.   filter(term ==  "highearn:after_1980") %>% 
  13.   pull(estimate)
  14.   
  15. modelsummary(list( "DID" = model_small,  "DID+control" = model_big))


DID DID+control
(Intercept) 1.126*** -1.528***

(0.031) (0.422)
highearn 0.256*** -0.152*

(0.047) (0.089)
after_1980 0.008 0.050

(0.045) (0.041)
highearn × after_1980 0.191*** 0.169***

(0.069) (0.064)
male
-0.084**


(0.042)
married
0.057


(0.037)
age
0.007***


(0.001)
hosp
1.130***


(0.037)
indust2
0.184***


(0.054)
indust3
0.163***


(0.038)
injtype2
0.935***


(0.144)
injtype3
0.635***


(0.085)
injtype4
0.555***


(0.093)
injtype5
0.641***


(0.085)
injtype6
0.615***


(0.086)
injtype7
0.991***


(0.191)
injtype8
0.434***


(0.119)
lprewage
0.284***


(0.080)
Num.Obs. 5626 5347
R2 0.021 0.190
R2 Adj. 0.020 0.187

* p < 0.1, ** p < 0.05, *** p < 0.01

在控制了许多人口统计因素之后,“差异比较”估计值变小(0.169),表明该政策导致工作场所受伤后的失业时间延长了16.9%。之所以较小,是因为其他自变量可以解释的部分log_duration的变化。

数据分析36计(20):优化新财年广告预算,乘法营销组合模型的Python实现
数据分析36计(19):美国生鲜配送平台【Instacart】如何实现按时配送——使用分位数回归

数据分析36计(18):Shopify如何使用准实验和反事实来优化产品
数据分析36计(17):Uber的 A/B 实验平台搭建
数据分析36计(16):和 A/B 测试同等重要的观察性研究:群组研究 VS 病例-对照方法

数据分析36计(15):这个序贯检验方法让 A/B 实验节约一半样本量

数据分析36计(14):A/B测试中的10个陷阱,一不注意就白做

数据分析36计(13):中介模型利用问卷数据探究用户心理过程,产品优化思路来源

数据分析36计(12):做不了AB测试,如何量化评估营销、产品改版等对业务的效果

数据分析36计(11):如何用贝叶斯概率准确提供业务方营销转化率

数据分析36计(十):Facebook开源时间序列预测算法 Prophet

数据分析36计(九):倾向得分匹配法(PSM)量化评估效果分析

数据运营36计(八):断点回归(RDD)评估产品设计效果

数据分析36计(七):营销增益模型(uplift model)如何识别营销敏感用户群-Python

数据运营36计(六):BG/NBD概率模型预测用户生命周期LTV-Python

数据运营36计(五):马尔可夫链对营销渠道归因建模,R语言实现

数据运营36计(四):互联网广告渠道归因分析之Sharply Value

数据运营36计(三):熵权法如何确定指标权重构建评价体系

数据运营36计(二):如何用合成控制法判断策略实施效果

数据运营36计(一):生存分析与用户行为如何联系起来

扫码即可关注


转载:https://blog.csdn.net/Pylady/article/details/112167138
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场