Перейти к содержимому

Концентрация поставщиков: HHI и топ-5

ABC и XYZ смотрят на категории. Следующий вопрос — на поставщиков: от кого зависит портфель. Если половину закупок закрывает одна компания, уход поставщика или смена собственника останавливает работу. Если поставщиков много и они примерно равны, риск ниже, но и торговаться объёмом труднее.

Две меры отвечают на этот вопрос по-разному:

  • доля топ-5 — какая часть затрат уходит пяти крупнейшим поставщикам;
  • индекс Херфиндаля (HHI) — сумма квадратов долей всех поставщиков, умноженная на 10 000.

HHI учитывает не только лидеров. Он равен 10 000, если деньги у одного поставщика, и близок к нулю, если их делят сотни равных. При шести равных поставщиках индекс равен 10 000 / 6 ≈ 1667 — это точка отсчёта для чтения результата: выше неё доли неровные, ниже — ровные.

purchases даёт сумму по поставщику, suppliers — имя для отчёта. Считаем по всем заказам за 24 месяца. В справочнике 60 записей, но заказы есть у 57 поставщиков: три записи не выиграли ничего и в расчёт долей не попадают — их доля равна нулю, а не «неизвестна».

WITH sp AS (
SELECT supplier_id, sum(amount) AS spend FROM purchases GROUP BY supplier_id
),
ranked AS (
SELECT supplier_id, spend,
spend / sum(spend) OVER () AS share,
row_number() OVER (ORDER BY spend DESC) AS rk
FROM sp
)
SELECT count(*) AS "поставщиков с заказами",
round(100 * sum(CASE WHEN rk <= 5 THEN share END), 1) AS "топ-5, %",
CAST(round(10000 * sum(share * share)) AS INTEGER) AS "HHI"
FROM ranked;
┌────────────────────────┬──────────┬─────┐
│ поставщиков с заказами │ топ-5, % │ HHI │
├────────────────────────┼──────────┼─────┤
│ 57 │ 57.0 │ 804 │
└────────────────────────┴──────────┴─────┘

Пять поставщиков забирают 57,0% затрат, а индекс 804 — ниже 1667, то есть формально портфель ближе к «много примерно равных поставщиков», чем к зависимости от одного.

Этот вывод обманчив, и вот почему. HHI по портфелю смешивает девять разных рынков. Поставщик, который держит половину категории, но категория даёт 3% затрат, в общем индексе почти не виден. Считать концентрацию нужно там, где принимают решение о закупке, — внутри категории.

WITH sp AS (
SELECT p.category, p.supplier_id, sum(p.amount) AS spend
FROM purchases p GROUP BY 1, 2
),
tot AS (
SELECT category, sum(spend) AS total FROM sp GROUP BY category
)
SELECT sp.category AS "категория",
count(*) AS "поставщиков",
round(100 * max(sp.spend) / any_value(t.total), 1) AS "лидер, %",
CAST(round(10000 * sum((sp.spend / t.total) * (sp.spend / t.total))) AS INTEGER) AS "HHI"
FROM sp JOIN tot t ON t.category = sp.category
GROUP BY sp.category
ORDER BY 4 DESC;
┌─────────────────┬─────────────┬──────────┬──────┐
│ категория │ поставщиков │ лидер, % │ HHI │
├─────────────────┼─────────────┼──────────┼──────┤
│ ИТ-оборудование │ 6 │ 48.0 │ 3538 │
│ Логистика │ 6 │ 50.2 │ 3455 │
│ Металлопрокат │ 5 │ 37.4 │ 2849 │
│ Химия и ГСМ │ 6 │ 33.1 │ 2350 │
│ Электрика │ 7 │ 28.2 │ 2017 │
│ Канцелярия │ 6 │ 26.9 │ 1988 │
│ Спецодежда │ 7 │ 33.2 │ 1906 │
│ Упаковка │ 7 │ 27.2 │ 1705 │
│ Крепёж │ 7 │ 22.9 │ 1698 │
└─────────────────┴─────────────┴──────────┴──────┘

Внутри категорий картина другая: HHI от 1698 до 3538, и в каждой есть поставщик с долей 22,9–50,2%. Даже в самой ровной категории (Крепёж, 1698) индекс выше точки «шесть равных» — то есть доли уже неровные.

Обратите внимание на порядок. По деньгам первая категория — Металлопрокат (46,8% бюджета), но по концентрации он лишь третий: 2849. Самые зависимые — ИТ-оборудование (3538, лидер 48,0%) и Логистика (3455, лидер 50,2%). Это и есть приоритет для работы с базой поставщиков: не там, где больше денег, а там, где меньше альтернатив.

