главная/Линейное преобразование для расчёта рейтинга пользователя
Линейное преобразование

Линейное преобразование для расчёта рейтинга пользователя

Во многих backend-системах возникает одна и та же задача: у пользователя есть несколько метрик, а нам нужно одно число, по которому можно принимать решения.

Например:

  • кто VIP
  • кого продвигать
  • кому дать скидку
  • кто особо не активен

Для этого идеально подходит линейное преобразование.

Линейное преобразование — это способ: превратить набор показателей в одну итоговую оценку, учитывая важность каждого показателя.

Математически оно выглядит так:

score = X @ W + b
  • X — вектор признаков пользователя
  • W -веса (важность каждого признака)
  • b — смещение (порог / базовый уровень)

Зачем вообще нужно преобразование

Без преобразования у нас есть набор разрозненных данных:

  • частота покупок
  • сумма покупок
  • давность последней активности

С ними сложно:

  • сортировать пользователей
  • сравнивать между собой
  • принимать автоматические решения

Линейное преобразование решает эту проблему, сводя всё к одному числу — рейтингу.

Определяем данные для расчета

orders_30d        — количество заказов за 30 дней
spend_30d         — сумма покупок за 30 дней
days_since_last   — дней с последней покупки
avg_check_90d     — средний чек за 90 дней

Это и есть наш вектор признаков:

X = [orders_30d, spend_30d, days_since_last, avg_check_90d]

Нормализуем

Признаки находятся в разных масштабах:

  • заказы — единицы
  • суммы — тысячи
  • дни — десятки

Если их не привести к одному диапазону, большие числа «сломают» формулу.

Простейшая нормализация:

orders_norm      = min(orders_30d, 10) / 10
spend_norm       = min(spend_30d, 50000) / 50000
recency_norm     = min(days_since_last, 60) / 60
avg_check_norm   = min(avg_check_90d, 10000) / 10000

Теперь все значения лежат в диапазоне 0..1.

Задаём веса — бизнес-смысл формулы

Веса отвечают на вопрос:

что для нас важнее?

Допустим:

  • частота покупок — очень важна
  • сумма — важна
  • давность последней покупки — негативный фактор
  • средний чек — дополнительный плюс

Тогда:

W = [ 0.45, 0.35, -0.50, 0.20 ]
b = 0

Знак веса важен:

  • + усиливает вклад признака
  • уменьшает рейтинг при росте признака

Считаем рейтинг пользователя

Формула:

score =
orders_norm    * 0.45 +
spend_norm     * 0.35 +
recency_norm   * (-0.50) +
avg_check_norm * 0.20

Пример пользователя:

  • 6 заказов → 0.6
  • 18 000 ₽ → 0.36
  • 3 дня → 0.05
  • средний чек 4200 → 0.42

Подставляем:

score =
0.6  * 0.45 +
0.36 * 0.35 +
0.05 * (-0.50) +
0.42 * 0.20
= 0.455

0.455 — рейтинг пользователя

Пример расчета на PHP

// Входные данные
$userData = [
    'orders_30d'        => 6,      // заказов за 30 дней
    'spend_30d'         => 18000,   // сумма покупок за 30 дней (₽)
    'days_since_last'   => 3,      // дней с последней покупки
    'avg_check_90d'     => 4200,   // средний чек за 90 дней
];

// Настройки веса
$config = [
    'max_orders_30d'      => 10,
    'max_spend_30d'       => 50000,
    'max_days_since_last' => 60,
    'max_avg_check_90d'   => 10000,

    'weights' => [
        'orders_30d'      => 0.45,
        'spend_30d'       => 0.35,
        'days_since_last' => -0.50,
        'avg_check_90d'   => 0.20,
    ],

    'bias' => 0.0,
];

// Функции расчета
function clamp01(float $value): float {
    return max(0.0, min(1.0, $value));
}

function normalizeUserData(array $data, array $config): array {
    return [
        'orders_30d' => clamp01(
            min($data['orders_30d'], $config['max_orders_30d'])
            / $config['max_orders_30d']
        ),

        'spend_30d' => clamp01(
            min($data['spend_30d'], $config['max_spend_30d'])
            / $config['max_spend_30d']
        ),

        'days_since_last' => clamp01(
            min($data['days_since_last'], $config['max_days_since_last'])
            / $config['max_days_since_last']
        ),

        'avg_check_90d' => clamp01(
            min($data['avg_check_90d'], $config['max_avg_check_90d'])
            / $config['max_avg_check_90d']
        ),
    ];
}

function calculateUserRating(array $normalized, array $weights, float $bias = 0.0): float {
    $score = 0.0;

    foreach ($weights as $key => $weight) {
        $score += ($normalized[$key] ?? 0.0) * $weight;
    }

    return $score + $bias;
}

// Запускаем
$normalized = normalizeUserData($userData, $config);
$rating = calculateUserRating($normalized, $config['weights'], $config['bias']);

echo "Normalized data:\n";
print_r($normalized);

echo "\nUser rating: " . round($rating, 3) . PHP_EOL;

Результат

Array
(
    [orders_30d] => 0.6
    [spend_30d] => 0.36
    [days_since_last] => 0.05
    [avg_check_90d] => 0.42
)

User rating: 0.455