SkillForge — результати A/B-тесту нового онбордингу

Автор: Дмитро Купріянов
Період експерименту: 01–20 серпня 2025 року

Короткий підсумок. Новий онбординг «Миттєве занурення» показав вищу конверсію в преміум-підписку: 43,13% у A проти 64,73% у B. Абсолютна різниця становить +21,60 п.п., відносний приріст — +50,08%. Різниця статистично значуща (p-значення < 0,001); 95% довірчий інтервал різниці B − A: [+19,65; +23,52] п.п.

Основний розрахунок охоплює користувачів із визначеною групою та відомим підсумковим статусом конверсії. Аналіз чутливості показує збереження переваги B за крайніх сценаріїв для невідомих статусів. Рекомендація — розглядати B для поетапного впровадження після перевірки трекінгу та контролю додаткових бізнес-метрик.

1. Підключення до бази даних

↑ До змістуДо панелі навігації

Підключимося до PostgreSQL та завантажимо підготовлені дані для подальшого A/B аналізу.

import pandas as pd
from sqlalchemy import create_engine, text
from sqlalchemy.engine import URL
from getpass import getpass
password = getpass("Пароль PostgreSQL: ")

db_url = URL.create(
    drivername="postgresql+psycopg2",
    username="postgres",
    password=password,
    host="localhost",
    port=5432,
    database="skillforge")

engine = create_engine(db_url)
with engine.connect() as conn:
    result = conn.execute(text("SELECT current_database(), current_user"))
    print(result.fetchone())
('skillforge', 'postgres')
with engine.connect() as conn:
    df_users = pd.read_sql(
        text("SELECT * FROM v_user_conversion"),conn)
df_users.head()

Попередній перегляд індивідуальних рядків не включено до публічної версії. Код і узагальнені результати збережені.

df_users.shape
(9946, 6)
df_users.info()
df_users.duplicated().sum()
np.int64(0)
df_users.isna().sum()
df_users['user_id'].duplicated().sum()
np.int64(0)

Із PostgreSQL завантажено 9 946 користувачів із визначеними групами A/B. Пропущені значення збережено для подальшого аналізу. Дублікатів і повторень user_id не виявлено. Дані підготовлені для подальшого аналізу.

with engine.connect() as conn:
    df_daily = pd.read_sql(
        text("SELECT * FROM v_daily_agg"),conn)
df_daily.head()
date group_label cnt_users cnt_conversions cnt_unknown_convert cnt_known conver_rate
0 2025-08-12 B 1504 251 10 1494 16.80
1 2025-08-06 B 2015 339 15 2000 16.95
2 2025-08-05 B 1486 203 14 1472 13.79
3 2025-08-03 B 747 127 7 740 17.16
4 2025-08-03 A 750 68 5 745 9.13
df_daily.shape
(40, 7)
df_daily.isna().sum()
date                   0
group_label            0
cnt_users              0
cnt_conversions        0
cnt_unknown_convert    0
cnt_known              0
conver_rate            0
dtype: int64
df_daily.duplicated().sum()
np.int64(0)
df_daily.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 40 entries, 0 to 39
Data columns (total 7 columns):
 #   Column               Non-Null Count  Dtype  
---  ------               --------------  -----  
 0   date                 40 non-null     object 
 1   group_label          40 non-null     object 
 2   cnt_users            40 non-null     int64  
 3   cnt_conversions      40 non-null     int64  
 4   cnt_unknown_convert  40 non-null     int64  
 5   cnt_known            40 non-null     int64  
 6   conver_rate          40 non-null     float64
dtypes: float64(1), int64(4), object(2)
memory usage: 2.3+ KB
df_daily['date'] =  pd.to_datetime(df_daily['date'])

df_daily = df_daily.sort_values(by = ['date','group_label']).reset_index(drop = True)

Дані з v_daily_agg успішно завантажено. Отримано 40 записів за 20 днів для двох груп A/B. Типи даних перевірено, дати впорядковано для подальшого аналізу.

2. Опис експерименту та гіпотези

↑ До змістуДо панелі навігації

Мета: перевірити, чи підвищує функція «Миттєве занурення» конверсію користувачів у преміум-підписку.

Група A (контрольна): стандартний процес після реєстрації.

Група B (тестова): автоматичний перехід до першого інтерактивного уроку.

Основна метрика: Конверсія — частка користувачів, які оформили преміум-підписку протягом тесту, серед користувачів із відомим підсумковим статусом.

