📈 第2章 实验
📄 一句话概括

把八家店按店址切成两格,每一格里降价都更好——方向和总体相反。 那个同时在两边起作用、又不在研究链条上的变量,叫混杂因素。它有三条腿,缺一条都只是巧合。 这一篇讲怎么认出它、怎么用拆分管住它,以及为什么拆分永远管不干净。

📝 本章和前面几章的关系

上一篇排除了问卷这条路,回到门店表。这一篇是第 2 章的核心:混杂因素。 第 9 章(直方图)会从「看形状」的角度再碰一次这个问题, 第 11 章(误差)会把它变成「分割数据」。

一、切开就看见了

按店址把八家店分成两格,每格里再按降不降价分——四组:

店址 降价 家数 变化
社区 降 3 −15.0%
社区 没降 1 −18.0%
商圈 降 1 −2.0%
商圈 没降 3 −5.0%

两格里方向一致,都是降价好 3 个百分点。可合起来,降价组更差 4.9 个百分点。

二、混杂因素的三条腿

跟结果有关:店址影响销售额。社区店整体跌 18%,商圈店只跌 5%。 跟分组有关:降不降价和店址相关——社区店更倾向于降价(生意差,想自救)。 不在研究链条上:店址不是降价造成的,它先于降价存在。

三条全占才是混杂因素。少一条,它只是「另一个变量」,不会把结论掰弯。

三、为什么会把方向掰反

因为组间差异 = 处理效果 + 分组差异。你不把分组差异扣掉,就会把它记在处理效果头上。 降价组背负了「社区店」这个身份,而社区店本身在暴跌。于是降价背了黑锅。 这就是辛普森悖论在实验场景里的样子:每一格都朝一个方向,合起来朝反方向。

四、拆分能管住它,但管不干净

按店址拆开,混杂就被固定住了——同类店里店址相同,差异只能来自降价。 可店址不是唯一的混杂因素。店长能力、装修新旧、门口有没有地铁口,每一个都同时影响两边。 你能想到几个就拆几个,永远拆不完。想不到的那些,数据里根本没有字段。

五、一个实用判断

看到「总体方向和每一格都相反」,第一反应就该是「有个变量在分组里不均」。 找它的办法不是算,是问:这两组在收数据之前就不一样的地方有哪些。 答案通常在业务常识里,不在统计软件里。

六、动手 Demo:混杂因素:藏在店址里的那只手

同一张表,切一刀,四行。看组内方向与总体方向怎样分道扬镳。 最后两行是这次的结论:混杂能拆掉一部分,拆不完。

stores = [('S01', 'community', True, 120.0), ('S02', 'community', True, 110.0),
          ('S03', 'community', True, 90.0), ('S04', 'downtown', True, 80.0),
          ('S05', 'downtown', False, 130.0), ('S06', 'downtown', False, 100.0),
          ('S07', 'downtown', False, 85.0), ('S08', 'community', False, 75.0)]
rate = {('community', True): -0.15, ('community', False): -0.18,
        ('downtown', True): -0.02, ('downtown', False): -0.05}
now = [s[3] * (1 + rate[(s[1], s[2])]) for s in stores]

for site in ('community', 'downtown'):
    for cut in (True, False):
        idx = [i for i, s in enumerate(stores) if s[1] == site and s[2] == cut]
        a = sum(stores[i][3] for i in idx)
        b = sum(now[i] for i in idx)
        print('%-10s %-5s %d 家  %+.1f%%'
              % (site, 'cut' if cut else 'keep', len(idx), (b / a - 1) * 100))

for cut in (True, False):
    idx = [i for i, s in enumerate(stores) if s[2] == cut]
    a = sum(stores[i][3] for i in idx)
    b = sum(now[i] for i in idx)
    print('总体 %-5s %d 家  %+.1f%%'
          % ('cut' if cut else 'keep', len(idx), (b / a - 1) * 100))

实跑输出:

--------------------------------------------------------------------
混杂因素:藏在店址里的那只手
--------------------------------------------------------------------

  ① 把数据按店址切开
  --------------------------------------------------------------------

| 店址 | 降价 | 门店数 | 销售额变化 |
|---|---|---:|---:|
| 社区 | 降 | 3 | -15.0% |
| 社区 | 没降 | 1 | -18.0% |
| 商圈 | 降 | 1 | -2.0% |
| 商圈 | 没降 | 3 | -5.0% |


  ② 每一格里,降价都更好
  --------------------------------------------------------------------
  社区店:降价 −15.0%,没降 −18.0%,差 3 个百分点。
  商圈店:降价 −2.0%,没降 −5.0%,也差 3 个百分点。
  两格里方向一致:降价让销售额多撑住了 3 个点。
  ! 可合起来,降价组反而更差 4.9 个百分点——这就是混杂因素的力量。

  ③ 混杂因素的三条腿
  --------------------------------------------------------------------

| 条件 | 在这里是什么 |
|---|---|
| **跟结果有关** | 店址影响销售额:社区店整体跌 18%,商圈店只跌 5% |
| **跟分组有关** | 降价店四家里三家在社区 |
| **不在研究链条上** | 店址不是降价造成的,是先于降价存在的 |

  三条全占,就是一个合格的混杂因素。少一条都只是巧合。

  ④ 拆分能管理它,但管不干净
  --------------------------------------------------------------------
  按店址拆开,你能看到「同类店里降价更好」。
  可店址不是唯一的混杂因素:店长能力、装修新旧、门口有没有地铁口,
  哪一个都同时影响「降不降价」和「卖得好不好」。
  你能想到几个就拆几个,但永远拆不完——因为你想不到的那些,数据里没有。

四行里两行是 −15 对 −18,两行是 −2 对 −5:组内差都是 3 个点,方向一致。 最后两行却翻过来了。这就是汇总的代价——它把结构信息平均掉了。

30 秒实验

  1. 把 S04(商圈降价)改成社区降价,四格会变成什么样?总体方向还翻吗?
  2. 再切一刀「店长是否新上任」,自己造一组数,看组内差会不会变。
  3. 想一想:如果某个混杂因素是「门店面积」,你能在现有的表里发现它吗?

常见误区

  1. 只管拆分不管样本量:拆完每组剩一家,结论的误差比效应还大。
  2. 把相关当混杂:只有同时影响「分组」和「结果」的变量才是混杂,只影响结果的不算。
  3. 以为拆完就干净:拆分只能处理你想到并记录下来的变量。
  4. 在混杂没排除前就报结论:这一步省下的时间,后面要用双倍去解释「为什么和实际相反」。
自测三问(点开对答案) 1. 混杂因素的三条腿是什么? —— 跟结果有关、跟分组有关、不在研究链条上(不是被研究因素造成的)。 2. 为什么拆分之后方向会反过来? —— 因为组间差异等于处理效果加分组差异。拆开后分组差异被固定住,剩下的才是处理效果。 3. 拆分为什么管不干净? —— 只能拆你已经想到并且记下来的变量。没想到的、没记成字段的,仍然在暗处起作用。

拆分确实救回来一点,但刚要下结论,又冒出第二个混杂因素:降价的那几家店,去年都换了店长。

相关笔记