Как посчитать K-factor в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем K-factor

K-factor — это количественное измерение виральности продукта. Каждый новый пользователь приводит в среднем X друзей, каждый из них — ещё X и так далее. K = 1 → продукт растёт экспоненциально без единого маркетингового рубля. K = 0.5 → органического роста нет, надо платить.

На собеседовании для growth-команд (Notion, Miro, Slack-подобные) это одна из главных тем. «Посчитай K-factor у нас», «как поменять, чтобы K > 1», «какое cycle time разумно». Без понимания формулы K = invites × conversion вы не пройдёте.

Типичные ошибки в подсчёте: считать приглашения, а не регистрации (один invite послать легко, но конверсия 5%), игнорировать cycle time (K=1 с циклом 30 дней значит удвоение через 30 дней, не сразу), не сегментировать по каналу share (WhatsApp vs Telegram vs ссылка).

В статье — готовые SQL-запросы:

  • Базовый K-factor = invites per user × conversion rate
  • Invites per active user
  • Conversion: % приглашений, приведших к регистрации
  • Cycle time — сколько дней от invite до signup
  • K-factor по каналам share
  • Cohort K-factor (улучшается ли)

Схема: users(id, invited_by_user_id, signup_at), invites(invite_id, sender_user_id, sent_at, converted_user_id).

1. Формула

K-factor = Invites per user × Conversion rate
  • Invites per user: среднее приглашений на активного пользователя
  • Conversion: % приглашений, ставших регистрациями

2. Базовый K-factor

WITH invites_sent AS (
    SELECT
        sender_user_id,
        COUNT(*) AS invites_sent
    FROM invites
    WHERE sent_at >= NOW() - INTERVAL '30 days'
    GROUP BY sender_user_id
),
active_users AS (
    SELECT COUNT(DISTINCT user_id) AS active
    FROM events
    WHERE event_at >= NOW() - INTERVAL '30 days'
),
conversion_stats AS (
    SELECT
        COUNT(*) AS total_invites,
        COUNT(converted_user_id) AS converted_invites
    FROM invites
    WHERE sent_at >= NOW() - INTERVAL '30 days'
)
SELECT
    -- среднее invites на активного
    SUM(invites_sent)::FLOAT / (SELECT active FROM active_users) AS invites_per_user,
    -- conversion
    (SELECT converted_invites::FLOAT / NULLIF(total_invites, 0) FROM conversion_stats) AS conversion,
    -- K-factor
    SUM(invites_sent)::FLOAT / (SELECT active FROM active_users) *
        (SELECT converted_invites::FLOAT / NULLIF(total_invites, 0) FROM conversion_stats) AS k_factor
FROM invites_sent;

3. Invites per user

SELECT
    DATE_TRUNC('week', sent_at) AS week,
    COUNT(*)::FLOAT / COUNT(DISTINCT sender_user_id) AS invites_per_sender
FROM invites
GROUP BY 1
ORDER BY 1;

Но лучше — на активного пользователя (не только отправителя):

WITH active AS (
    SELECT DISTINCT DATE_TRUNC('week', event_at) AS week, user_id
    FROM events
),
sent AS (
    SELECT DATE_TRUNC('week', sent_at) AS week, sender_user_id AS user_id, COUNT(*) AS cnt
    FROM invites
    GROUP BY 1, 2
)
SELECT
    a.week,
    SUM(COALESCE(s.cnt, 0))::FLOAT / COUNT(DISTINCT a.user_id) AS invites_per_active_user
FROM active a
LEFT JOIN sent s USING (week, user_id)
GROUP BY a.week
ORDER BY a.week;

4. Conversion rate приглашений

SELECT
    DATE_TRUNC('month', sent_at) AS month,
    COUNT(*) AS invites_sent,
    COUNT(converted_user_id) AS conversions,
    100.0 * COUNT(converted_user_id) / COUNT(*) AS conversion_pct
FROM invites
GROUP BY 1
ORDER BY 1;

5. Cycle time — сколько от invite до signup

SELECT
    AVG(EXTRACT(EPOCH FROM (u.signup_at - i.sent_at)) / 86400) AS avg_cycle_days,
    PERCENTILE_CONT(0.5) WITHIN GROUP (
        ORDER BY EXTRACT(EPOCH FROM (u.signup_at - i.sent_at)) / 86400
    ) AS median_cycle_days
FROM invites i
JOIN users u ON u.id = i.converted_user_id
WHERE i.converted_user_id IS NOT NULL;

Короткий cycle (< 1 дня) + K > 1 → взрывной рост. Длинный cycle — даже K > 1 растёт медленно.

6. K-factor по каналам share

WITH invites_by_channel AS (
    SELECT
        share_channel,
        COUNT(*) AS sent,
        COUNT(converted_user_id) AS conversions
    FROM invites
    GROUP BY share_channel
)
SELECT
    share_channel,
    sent,
    conversions,
    100.0 * conversions / sent AS conversion_pct
FROM invites_by_channel
ORDER BY conversion_pct DESC;

WhatsApp обычно конвертит лучше email. Email лучше SMS. Copy-link хуже.

7. K-factor по cohort

WITH cohorts AS (
    SELECT id AS user_id, DATE_TRUNC('month', signup_at) AS cohort
    FROM users
),
cohort_invites AS (
    SELECT
        c.cohort,
        COUNT(DISTINCT c.user_id) AS cohort_size,
        COUNT(i.invite_id) AS total_invites,
        COUNT(i.converted_user_id) AS converted
    FROM cohorts c
    LEFT JOIN invites i ON i.sender_user_id = c.user_id
    GROUP BY c.cohort
)
SELECT
    cohort,
    total_invites::FLOAT / cohort_size AS invites_per_user,
    converted::FLOAT / NULLIF(total_invites, 0) AS conversion,
    total_invites::FLOAT / cohort_size *
        converted::FLOAT / NULLIF(total_invites, 0) AS k_factor
FROM cohort_invites
ORDER BY cohort;

8. K-factor для виральной петли

Если растите, отслеживайте unit-dynamics:

users_next_month = users × K (если K < 1, равновесие)

При K = 0.5 и cycle 30 дней:

  • Месяц 1: 100
  • Месяц 2: 100 + 50 = 150
  • Месяц 3: 150 + 75 = 225
  • Но это рост, а не виральность (один user приводит <1 в долгую).

Частые ошибки

Считать invites, а не converted invites

K = invites × conversion, не просто invites.

Игнорировать cycle time

K = 1 с cycle year ≠ K = 1 с cycle day.

Не учитывать active vs all users

Invites per user от неактивных = разбавление.

Considering self-invites

Пользователи могут приглашать себя со второго аккаунта → фильтр по FK / IP.

Связанные темы

FAQ

K > 1 реально?

Редкость. Большинство успешных продуктов K = 0.3-0.7.

K-factor для B2B?

Да. Slack, Notion, Figma растут через invites (seats).

Cycle time влияет?

Критично. K=1 с циклом 30 дней ≠ K=1 с циклом 1 день.

Как повысить K?

Два рычага: invites per user (больше мотивации) + conversion (лучший onboarding для приглашённых).


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.