Правило підсумкового статусу:

  • 1 — є хоча б один запис converted = 1;
  • NULL — немає підтвердженої конверсії, але є невідомий щоденний статус, або немає жодного запису daily;
  • 0 — усі наявні щоденні статуси відомі та дорівнюють нулю.

Нульова гіпотеза (H₀): нова функція «Миттєве занурення» не підвищує конверсію користувачів у преміум-підписку.

Альтернативна гіпотеза (H₁): нова функція «Миттєве занурення» підвищує конверсію користувачів у преміум-підписку.

Рівень значущості: α = 0.05.

3. Огляд вибірки

↑ До змістуДо панелі навігації

Перевіримо кількість користувачів у групах A/B та розподіл статусів конверсії перед статистичним аналізом.

df_users

Попередній перегляд індивідуальних рядків не включено до публічної версії. Код і узагальнені результати збережені.

df_users['group_label'].value_counts()
group_label
A    5006
B    4940
Name: count, dtype: int64
group_stats = df_users.groupby('group_label')['conversion_status'].value_counts(dropna = False).reset_index()

group_stats['percent'] = (group_stats['count'] / group_stats.groupby('group_label')['count'].transform('sum') * 100).round(2)

group_stats.rename(columns={'group_label':'Група', 'conversion_status':'Статус конверсії', 'count':'Користувачі', 'percent':'Частка, %'})
Група Статус конверсії Користувачі Частка, %
0 A 0.0 2765 55.23
1 A 1.0 2097 41.89
2 A NaN 144 2.88
3 B 1.0 3142 63.60
4 B 0.0 1712 34.66
5 B NaN 86 1.74

У групі A виявлено 144 користувачі з невідомим статусом конверсії (2.88%), у групі B — 86 (1.74%). Пропуски розподілені нерівномірно, тому їхній вплив на результат A/B тесту перевіримо окремо.

До 230 невідомих статусів входять 224 користувачі з невизначеним результатом за наявними записами та 6 без записів daily (2 у A і 4 у B).

4. Конверсія та приріст

↑ До змістуДо панелі навігації

Розрахуємо конверсію для груп A та B, абсолютну різницю та відносний приріст конверсії.

4.1. Конверсія за групами

↑ До змістуДо панелі навігації

df_users_conver = df_users.groupby('group_label').agg(total_users=('user_id', 'size'),known_users = ('conversion_status','count'),
                                                      conversion_count = ('conversion_status','sum'))

df_users_conver['conversion_rate'] = (df_users_conver['conversion_count'] / df_users_conver['known_users'] * 100).round(2)

df_users_conver.rename(columns={'total_users':'Усього користувачів', 'known_users':'Користувачі з відомим статусом','conversion_count':'Конверсії',
                                'conversion_rate':'Конверсія, %'}).rename_axis('Група')
Усього користувачів Користувачі з відомим статусом Конверсії Конверсія, %
Група
A 5006 4862 2097.0 43.13
B 4940 4854 3142.0 64.73

Конверсія в групі A становить 43.13%, у групі B — 64.73%. Нова функція показує вищу конверсію, проте статистичну значущість різниці ще потрібно перевірити

4.2. Абсолютний та відносний приріст

↑ До змістуДо панелі навігації

crn_a = df_users_conver.loc['A','conversion_count'] / df_users_conver.loc['A','known_users']

crn_b = df_users_conver.loc['B','conversion_count'] / df_users_conver.loc['B','known_users']

absolute_lift = (crn_b - crn_a) * 100

relative_lift = ((crn_b - crn_a) / crn_a) * 100

print(f'Абсолютний приріст: {absolute_lift:.2f} п.п.')
print(f'Відносний приріст: {relative_lift:.2f}%')
Абсолютний приріст: 21.60 п.п.
Відносний приріст: 50.08%

Конверсія групи B вища за групу A на 21.60 процентного пункта, що відповідає відносному приросту 50.08%. Результат свідчить про потенційну ефективність нової функції, проте статистичну значущість ще потрібно перевірити.

4.3. Графік конверсії

↑ До змістуДо панелі навігації