Ещё один слой — число поставщиков в категории: от пяти до семи. Само по себе оно ничего не говорит, но в паре с HHI работает. Пять поставщиков при лидере 37,4% — это мало альтернатив на большом объёме. Семь поставщиков при лидере 22,9% — рынок, где торг уместен.

Прежде чем делать выводы, справочник надо проверить на дубли: одна компания может быть заведена дважды — с организационно-правовой формой и без, в разном регистре. Находится нормализацией имени.

WITH norm AS (
SELECT id, name,
regexp_replace(
regexp_replace(lower(name), '^(ооо|ао|пао|зао|ип)\s*', '', 'g'),
'[^а-яёa-z]', '', 'g') AS key
FROM suppliers
)
SELECT key AS "нормализованное имя",
count(*) AS "записей",
string_agg(id || ' — ' || name, '; ' ORDER BY id) AS "что в справочнике"
FROM norm
GROUP BY key
HAVING count(*) > 1
ORDER BY key;
┌─────────────────────┬─────────┬──────────────────────────────────────────────────────┐
│ нормализованное имя │ записей │ что в справочнике │
├─────────────────────┼─────────┼──────────────────────────────────────────────────────┤
│ датацентрсервис │ 2 │ 36 — ООО «Дата-Центр-Сервис»; 59 — Дата-Центр-Сервис │
│ крепёжторг │ 2 │ 7 — АО «КрепёжТорг»; 56 — КрепёжТорг │
│ паклюкс │ 2 │ 37 — ООО «ПакЛюкс»; 60 — Паклюкс │
│ спецторг │ 2 │ 19 — ООО «Спецторг»; 58 — СпецТорг │
│ уралметаллснаб │ 2 │ 1 — ООО «УралМеталлСнаб»; 55 — УралМеталлСнаб │
│ электромир │ 2 │ 13 — ООО «ЭлектроМир»; 57 — Электромир │
└─────────────────────┴─────────┴──────────────────────────────────────────────────────┘

Шесть пар — это шесть компаний, задвоенных в справочнике. Что происходит с концентрацией, если склеить дубли в Металлопрокате:

WITH m AS (
SELECT regexp_replace(
regexp_replace(lower(s.name), '^(ооо|ао|пао|зао|ип)\s*', '', 'g'),
'[^а-яёa-z]', '', 'g') AS key,
sum(p.amount) AS spend
FROM purchases p
JOIN suppliers s ON s.id = p.supplier_id
WHERE p.category = 'Металлопрокат'
GROUP BY 1
),
s AS (SELECT key, spend, spend / sum(spend) OVER () AS share FROM m)
SELECT count(*) AS "поставщиков после склейки",
round(100 * max(share), 1) AS "лидер, %",
CAST(round(10000 * sum(share * share)) AS INTEGER) AS "HHI"
FROM s;
┌───────────────────────────┬──────────┬──────┐
│ поставщиков после склейки │ лидер, % │ HHI │
├───────────────────────────┼──────────┼──────┤
│ 4 │ 59.5 │ 4502 │
└───────────────────────────┴──────────┴──────┘

Было пять поставщиков с лидером 37,4% и HHI 2849, стало четыре с лидером 59,5% и HHI 4502. Две записи по 37,4% и 22,1% оказались одной компанией, и вдвоём они держат 59,5% категории. HHI при склейке вырос, потому что квадрат суммы больше суммы квадратов: (a + b)² > a² + b².

Отсюда практическое правило: концентрацию считают после нормализации справочника. Иначе отчёт покажет конкуренцию там, где её нет.

Ошибка Что происходит Как правильно
Считать HHI по портфелю и успокаиваться Девять рынков смешиваются в один: 804 при 3538 внутри категории Считать по категории, портфельный HHI — только обзор
Считать доли среди топ-10, а не среди всех Сумма долей не равна 100%, HHI завышен Знаменатель — весь объём категории
Оставить дубли в справочнике Концентрация занижена, лидер размыт на две записи Нормализовать имена до расчёта
Брать HHI по количеству заказов Индекс отражает число процедур, а не деньги Метрика HHI — доля затрат
Сравнивать HHI категорий с разным числом поставщиков 3000 при двух поставщиках и при десяти — разные ситуации Рядом с HHI показывать число поставщиков и долю лидера