Как посчитать K-factor в SQL
sessions 5 строк. Значения utm_source: 'google', NULL, 'email', NULL, NULL. Чему равны COUNT(*) и COUNT(utm_source)?Содержание:
Зачем K-factor
K-factor — количественная мера виральности: в среднем каждый пользователь продукта приводит столько-то новых. При K ≥ 1 продукт растёт сам, без денег на маркетинг. При K < 1 вирусность играет роль «множителя» для платного привлечения, но органического взрыва не даёт.
В B2C соцсетях и B2B-инструментах (Slack, Notion, Miro и аналоги) K-factor — одна из центральных тем на собесах в growth-командах. Ошибиться в подсчёте легко: посчитать приглашения вместо фактических регистраций, забыть про cycle time, не вычесть self-invites. Ниже — формулы, SQL-запросы и самые частые грабли.
Формула
K-factor = invites_per_user × conversion_rate- invites_per_user — среднее число приглашений на активного пользователя.
- conversion_rate — доля приглашений, которые стали регистрациями.
Важен не только K, но и cycle time — время от отправки приглашения до регистрации приглашённого. При одинаковом K короткий цикл даёт более быстрый рост.
Схема данных
users (id, invited_by_user_id, signup_at)
invites (invite_id, sender_user_id, sent_at, converted_user_id, share_channel)
events (user_id, event_name, event_at)1. Базовый K-factor за период
WITH sent AS (
SELECT
COUNT(*) AS total_invites,
COUNT(converted_user_id) AS converted_invites
FROM invites
WHERE sent_at >= NOW() - INTERVAL '30 days'
),
active_users AS (
SELECT COUNT(DISTINCT user_id) AS active_cnt
FROM events
WHERE event_at >= NOW() - INTERVAL '30 days'
)
SELECT
s.total_invites::NUMERIC / NULLIF(a.active_cnt, 0) AS invites_per_user,
s.converted_invites::NUMERIC / NULLIF(s.total_invites, 0) AS conversion_rate,
(s.total_invites::NUMERIC / NULLIF(a.active_cnt, 0))
* (s.converted_invites::NUMERIC / NULLIF(s.total_invites, 0)) AS k_factor
FROM sent s, active_users a;Обычно имеет смысл считать K по активным пользователям (знаменатель active_users), а не по отправителям: иначе легко получить красивую, но малоинформативную картину, где 5% ядра приглашает много и держит среднее.
2. Приглашения на активного пользователя по неделям
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,
COALESCE(SUM(s.cnt), 0)::NUMERIC / 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;3. Конверсия приглашений
SELECT
DATE_TRUNC('month', sent_at)::DATE AS month,
COUNT(*) AS invites_sent,
COUNT(converted_user_id) AS conversions,
100.0 * COUNT(converted_user_id) / NULLIF(COUNT(*), 0) AS conversion_pct
FROM invites
GROUP BY 1
ORDER BY 1;Если конверсия падает, чаще причина не в сетях и не в оформлении, а в онбординге после перехода.
4. Cycle time
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;Короткий цикл критичен для быстрого роста: при одном и том же K продукт с циклом «часы» растёт на порядок быстрее, чем с циклом «недели».
5. K-factor по каналам распространения
SELECT
share_channel,
COUNT(*) AS sent,
COUNT(converted_user_id) AS conversions,
100.0 * COUNT(converted_user_id) / NULLIF(COUNT(*), 0) AS conversion_pct
FROM invites
GROUP BY share_channel
ORDER BY conversion_pct DESC;Обычно каналы с «тёплой» идентификацией (мессенджер, где видно отправителя) конвертят лучше «холодных» (копируемая ссылка), но это нужно проверять на своих данных — зависит от аудитории и дизайна приглашения.
6. K-factor по когортам
WITH cohorts AS (
SELECT id AS user_id, DATE_TRUNC('month', signup_at)::DATE AS cohort
FROM users
),
cohort_stats 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 conversions
FROM cohorts c
LEFT JOIN invites i ON i.sender_user_id = c.user_id
GROUP BY c.cohort
)
SELECT
cohort,
cohort_size,
total_invites::NUMERIC / NULLIF(cohort_size, 0) AS invites_per_user,
conversions::NUMERIC / NULLIF(total_invites, 0) AS conversion_rate,
(total_invites::NUMERIC / NULLIF(cohort_size, 0))
* (conversions::NUMERIC / NULLIF(total_invites, 0)) AS k_factor
FROM cohort_stats
ORDER BY cohort;Когортный K-factor помогает увидеть, улучшает ли продукт виральность со временем или только раздувает общий трафик за счёт платных каналов.
7. Что означает K < 1
K = 0.5 не значит «экспоненциально растём». Это значит, что каждый пользователь приводит в среднем 0,5 нового — то есть исходный поток приглашений постепенно затухает. На практике при K < 1 виральная петля работает как мультипликатор к платному привлечению:
итоговое число пользователей ≈ paid_users × 1 / (1 − K)Например, при K = 0.5 на каждого платного пользователя в итоге приходит ещё один органический «виральный». Это всё равно полезно — но это не «бесплатный экспоненциальный рост», которого ждут от K ≥ 1.
Примеры с собеседований
K-factor на собесе в growth-командах проверяют на понимании петли.
«Что значит K = 0.5 — растём вирально или нет?» Слабый ответ — «да, растём». Сильный: K < 1 не даёт экспоненциального роста, петля затухает; K = 0.5 работает как множитель к платному привлечению (итог ≈ paid × 1/(1−K), то есть удвоение), но не заменяет его.
«Как считать K и на что делить?» Сильный ответ: K = invites_per_user × conversion_rate, причём по активным пользователям, а не по отправителям, и по фактическим регистрациям, а не по отправленным приглашениям. Иначе метрика завышена.
«Почему cycle time не менее важен, чем сам K?» Сильный ответ: при одинаковом K продукт с циклом в часы растёт на порядок быстрее, чем с циклом в недели — скорость петли определяет темп роста. Прогнать такие вопросы можно в Карьернике.
Частые ошибки
1. Считать приглашения вместо конверсий
K-factor меряется по конвертировавшимся приглашениям, а не по отправленным. Отправить ссылку дёшево, зарегистрироваться по ней — уже конверсия.
2. Игнорировать cycle time
K = 1 с циклом «30 дней» и K = 1 с циклом «сутки» — разный бизнес: второй даёт взрывной рост за недели, первый — только за месяцы.
3. Знаменатель — все пользователи, а не активные
Если делить invites на всех зарегистрировавшихся (включая «спящих»), invites_per_user систематически занижается.
4. Self-invites
Пользователи приглашают себя со второго адреса или устройства. Полезно фильтровать по IP, device_id, email-доменам или эвристикам типа «приглашение отправлено и конвертировано в течение 5 минут с того же устройства».
5. Чистый K без контекста
K = 0,4 в вакууме не говорит ни о чём. Разумно показывать его рядом с cycle time, invites_per_user, conversion_rate и динамикой по когортам.
Связанные темы
FAQ
K > 1 вообще бывает?
Редко и обычно на коротких промежутках у продуктов с сильной утилитарной виральностью (мессенджеры на старте, кросс-платформенные соцсети в фазе взрывного роста). В устойчивом состоянии большинство успешных продуктов держат K < 1 и компенсируют платным привлечением.
K-factor важен для B2B?
Да. В B2B-продуктах виральность часто идёт через «приглашение коллег в рабочее пространство» — и это один из ключевых драйверов роста инструментов типа Slack, Miro, Notion.
Cycle time сильно важен?
Критично. При прочих равных чем короче цикл, тем быстрее растёт пул пользователей.
Как повышать K?
Два рычага: invites_per_user (больше мотивации и удобства делиться) и conversion_rate (лучший онбординг и чёткая ценность для приглашённого до регистрации).