Конверсія A/B серед користувачів із відомим підсумковим статусом. Використано вже розраховані crn_a, crn_b та absolute_lift.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(8, 5))
rates = [crn_a * 100, crn_b * 100]
bars = ax.bar(['A', 'B'], rates, color=['#3568A8', '#D7792A'], width=0.5)
for bar, rate in zip(bars, rates):
    ax.text(bar.get_x() + bar.get_width() / 2, rate + 1.5,
            f'{rate:.2f}%'.replace('.', ','), ha='center', fontweight='bold')
ax.set_ylim(0, 100)
ax.set_title('Конверсія в преміум-підписку — SkillForge\n01–20 серпня 2025 року')
ax.set_xlabel('Група експерименту')
ax.set_ylabel('Конверсія, %')
ax.text(0.98, 0.94, f'Приріст B відносно A: +{absolute_lift:.2f} п.п.'.replace('.', ',', 1),
        transform=ax.transAxes, ha='right', va='top')
ax.grid(axis='y', alpha=0.2)
ax.set_axisbelow(True)
fig.tight_layout()
plt.show()
No description has been provided for this image

5. Статистична значущість

↑ До змістуДо панелі навігації

Перевіримо статистичну значущість різниці конверсій між групами A та B за допомогою двовибіркового z-тесту для пропорцій при рівні значущості α = 0.05.

from statsmodels.stats.proportion import proportions_ztest

count = [df_users_conver.loc['B', 'conversion_count'],
         df_users_conver.loc['A', 'conversion_count']]

nobs = [df_users_conver.loc['B', 'known_users'],
        df_users_conver.loc['A', 'known_users']]
z_stat, p_value = proportions_ztest(
    count=count,
    nobs=nobs,
    alternative='larger')

print(f'Z-статистика: {z_stat:.4f}')
print(f'p-значення: {p_value:.3e}')
Z-статистика: 21.3566
p-значення: 1.694e-101
alpha = 0.05

if p_value < alpha:
    print('Відхиляємо H0. Різниця статистично значуща.')
else:
    print('Не відхиляємо H0. Статистичних доказів недостатньо.')
Відхиляємо H0. Різниця статистично значуща.

За результатами одностороннього Z-тесту для двох пропорцій отримано Z-статистика = 21.3566 та p-значення < 0.001. Оскільки p-значення < 0.05, нульову гіпотезу відхиляємо.

Різниця конверсій між групами A (43.13%) та B (64.73%) є статистично значущою. Результати свідчать на користь вищої конверсії в тестовій групі B. Остаточний висновок щодо ефективності нової функції зробимо після перевірки впливу пропущених значень.

6. Довірчий інтервал 95%

↑ До змістуДо панелі навігації

Розрахуємо 95% довірчий інтервал для абсолютної різниці конверсій між групами B та A, щоб оцінити точність отриманого ефекту.

from statsmodels.stats.proportion import confint_proportions_2indep
ci_low, ci_high = confint_proportions_2indep(
    count1=count[0],
    nobs1=nobs[0],
    count2=count[1],
    nobs2=nobs[1],
    compare='diff',
    method='newcomb',
    alpha=0.05)
print(f'95% довірчий інтервал абсолютного приросту: [{ci_low * 100:.2f}; {ci_high * 100:.2f}] п.п.')
95% довірчий інтервал абсолютного приросту: [19.65; 23.52] п.п.

Висновок: Абсолютний приріст конверсії групи B відносно A становить +21.60 п.п. 95% довірчий інтервал для різниці конверсій знаходиться в межах від +19.65 до +23.52 п.п.

Оскільки довірчий інтервал повністю вищий за нуль, результати підтверджують статистично значущу перевагу групи B. Водночас перед остаточними бізнес-рекомендаціями необхідно перевірити вплив пропущених значень конверсії.

7. Аналіз чутливості

↑ До змістуДо панелі навігації

Перевіримо, наскільки пропущені значення conversion_status можуть вплинути на результати A/B-тесту.

У попередніх розрахунках користувачів із невідомим статусом було виключено. Оскільки частка пропусків відрізняється між групами A (2.88%) та B (1.74%), проведемо аналіз чутливості за трьома сценаріями:

  1. Основний сценарій: виключаємо користувачів із невідомим статусом, як в основному аналізі.
  2. Найгірший сценарій для B: усі невідомі статуси в групі A вважаємо конверсіями, а в групі B — неконверсіями.
  3. Найкращий сценарій для B: усі невідомі статуси в групі B вважаємо конверсіями, а в групі A — неконверсіями.

Для кожного сценарію розрахуємо конверсію обох груп та абсолютний приріст, щоб перевірити стійкість отриманих результатів.

