Как посчитать MAP@k в SQL

Закрепи формулу map at k в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать map at k в Telegram

Зачем MAP

Mean Average Precision (MAP) — стандартная метрика для рейтинговых систем с множественными релевантными документами. В отличие от MRR (только first), MAP усредняет precision на каждой позиции, где релевантный документ. MAP высокий = и first, и middle, и all relevant документы на верху.

Применяется в search, информационный retrieval, рейтинг продуктов в e-com.

Average Precision

Для одного запроса:

AP@k = Σ over relevant ranks r ≤ k of (precision@r) / total_relevant

То есть: для каждой позиции, где есть релевантный, считаем precision до этой позиции (включая её). Усредняем.

MAP@k в SQL

WITH ranked AS (
    SELECT
        query_id,
        rank,
        is_relevant,
        SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END) OVER (
            PARTITION BY query_id ORDER BY rank
        ) AS relevant_so_far,
        rank AS k
    FROM search_results
    WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
      AND rank <= 10
),
precision_at_relevant AS (
    SELECT
        query_id,
        rank,
        relevant_so_far::NUMERIC / rank AS precision_at_rank
    FROM ranked
    WHERE is_relevant = TRUE
),
ap_per_query AS (
    SELECT
        query_id,
        SUM(precision_at_rank) / NULLIF(COUNT(*), 0) AS ap
    FROM precision_at_relevant
    GROUP BY query_id
),
all_queries AS (
    SELECT DISTINCT query_id FROM search_results
    WHERE query_date >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT
    AVG(COALESCE(ap, 0)) AS map_at_10,
    COUNT(*) AS total_queries
FROM all_queries
LEFT JOIN ap_per_query USING (query_id);

Запросы без релевантных получают AP = 0.

MAP vs MRR

WITH ranked AS (
    SELECT
        query_id, rank, is_relevant,
        SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END) OVER (PARTITION BY query_id ORDER BY rank) AS relevant_so_far
    FROM search_results
    WHERE rank <= 10
),
ap_per_query AS (
    SELECT
        query_id,
        SUM(relevant_so_far::NUMERIC / rank) FILTER (WHERE is_relevant)
        / NULLIF(SUM(CASE WHEN is_relevant THEN 1 ELSE 0 END), 0) AS ap
    FROM ranked
    GROUP BY query_id
),
first_relevant AS (
    SELECT query_id, MIN(rank) AS first_rank
    FROM ranked
    WHERE is_relevant
    GROUP BY query_id
)
SELECT
    AVG(1.0 / fr.first_rank) AS mrr,
    AVG(COALESCE(ap.ap, 0)) AS map
FROM first_relevant fr
LEFT JOIN ap_per_query ap USING (query_id);

MAP всегда ≤ MRR (потому что average precision учитывает и положения после first relevant).

Закрепи формулу map at k в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать map at k в Telegram

MAP vs NDCG

MAP NDCG
Бинарная релевантность Yes Можно
Градированная (0/1/2/3) Нет Yes
Позиция важна? Yes Yes (log)
Применение Информационный retrieval Web search, recsys

Для бинарных задач — MAP проще. Для градированных — NDCG.

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

Ошибка 1. MAP на single relevant. Если у каждого запроса 1 релевантный, MAP = MRR. Используйте MRR проще.

Ошибка 2. Считать AP на запросах без релевантных. Деление на 0. Эти запросы AP = 0 явно.

Ошибка 3. precision@rank на total documents. Precision = relevant_so_far / rank. Не «relevant / all in DB».

Ошибка 4. Игнорировать total_relevant unbounded. Стандартная MAP делит на total_relevant. Если есть docs выше top-k, надо учесть. AP@k без normalization — иногда другая формула.

Ошибка 5. CI без bootstrap. MAP variance высокая. Bootstrap 1000 запросов для CI.

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

FAQ

MAP@k vs MAP?

MAP@k обрезает по top-k. MAP — на весь ранкинг.

Какой MAP хороший?

В TREC web search 0.4-0.6 — strong. В recsys 0.1-0.3 — норма.

MAP для бинарной vs градированной?

Лучше для бинарной. Для градированной — NDCG.

MAP считается на one query или multi?

AP — на один запрос. MAP — среднее AP по всем запросам.

Можно ли MAP в feed recsys?

Да, если у каждого юзера есть ground truth relevant items.