← Назад к вопросам
Как рассчитать конверсию из просмотра товара в покупку?
2.0 Middle🔥 181 комментариев
#SQL и базы данных#Аналитика и метрики
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Расчёт конверсии из просмотра товара в покупку
Конверсия — это ключевая метрика e-commerce, которая показывает, какой процент пользователей, посетивших товар, его купили. Data Engineer отвечает за сбор и обработку данных для точного расчёта этой метрики.
Определение метрики
Конверсия (conversion rate) рассчитывается по формуле:
Conversion Rate = (Количество покупок) / (Количество просмотров товара) * 100%
Например, если товар просмотрели 1000 раз, а купили 50 раз, конверсия = 50 / 1000 * 100% = 5%.
Сбор данных о просмотрах и покупках
# Пример: отправка событий в аналитику
# Frontend код
const trackProductView = (productId, userId) => {
fetch('/api/v1/analytics/event', {
method: 'POST',
body: JSON.stringify({
event_type: 'product_view',
product_id: productId,
user_id: userId,
timestamp: new Date().toISOString(),
session_id: getSessionId()
})
})
}
const trackPurchase = (productId, userId, amount) => {
fetch('/api/v1/analytics/event', {
method: 'POST',
body: JSON.stringify({
event_type: 'purchase',
product_id: productId,
user_id: userId,
amount: amount,
timestamp: new Date().toISOString(),
session_id: getSessionId()
})
})
}
ETL для обработки событий
# Backend: приём и сохранение событий
from fastapi import FastAPI, HTTPException
from sqlalchemy import create_engine, Column, String, Integer, Float, DateTime
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from datetime import datetime
import json
app = FastAPI()
Base = declarative_base()
class AnalyticsEvent(Base):
__tablename__ = 'events'
event_id = Column(Integer, primary_key=True)
event_type = Column(String(50)) # 'product_view' или 'purchase'
product_id = Column(String(50))
user_id = Column(String(50))
session_id = Column(String(100))
amount = Column(Float, nullable=True) # для покупок
timestamp = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow())
@app.post('/api/v1/analytics/event')
async def log_event(event: dict, db: Session):
"""
Логирует событие просмотра или покупки
"""
db_event = AnalyticsEvent(
event_type=event['event_type'],
product_id=event['product_id'],
user_id=event['user_id'],
session_id=event.get('session_id'),
amount=event.get('amount'),
timestamp=datetime.fromisoformat(event['timestamp'])
)
db.add(db_event)
db.commit()
return {'ok': True}
SQL для расчёта конверсии
-- Базовая таблица с событиями
CREATE TABLE events (
event_id INT PRIMARY KEY,
event_type VARCHAR(50), -- 'product_view' или 'purchase'
product_id VARCHAR(50),
user_id VARCHAR(50),
session_id VARCHAR(100),
amount DECIMAL(10, 2),
timestamp TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
-- Простой расчёт конверсии по товару
CREATE VIEW v_product_conversion AS
SELECT
product_id,
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) as view_count,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END) as purchase_count,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END)::FLOAT /
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) * 100 as conversion_rate
FROM events
WHERE event_type IN ('product_view', 'purchase')
GROUP BY product_id;
-- Конверсия по дням
CREATE VIEW v_daily_conversion AS
SELECT
DATE(timestamp) as date,
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) as daily_views,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END) as daily_purchases,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END)::FLOAT /
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) * 100 as daily_conversion_rate
FROM events
WHERE event_type IN ('product_view', 'purchase')
GROUP BY 1
ORDER BY 1 DESC;
Учёт временного промежутка
Важно: пользователь может просмотреть товар, но купить его через день или неделю. Нужно учитывать это в расчётах:
-- Конверсия с учётом временного окна (24 часа между просмотром и покупкой)
CREATE VIEW v_conversion_within_24h AS
SELECT
p.product_id,
COUNT(DISTINCT p.user_id) as views_in_period,
COUNT(DISTINCT CASE
WHEN EXISTS (
SELECT 1 FROM events pu
WHERE pu.event_type = 'purchase'
AND pu.product_id = p.product_id
AND pu.user_id = p.user_id
AND pu.timestamp BETWEEN p.timestamp AND p.timestamp + INTERVAL '24 hours'
) THEN p.user_id
END) as purchases_within_24h,
COUNT(DISTINCT CASE
WHEN EXISTS (
SELECT 1 FROM events pu
WHERE pu.event_type = 'purchase'
AND pu.product_id = p.product_id
AND pu.user_id = p.user_id
AND pu.timestamp BETWEEN p.timestamp AND p.timestamp + INTERVAL '24 hours'
) THEN p.user_id
END)::FLOAT / COUNT(DISTINCT p.user_id) * 100 as conversion_24h
FROM events p
WHERE p.event_type = 'product_view'
GROUP BY 1;
Атрибуция покупки к просмотру
-- Связываем каждую покупку с соответствующим просмотром
CREATE TABLE conversion_funnel AS
SELECT
p.event_id as view_event_id,
pu.event_id as purchase_event_id,
p.product_id,
p.user_id,
p.session_id,
p.timestamp as view_timestamp,
pu.timestamp as purchase_timestamp,
EXTRACT(EPOCH FROM (pu.timestamp - p.timestamp)) / 3600 as hours_to_purchase,
pu.amount as purchase_amount
FROM events p
JOIN events pu ON
p.product_id = pu.product_id
AND p.user_id = pu.user_id
AND p.event_type = 'product_view'
AND pu.event_type = 'purchase'
AND pu.timestamp > p.timestamp
AND pu.timestamp <= p.timestamp + INTERVAL '30 days' -- конверсионное окно
ORDER BY p.timestamp DESC;
-- Берём только первую покупку после просмотра
CREATE TABLE conversion_funnel_deduplicated AS
SELECT
DISTINCT ON (view_event_id) *
FROM conversion_funnel
ORDER BY view_event_id, purchase_timestamp ASC;
Расчёт конверсии по каналам и когортам
-- Конверсия по источнику трафика
CREATE VIEW v_conversion_by_source AS
SELECT
u.traffic_source,
COUNT(DISTINCT CASE WHEN e.event_type = 'product_view' THEN e.user_id END) as views,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN e.user_id END) as purchases,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN e.user_id END)::FLOAT /
COUNT(DISTINCT CASE WHEN e.event_type = 'product_view' THEN e.user_id END) * 100 as conversion_rate
FROM events e
JOIN users u ON e.user_id = u.user_id
WHERE e.event_type IN ('product_view', 'purchase')
GROUP BY u.traffic_source;
-- Когортный анализ (по дате первого посещения)
CREATE VIEW v_cohort_conversion AS
SELECT
DATE_TRUNC('week', u.created_at) as cohort_week,
COUNT(DISTINCT CASE WHEN e.event_type = 'product_view' THEN e.user_id END) as cohort_views,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN e.user_id END) as cohort_purchases,
COUNT(DISTINCT CASE WHEN e.event_type = 'purchase' THEN e.user_id END)::FLOAT /
COUNT(DISTINCT CASE WHEN e.event_type = 'product_view' THEN e.user_id END) * 100 as cohort_conversion
FROM events e
JOIN users u ON e.user_id = u.user_id
WHERE e.event_type IN ('product_view', 'purchase')
GROUP BY 1
ORDER BY 1 DESC;
Мониторинг аномалий в конверсии
# Обнаружение резкого падения конверсии
import pandas as pd
from scipy import stats
def detect_conversion_anomaly():
"""
Обнаруживает аномальное падение конверсии
"""
query = """
SELECT
DATE(timestamp) as date,
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) as views,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END) as purchases,
COUNT(DISTINCT CASE WHEN event_type = 'purchase' THEN user_id END)::FLOAT /
COUNT(DISTINCT CASE WHEN event_type = 'product_view' THEN user_id END) * 100 as conversion_rate
FROM events
WHERE event_type IN ('product_view', 'purchase')
AND timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY 1
ORDER BY 1
"""
df = pd.read_sql(query, db_engine)
# Вычисляем z-score для последнего дня
mean = df['conversion_rate'][:-1].mean()
std = df['conversion_rate'][:-1].std()
last_day_rate = df['conversion_rate'].iloc[-1]
z_score = abs((last_day_rate - mean) / std)
if z_score > 2.5: # Порог аномалии
send_alert(
f"Conversion rate anomaly detected: {last_day_rate:.2f}% "
f"(expected: {mean:.2f}%, z-score: {z_score:.2f})"
)
return True
return False
Best Practices
- Дефиниция: четко определи, что считается "просмотром" и "покупкой"
- Сеансы: используй session_id для корректного подсчёта уникальных пользователей
- Фильтры: исключи ботов и тестовые покупки из расчётов
- Временные окна: выбери подходящее окно для атрибуции (24ч, 7дней, 30дней)
- Когортный анализ: отслеживай изменение конверсии для разных когорт пользователей
- Мониторинг: наблюдай за аномалиями в реальном времени
- Сегментация: рассчитывай конверсию отдельно по каналам, устройствам, географии