7.1. Найгірший сценарій для групи B

↑ До змістуДо панелі навігації

# Загальна кількість користувачів у кожній групі
total_users = group_stats.groupby('group_label')['count'].sum()

# Кількість користувачів із невідомим статусом
missing = (group_stats[group_stats['conversion_status'].isna()].set_index('group_label')['count'])

# Підтверджені конверсії
conversions = df_users_conver['conversion_count']

# Найгірший сценарій для B
crn_a_worst = (conversions['A'] + missing['A']) / total_users['A'] * 100
crn_b_worst = conversions['B'] / total_users['B'] * 100

absolute_lift_worst = crn_b_worst - crn_a_worst

print(f'CR A: {crn_a_worst:.2f}%')
print(f'CR B: {crn_b_worst:.2f}%')
print(f'Абсолютний приріст: {absolute_lift_worst:.2f} п.п.')
CR A: 44.77%
CR B: 63.60%
Абсолютний приріст: 18.84 п.п.

7.2. Найкращий сценарій для групи B

↑ До змістуДо панелі навігації

# Найкращий сценарій для B

crn_a_case = conversions['A']  / total_users['A'] * 100
crn_b_case = (conversions['B'] + missing['B']) / total_users['B'] * 100


absolute_lift_case = crn_b_case - crn_a_case


print(f'CR A: {crn_a_case:.2f}%')
print(f'CR B: {crn_b_case:.2f}%')
print(f'Абсолютний приріст: {absolute_lift_case:.2f} п.п.')
CR A: 41.89%
CR B: 65.34%
Абсолютний приріст: 23.45 п.п.

7.3. Порівняння сценаріїв

↑ До змістуДо панелі навігації

table_scenario = pd.DataFrame({
    'Сценарій': ['Основний сценарій', 'Найгірший сценарій для B', 'Найкращий сценарій для B'],
    'Конверсія A, %': [crn_a * 100, crn_a_worst, crn_a_case],
    'Конверсія B, %': [crn_b * 100, crn_b_worst, crn_b_case],
    'Абсолютний приріст (п.п.)': [
        absolute_lift,
        absolute_lift_worst,
        absolute_lift_case]}).round(2)

table_scenario
Сценарій Конверсія A, % Конверсія B, % Абсолютний приріст (п.п.)
0 Основний сценарій 43.13 64.73 21.60
1 Найгірший сценарій для B 44.77 63.60 18.84
2 Найкращий сценарій для B 41.89 65.34 23.45

Висновок: Аналіз чутливості показав, що за всіх розглянутих сценаріїв конверсія групи B залишається вищою за групу A.

Абсолютний приріст становить від +18.84 п.п. у найгіршому сценарії до +23.45 п.п. у найкращому.

Отже, навіть за максимально несприятливого розподілу пропущених значень перевага групи B зберігається. Це свідчить про стійкість спостережуваного результату до невідомих статусів конверсії.

8. Аналіз сегментів

↑ До змістуДо панелі навігації

Проаналізуємо конверсію груп A та B за пристроями й країнами, щоб оцінити ефективність нового онбордингу в різних сегментах.

Для кожного сегмента розрахуємо кількість користувачів із відомим статусом конверсії, конверсію та абсолютний приріст. Пропущені статуси конверсії виключаємо, як і в основному аналізі.

8.1. Конверсія за пристроями

↑ До змістуДо панелі навігації

df_users_con_dev = df_users.groupby(['device_name','group_label'], dropna = False).agg(conversion_count = ('conversion_status','sum'),
                                                      known_users = ('conversion_status','count'))

df_users_con_dev = df_users_con_dev.unstack('group_label')

df_users_con_dev['crn_a'] = df_users_con_dev[('conversion_count', 'A')] / df_users_con_dev[('known_users', 'A')] * 100

df_users_con_dev['crn_b'] = df_users_con_dev[('conversion_count', 'B')] / df_users_con_dev[('known_users', 'B')] * 100

df_users_con_dev['absolute_lift'] = df_users_con_dev['crn_b'] - df_users_con_dev['crn_a']

