Skip to content
Scenario3 min read

Scenario: Classic RCT

We call 'Classic Randomized Controlled Trial' (RCT) a scenario where a treatment is randomly assigned to participants, and we do not have pre-experiment data of...

Scenario: Classic RCT

We call 'Classic Randomized Controlled Trial' (RCT) a scenario where a treatment is randomly assigned to participants, and we do not have pre-experiment data of participants like pre-treatment outcome.

Treatment - new onboarding for new users.

We will test hypothesis:

HoH_o - There is no difference in conversion rate between treatment and control groups.

HaH_a - There is a difference in conversion rate between treatment and control groups.

Causal Assumptions

Unconfoundedness: random assignment of treatment. Will be tested with SRM and Balance Check

Overlap: each unit has a non-zero probability of assignment to every arm. By design

SUTVA: no interference and consistent treatment definitions. By design

Data

For the analysis you need data in pandas dataframe:

  • treatment column in binary format (1/0)
  • outcome column numeric format, measured after treatment time
  • user_id column (Optional, but useful)
  • confounders columns (Optional, measured before treatment time, numeric format, used for causal assumption check)

We will take data from Causalis DGP. More you can read at https://causalis.causalcraft.com/articles/generate_classic_rct_26

Result
user_idconversiondplatform_ioscountry_usasource_paidmm_obstau_linkg0g1cate
001fc40.00.01.00.01.00.50.50.1064830.3106200.3338680.023249
10204c0.01.00.00.01.00.50.50.1064830.1982570.2157270.017471
2002cf0.00.01.01.00.00.50.50.1064830.2319690.2514790.019509
30202d0.01.01.01.00.00.50.50.1064830.2319690.2514790.019509
4011cb0.01.00.01.00.00.50.50.1064830.1421890.1556780.013489
Result

CausalData(df=(10000, 5), treatment='d', outcome='conversion', confounders=['platform_ios', 'country_usa', 'source_paid'])

EDA

Result
treatmentcountmeanstdminp10p25medianp75p90max
0049550.1989910.3992810.00.00.00.00.01.01.0
1150450.2329040.4227230.00.00.00.00.01.01.0
Result

png

SRM

Our system is randomly splitting users. Half of them must have new onboarding, other half has not. We should monitor the split with SRM test. Read more at https://causalis.causalcraft.com/articles/srm

Result

SRMResult(status=no SRM, p_value=0.36812, chi2=0.8100)

Confounders balance

Are groups equal in terms of confounders? We need to choose with domain and business sense confounders and check balance of them. The standard benchmark:

  • SMD>0.1SMD > 0.1
  • ks_pvalue < 0.05
Result
confoundersmean_d_0mean_d_1abs_diffsmdks_pvalue
0source_paid0.2990920.3137760.0146840.0318530.64592
1platform_ios0.4940460.5028740.0088280.0176540.98861
2country_usa0.5862760.5918730.0055970.0113741.00000

As we see system splitted users randomly

SUTVA

Result

1.) Are your clients independent (i). Outcome of ones do not depend on others? 2.) Are all clients have full window to measure metrics? 3.) Do you measure confounders before treatment and outcome after? 4.) Do you have a consistent label of treatment, such as if a person does not receive a treatment, he has a label 0?

Estimation with Diff-in-Means

In Causalis.DiffInMeans model implemented ttest, conversion_ztest and welch_permutation_t_test:

  • use conversion_ztest when users < 100k and outcome is binary
  • use welch_permutation_t_test when users < 10k or outcome is ratio metric or your metric is highly skewed
  • in other cases use ttest

Read more in https://causalis.causalcraft.com/articles/rct-classic-model

We will use conversion_ztest for our scenario

Result
value
field
estimandATE
modelDiffInMeans
value0.0339 (ci_abs: 0.0111, 0.0567)
value_relative17.0425 (ci_rel: 8.2614, 25.8235)
alpha0.0500
p_value0.0000
is_significantTrue
n_treated5045
n_control4955
treatment_mean0.2329
control_mean0.1990
time2026-05-09

Let's compare ATE with oracle effect

Result

Ground truth ATE is 0.01719144406311028