🟦CUPED#

Π’ΡƒΡ‚ ΠΌΠ°Π»ΠΎ ΠΈΠ½Ρ„Ρ‹. Если Π½Π°Π΄ΠΎ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ, Ρ‚ΠΎ Π²ΠΎΡ‚ ΡΡ‚Π°Ρ‚ΡŒΡ.

CUPED (Controlled-experement Using Pre-Experement Data) - Ρ‚Π΅Ρ…Π½ΠΈΠΊΠ° увСличСния Ρ‡ΡƒΠ²ΡΡ‚Π²ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ статистичСских тСстов (A/B тСстов) Π·Π° счСт использования историчСских Π΄Π°Π½Π½Ρ‹Ρ….


Π‘ΡƒΡ‚ΡŒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π° CUPED состоит Π² ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π΅ ΠΎΡ‚ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ \(Y\) ΠΊ \(Y_{cuped}\), которая вычисляСтся ΠΏΠΎ Ρ„ΠΎΡ€ΠΌΡƒΠ»Π΅ \(Y_{cuped} = Y - \theta X, \,\,\theta \in \mathbb{R}\). Π’.Π΅. для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠΊΠΎΠΉ ΠΈ ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅ Π½Π°Π΄ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ ΠΈ ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Ρ‚Ρ‹, ΠΏΠΎ Π½ΠΈΠΌ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠΉ cuped-ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ. ЗначСния ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ ΠΈ ΡΠΊΡΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ cuped-ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ Π±ΡƒΠ΄ΡƒΡ‚ ΠΏΠΎΠ΄Π°Π²Π°Ρ‚ΡŒΡΡ Π² статистичСский ΠΊΡ€ΠΈΡ‚Π΅Ρ€ΠΈΠΉ для ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π½ΡƒΠΆΠ½ΠΎΠΉ Π½Π°ΠΌ Π³ΠΈΠΏΠΎΡ‚Π΅Π·Ρ‹.

\[ D[\overline X_{cuped}] = D[\overline Y - \theta \overline X] = \frac{D[Y-\theta X]}{n} = \frac{D[Y] + \theta^2D[X]+ 2\theta cov(Y, X)}{n} \]

ΠžΡ‚ΡΡŽΠ΄Π° \(\theta_o = \frac{cov(X, Y)}{D[X]}\).

Π§Π΅ΠΌ сильнСС ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Ρ‚Π° ΠΊΠΎΡ€Ρ€Π΅Π»ΠΈΡ€ΡƒΠ΅Ρ‚ с Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΎΠΉ, Ρ‚Π΅ΠΌ сильнСС ΠΌΠΎΠΆΠ½ΠΎ ΡΠ½ΠΈΠ·ΠΈΡ‚ΡŒ Π΄ΠΈΡΠΏΠ΅Ρ€ΡΠΈΡŽ.

Алгоритм примСнСния CUPED.

  1. Π’Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ значСния исходной ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ \(Y\) для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ ΠΈ ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ°Ρ….

  2. Π’Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ значСния ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Ρ‚Ρ‹ \(X\) для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ ΠΈ ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ°Ρ…. Если Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ Π½Π΅ получаСтся, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ, Ρ‚ΠΎ просто ставим Π΅ΠΌΡƒ 0.

  3. Π’Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ \(\theta_o = \frac{cov(X, Y)}{D[X]}\)

  4. Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π²Ρ‹Ρ‡ΠΈΡΠ»ΠΈΡ‚ΡŒ \(Y_{cuped} = Y - \theta X\)

  5. ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ статистичСский тСст Π½Π° значСниях \(cuped\)-ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ ΠΈ ΡΠΊΡΠΏΠ΅Ρ€ΠΈΠΌΠ΅Π½ΠΈΡ‚Π°Π»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏ.

πŸ“ŽΠžΠ±Ρ‹Ρ‡Π½ΠΎ Π² качСствС \(X\) Π±Π΅Ρ€ΡƒΡ‚ значСния Ρ†Π΅Π»Π΅Π²ΠΎΠΉ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ Π·Π° Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ срок Π΄ΠΎ провСдСня экспСримСнта. Но ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΠΎΠ» ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ, Π³ΠΎΡ€ΠΎΠ΄, возраст ΠΈ Ρ‚.Π΄. А \(X\) ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·Ρ‹Π²Π°Ρ‚ΡŒ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ МО.

Код

ΠŸΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… сСссий.#

Π‘Π³Π΅Π½Π΅Ρ€ΠΈΠΌ синтСтичСскиС Π΄Π°Π½Π½Ρ‹Π΅ со срСднСй ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒΡŽ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΡ… сСссий.

import numpy as np
import pandas as pd
from scipy.stats import ttest_ind

group_size = 100

np.random.seed(33)

df_pilot = pd.DataFrame({'y_before': np.random.normal(120, 40, group_size)})
df_control = pd.DataFrame({'y_before': np.random.normal(120, 40, group_size)})