(
    df_users_con_dev.round(2)
    .rename(columns={
        'conversion_count': 'Конверсії',
        'known_users': 'Користувачі з відомим статусом',
        'crn_a': 'Конверсія A, %',
        'crn_b': 'Конверсія B, %',
        'absolute_lift': 'Абсолютний приріст, п.п.'
    }, level=0)
    .rename_axis(index='Пристрій', columns=[None, 'Група'])
)
Конверсії Користувачі з відомим статусом Конверсія A, % Конверсія B, % Абсолютний приріст, п.п.
Група A B A B
Пристрій
Desktop 553.0 878.0 1368 1422 40.42 61.74 21.32
Mobile 1322.0 1920.0 2921 2869 45.26 66.92 21.66
Smartwatch 4.0 3.0 7 5 57.14 60.00 2.86
TV 3.0 9.0 5 13 60.00 69.23 9.23
Tablet 169.0 272.0 465 441 36.34 61.68 25.33
NaN 46.0 60.0 96 104 47.92 57.69 9.78

8.2. Конверсія за країнами

↑ До змістуДо панелі навігації

df_users_con_cont = df_users.groupby(['country_code','group_label'], dropna = False).agg(conversion_count = ('conversion_status','sum'),
                                                      known_users = ('conversion_status','count'))

df_users_con_cont = df_users_con_cont.unstack('group_label')

df_users_con_cont['crn_a'] = df_users_con_cont[('conversion_count', 'A')] / df_users_con_cont[('known_users', 'A')] * 100

df_users_con_cont['crn_b'] = df_users_con_cont[('conversion_count', 'B')] / df_users_con_cont[('known_users', 'B')] * 100

df_users_con_cont['absolute_lift'] = df_users_con_cont['crn_b'] - df_users_con_cont['crn_a']

(
    df_users_con_cont.round(2)
    .rename(columns={
        'conversion_count': 'Конверсії',
        'known_users': 'Користувачі з відомим статусом',
        'crn_a': 'Конверсія A, %',
        'crn_b': 'Конверсія B, %',
        'absolute_lift': 'Абсолютний приріст, п.п.'
    }, level=0)
    .rename_axis(index='Країна', columns=[None, 'Група'])
)
Конверсії Користувачі з відомим статусом Конверсія A, % Конверсія B, % Абсолютний приріст, п.п.
Група A B A B
Країна
BR 350.0 560.0 920 891 38.04 62.85 24.81
DE 343.0 573.0 922 954 37.20 60.06 22.86
FR 467.0 580.0 896 856 52.12 67.76 15.64
IN 414.0 611.0 875 889 47.31 68.73 21.41
US 374.0 591.0 890 938 42.02 63.01 20.98
XX 11.0 5.0 18 12 61.11 41.67 -19.44
NaN 138.0 222.0 341 314 40.47 70.70 30.23

8.3. Висновки сегментного аналізу

↑ До змістуДо панелі навігації

Сегментний аналіз проведено для перевірки ефективності нового онбордингу серед користувачів різних пристроїв та країн.

За пристроями: група B демонструє вищу конверсію в усіх категоріях. Найбільший Абсолютний приріст спостерігається для Tablet (+25.33 п.п.), а Mobile (+21.66 п.п.) і Desktop (+21.32 п.п.) мають схожі результати.

За країнами: у всіх п'яти основних країнах конверсія групи B також вища за A. Найбільший приріст зафіксовано у BR (+24.81 п.п.), найменший — у FR (+15.64 п.п.).

Для Smartwatch, TV та країни XX результати слід інтерпретувати обережно через малі вибірки. Категорії з невідомим пристроєм або країною не можна однозначно віднести до конкретних сегментів.

Загальний висновок: перевага нового онбордингу спостерігається не лише на рівні загальної конверсії, а й у всіх основних досліджених сегментах. Це додатково підтримує позитивну оцінку тестового варіанта B. Водночас відмінності у величині приросту між сегментами є описовими та потребують окремої статистичної перевірки.

↑ До змістуДо панелі навігації

Проаналізуємо щотижневу динаміку використання SkillForge, щоб визначити, як змінюється активність користувачів протягом експерименту та чи відрізняються тренди між групами A і B.

Для аналізу використаємо щоденні агреговані дані з v_daily_agg. Розділимо 20 днів експерименту на три періоди (7, 7 і 6 днів) та порівняємо середню кількість користувачів із записами за день для кожної групи.

9.1. Порівняння періодів експерименту

↑ До змістуДо панелі навігації

Використаємо кількість користувачів із записами daily. Порівняємо середню кількість таких користувачів за день у періодах 01–07.08, 08–14.08 та 15–20.08 (7, 7 і 6 днів), а також поденні графіки. Це показник наявності щоденних записів, а не основний CR у преміум-підписку.

start_date = df_daily['date'].min()

df_daily['experiment_week'] = ((df_daily['date'] - start_date).dt.days // 7 + 1)

df_daily.head(20)
date group_label cnt_users cnt_conversions cnt_unknown_convert cnt_known conver_rate experiment_week
0 2025-08-01 A 1938 173 17 1921 9.01 1
1 2025-08-01 B 1955 324 23 1932 16.77 1
2 2025-08-02 A 1042 111 11 1031 10.77 1
3 2025-08-02 B 1036 162 6 1030 15.73 1
4 2025-08-03 A 750 68 5 745 9.13 1
5 2025-08-03 B 747 127 7 740 17.16 1
6 2025-08-04 A 1231 111 12 1219 9.11 1
7 2025-08-04 B 1219 197 10 1209 16.29 1
8 2025-08-05 A 1487 119 12 1475 8.07 1
9 2025-08-05 B 1486 203 14 1472 13.79 1
10 2025-08-06 A 2095 181 17 2078 8.71 1
11 2025-08-06 B 2015 339 15 2000 16.95 1
12 2025-08-07 A 2206 212 22 2184 9.71 1
13 2025-08-07 B 2243 346 14 2229 15.52 1
14 2025-08-08 A 2004 192 15 1989 9.65 2
15 2025-08-08 B 1917 336 13 1904 17.65 2
16 2025-08-09 A 1026 106 9 1017 10.42 2
17 2025-08-09 B 993 148 6 987 14.99 2
18 2025-08-10 A 750 76 2 748 10.16 2
19 2025-08-10 B 733 142 9 724 19.61 2
weekly_stats = (df_daily.groupby(['experiment_week', 'group_label']).agg(days = ('date', 'nunique'), 
                                                                         total_user_days = ('cnt_users', 'sum')) .reset_index())

weekly_stats['avg_users_per_day'] = (weekly_stats['total_user_days'] / weekly_stats['days'])

weekly_stats.round(2).rename(columns={'experiment_week':'Тиждень', 'group_label':'Група', 'days':'Днів', 'total_user_days':'Користувачо-дні', 'avg_users_per_day':'Середня кількість за день'})
Тиждень Група Днів Користувачо-дні Середня кількість за день
0 1 A 7 10749 1535.57
1 1 B 7 10701 1528.71
2 2 A 7 10756 1536.57
3 2 B 7 10608 1515.43
4 3 A 6 8454 1409.00
5 3 B 6 8178 1363.00
import matplotlib.pyplot as plt

plt.figure(figsize=(10, 5))

for group, data in weekly_stats.groupby('group_label'):
    plt.plot(
        data['experiment_week'],
        data['avg_users_per_day'],
        marker='o',
        linewidth=2,
        label=f'Група {group}')

plt.title('Тижнева динаміка користувачів — A/B-тест')
plt.xlabel('Тиждень експерименту')
plt.ylabel('Середня кількість користувачів за день')
plt.xticks([1, 2, 3])
plt.legend()
plt.grid(alpha=0.3)

plt.show()
No description has been provided for this image

9.2. Щоденні коливання

↑ До змістуДо панелі навігації

days_stats = df_daily[['date', 'group_label', 'cnt_users']].copy()
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

plt.figure(figsize=(12, 5))

for group, data in days_stats.groupby('group_label'):
    plt.plot(
        data['date'],
        data['cnt_users'],
        marker='o',
        linewidth=2,
        label=f'Група {group}')

plt.title('Щоденна динаміка користувачів — A/B-тест')
plt.xlabel('Дата')
plt.ylabel('Кількість користувачів за день')

plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=2))
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d.%m'))

plt.legend()
plt.grid(alpha=0.3)
plt.xticks(rotation=45)
plt.tight_layout()

plt.show()
No description has been provided for this image

9.3. Висновки щодо трендів

↑ До змістуДо панелі навігації

Аналіз показав, що протягом перших двох тижнів середня щоденна кількість користувачів із записами залишалася відносно стабільною. На третьому тижні спостерігається зниження показника в обох групах.

Поденний аналіз виявив повторювану тижневу закономірність: найменша кількість записів припадає на неділі, а найбільша — на четверги. Динаміка груп A та B майже однакова, що свідчить про схожий характер щоденних коливань.