Π”ΠΎΠ±Π°Π²ΠΈΠΌ столбСц со срСдним Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈΠΌ сСссии Π²ΠΎ врСмя ΠΏΠΈΠ»ΠΎΡ‚Π°. ΠŸΡƒΡΡ‚ΡŒ наши измСнСния Π²Ρ‹Π·Π²Π°Π»ΠΈ Π² ΠΏΠΈΠ»ΠΎΡ‚Π½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅ рост Π΄Π»ΠΈΠ½Ρ‹ сСссии Π½Π° 5 ΠΌΠΈΠ½ΡƒΡ‚ Π² срСднСм.

df_pilot['y'] = df_pilot['y_before'] + np.random.normal(0, 10, group_size)
df_control['y'] = df_control['y_before'] + np.random.normal(0, 10, group_size)

df_pilot['y'] += np.random.normal(5, 2, group_size)
df_pilot.head()
y_before y
0 107.245860 117.249746
1 55.880778 64.625057
2 58.591285 54.735771
3 97.183964 105.821062
4 111.330868 117.212246
df_pilot.mean().round(1)
y_before    118.5
y           123.9
dtype: float64
df_control.mean().round(1)
y_before    114.2
y           116.1
dtype: float64

Π’ΠΈΠ΄ΠΈΠΌ, Ρ‡Ρ‚ΠΎ срСдниС стали ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ, Π² ΠΏΠΈΠ»ΠΎΡ‚Π½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ большС.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ Π·Π½Π°Ρ‡ΠΈΠΌΠΎΡΡ‚ΡŒ ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠΉ.

ΠžΠ»ΠΈΡ‡ΠΈΡ Π½Π΅ Π·Π½Π°Ρ‡ΠΈΠΌΡ‹, ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌ CUPED

_, pvalue_prepilot = ttest_ind(df_pilot['y_before'], df_control['y_before'])
_, pvalue_pilot = ttest_ind(df_pilot['y'], df_control['y'])
print(f'pvalue prepilot {pvalue_prepilot:0.3f}')
print(f'pvalue pilot {pvalue_pilot:0.3f}')
pvalue prepilot 0.475
pvalue pilot 0.209
def calculate_theta(y_control, y_pilot, y_control_cov, y_pilot_cov) -> float:
    """ВычисляСм Theta.
    
    y_control - значСния ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ Π²ΠΎ врСмя ΠΏΠΈΠ»ΠΎΡ‚Π° Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅
    y_pilot - значСния ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ Π²ΠΎ врСмя ΠΏΠΈΠ»ΠΎΡ‚Π° Π½Π° ΠΏΠΈΠ»ΠΎΡ‚Π½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅
    y_control_cov - значСния ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π½Π° ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅
    y_pilot_cov - значСния ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π½Π° ΠΏΠΈΠ»ΠΎΡ‚Π½ΠΎΠΉ Π³Ρ€ΡƒΠΏΠΏΠ΅
    """
    y = np.hstack([y_control, y_pilot])
    y_cov = np.hstack([y_control_cov, y_pilot_cov])
    covariance = np.cov(y_cov, y)[0, 1]
    variance = y_cov.var()
    theta = covariance / variance
    return theta
theta = calculate_theta(
    df_control['y'], df_pilot['y'],
    df_control['y_before'], df_pilot['y_before']
)

for df_ in [df_pilot, df_control]:
    df_['y_cuped'] = df_['y'] - theta * df_['y_before']

_, pvalue_cuped = ttest_ind(df_pilot['y_cuped'], df_control['y_cuped'])
print(f'pvalue cuped {pvalue_cuped:0.3f}')
pvalue cuped 0.014

ΠŸΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΈ Π·Π½Π°Ρ‡ΠΈΠΌΡ‹ΠΉ эффСкт, посмотрим ΠΊΠ°ΠΊ измСнились диспСрсии ΠΈ Ρ€Π°Π·Π½ΠΈΡ†Π° срСдних.

var_y_pilot = df_pilot['y'].var()
var_y_control = df_control['y'].var()
var_y_cuped_pilot = df_pilot['y_cuped'].var()
var_y_cuped_control = df_control['y_cuped'].var()

delta_y = df_pilot['y'].mean() - df_control['y'].mean()
delta_y_cuped = df_pilot['y_cuped'].mean() - df_control['y_cuped'].mean()

print(
    f'pilot group\n    var(y) = {var_y_pilot:0.1f}\n    var(y_cuped) = {var_y_cuped_pilot:0.1f}'
    f'\n    var(y)/var(y_cuped) = {var_y_pilot/var_y_cuped_pilot:0.2f}'
)
print(
    f'control group\n    var(y) = {var_y_control:0.1f}\n    var(y_cuped) = {var_y_cuped_control:0.1f}'
    f'\n    var(y)/var(y_cuped) = {var_y_control/var_y_cuped_control:0.2f}'
)
print(f'\ndelta_y = {delta_y:0.2f}\ndelta_y_cuped = {delta_y_cuped:0.2f}')
pilot group
    var(y) = 1842.7
    var(y_cuped) = 101.6
    var(y)/var(y_cuped) = 18.14
control group
    var(y) = 1920.2
    var(y_cuped) = 86.6
    var(y)/var(y_cuped) = 22.16

delta_y = 7.73
delta_y_cuped = 3.41

ДиспСрсия ΡƒΠΏΠ°Π»Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Π² 20 Ρ€Π°Π·.