У третьому періоді (15–20 серпня) немає четверга, який має найвищі значення. Інший склад днів тижня може частково пояснювати нижче середнє; сама кількість днів не пояснює зниження середнього показника.

Загалом: на графіках групи A та B демонструють схожий характер щоденних коливань. Статистичну рівність трендів не перевіряли. При цьому показник відображає кількість користувачів із щоденними записами, а не основну конверсію в преміум-підписку.

10. Висновки та бізнес-рекомендації

↑ До змістуДо панелі навігації

10.1. Основні результати A/B-тесту

↑ До змістуДо панелі навігації

Метою експерименту SkillForge було визначити, чи підвищує новий онбординг (група B) конверсію користувачів у преміум-підписку порівняно з традиційним варіантом (група A).

За результатами аналізу отримано:

Метрика Результат
Конверсія A 43.13%
Конверсія B 64.73%
Абсолютний приріст +21.60 п.п.
Відносний приріст +50.08%
Z-статистика 21.3566
p-значення < 0.001
95% довірчий інтервал абсолютного приросту [+19.65; +23.52] п.п.

Односторонній Z-тест для двох пропорцій підтвердив статистично значущу різницю між групами (p-значення < 0.05). Нульову гіпотезу відхилено на користь вищої конверсії групи B.

10.2. Практична значущість

↑ До змістуДо панелі навігації

Отриманий приріст конверсії є суттєвим для основної бізнес-метрики — оформлення преміум-підписки.

За умови збереження результатів експерименту новий онбординг потенційно може забезпечувати приблизно 216 додаткових преміум-підписок на кожну 1 000 користувачів із відомим статусом конверсії порівняно з традиційним варіантом.

Водночас для оцінки фінансового ефекту необхідні додаткові дані про дохід від підписок, витрати на впровадження та утримання користувачів.

10.3. Додаткові результати та обмеження

↑ До змістуДо панелі навігації

Аналіз чутливості. Перевірка впливу невідомих статусів конверсії показала, що перевага групи B зберігається за всіх розглянутих сценаріїв. Абсолютний приріст становить від +18.84 до +23.45 п.п. Це свідчить про стійкість спостережуваної переваги до можливих значень пропущених статусів.

Сегментний аналіз. Вища конверсія групи B спостерігається на основних типах пристроїв та в усіх п'яти основних країнах. Найбільший Абсолютний приріст серед пристроїв зафіксовано для Tablet (+25.33 п.п.), серед основних країн — для BR (+24.81 п.п.). Результати малочисельних сегментів потребують обережної інтерпретації.

Тижнева динаміка. Групи A та B демонструють схожі коливання кількості користувачів зі щоденними записами. Помітна повторювана тижнева закономірність, однак наявні дані не дозволяють однозначно оцінити вплив онбордингу на активність користувачів.

Обмеження аналізу: основні статистичні розрахунки виконано для користувачів із відомим статусом конверсії. Також не проводилася окрема статистична перевірка відмінностей ефекту між сегментами. Тому результати сегментного аналізу слід розглядати як описові.

10.4. Бізнес-рекомендації

↑ До змістуДо панелі навігації

На основі отриманих результатів рекомендується:

  1. Розглядати новий онбординг B як пріоритетний варіант для впровадження, оскільки він демонструє статистично значуще та практично суттєве підвищення конверсії.
  2. Перед повним запуском перевірити якість даних і відстеження конверсій, зокрема причини пропущених значень та коректність реєстрації преміум-підписок.
  3. Впроваджувати зміни поетапно, контролюючи конверсію, утримання користувачів, скасування підписок та інші важливі бізнес-показники.
  4. Продовжити моніторинг за пристроями та країнами, приділяючи увагу сегментам із невеликими вибірками або нетиповими результатами.

10.5. Загальний висновок

↑ До змістуДо панелі навігації

A/B-тест SkillForge показав, що новий онбординг групи B має суттєву перевагу над традиційним варіантом за конверсією у преміум-підписку.

Зростання конверсії на 21.60 п.п. (+50.08% відносно групи A) є статистично значущим і потенційно цінним для бізнесу. Результати додаткових перевірок підтримують цей висновок.

Новий онбординг рекомендовано розглядати для поетапного впровадження після перевірки якості даних і контролю додаткових бізнес-метрик.

Код і збережені узагальнені результати з авторського ноутбука. Приклади індивідуальних записів не публікуються.