Условие
Дан реестр медицинских услуг по ДМС: row_id, batch_id, service_id, service_name, service_quantity, service_date, service_amount, icd_codes (МКБ-10), insured_id, insured_age, insured_gender, provider_id, service_code, service_code_name.
Найти систематические искажения в реестре. Не системное (один раз ошиблись, или услуга подходит, но цена странная) — не считать. Системные паттерны (каждому пациенту приписывается лишний анализ; всегда дороже, чем по тарифу; и т.д.) — считать.
Решение
Каркас
Мошенничество = систематическое отклонение от ожидаемого распределения. Алгоритм:
- Определить «нормальное поведение» (по другим клиникам или средним по системе).
- Для каждой клиники посчитать метрики и найти outliers.
- Объяснить, почему конкретный паттерн подозрителен.
Признаки FWA
A. Upcoding — услуга оформлена как более дорогая.
- Считаем для каждой
service_codeраспределениеservice_amountпо всем клиникам. - Если у
provider_id = Xсреднее значимо выше — flag.
import pandas as pd
sys_avg = df.groupby('service_code')['service_amount'].agg(['mean', 'std']).reset_index()
prov = df.groupby(['provider_id', 'service_code'])['service_amount'].mean().reset_index()
m = prov.merge(sys_avg, on='service_code', suffixes=('_prov', '_sys'))
m['z'] = (m['service_amount'] - m['mean']) / m['std']
m[m['z'] > 3] # подозрительныеB. Phantom billing — услуга приписана при отсутствии релевантного диагноза.
- Кросс-таблица «услуга × ICD-код»: какая частота услуги при ICD у других клиник?
- Если у
provider Xуслуга часто (>>) встречается без релевантного ICD — flag.
# Например: рентген пазух носа (4212.01) при гриппе (J10) у других — 5%, у X — 60%
suspicious = (df.groupby(['provider_id', 'service_code'])
.apply(lambda g: (g['icd_codes'].str.contains('J10|J11')).mean())
.reset_index(name='flu_share'))B'. Unbundling: одна процедура разбита на несколько «дешёвых», но с большим суммарным чеком.
- На уровне
batch_id: считаем «средний размер счёта» и распределение услуг внутри. - Если у клиники характерно X услуг в batch с известными комбинациями — flag.
C. Дубликаты услуг: один и тот же (insured, service_code, date) 2 раза в одной клинике.
- Нормально: 0.x%. Подозрительно: значимо выше.
dups = (df.groupby(['provider_id', 'insured_id', 'service_code', 'service_date'])
.size().reset_index(name='cnt'))
dups[dups['cnt'] > 1]['provider_id'].value_counts()D. Невозможные комбинации: пациент-мужчина с гинекологией; пациент-ребёнок 4 года с услугами для пожилых.
- На уровне
service_codeопределить «допустимый возраст / пол» (по справочнику или эмпирически). - Считать долю «невозможных» по клинике.
E. Завышенный квант услуг: service_quantity > 1 для услуг, которые «должны быть = 1».
- Прием первичный — 1 раз в день логически. Если у клиники X доля
quantity > 1для приёма выше системного — flag.
F. Pattern «одинаковый набор услуг для всех»:
- Считаем энтропию набора услуг внутри клиники. Низкая энтропия = всем подряд одно и то же = подозрительно.
from collections import Counter
def case_entropy(g):
c = Counter(g['service_code'])
n = sum(c.values())
p = [v/n for v in c.values()]
return -sum(pi*math.log(pi+1e-12) for pi in p)G. Среднее число услуг на batch: клиника-аномалия имеет резко больше услуг на batch.
Объединение в risk-score
Для каждой provider_id:
Топ-5 по risk → аналитик расследует руками. Минимум одна реальная схема в ответе обязательна (требование задания).
Пример конкретной находки
«У provider X доля приемов терапевта повторных (1101.02) на каждого insured ≥ 5 в среднем — против системного 1.7. И при этом средний service_amount повторного приема на 30% выше среднесистемного. Гипотеза: провайдер выставляет дополнительные приёмы и upcode-ит их.»
Подводные камни
- Размер клиники: маленькая клиника может выглядеть аномально по любому статистическому критерию из-за малой выборки. Нужен фильтр
n_services > 100. - Специализация клиник: онкоцентр будет иметь больше дорогих услуг; неотложка — больше срочных. Сравнивайте «подобное с подобным» — после кластеризации.
- Sex/age по
insured: одна и та же insured_id может иметь разный возраст в разные даты — фильтруйте на текущий visit. - Multiple testing: при сотнях клиник × десятках услуг → много false-positive. Используйте FDR (BH).
- Корректность ICD-сопоставления: «релевантность» услуги к диагнозу не всегда тривиальна — нужны медицинские справочники.
Эталонный ответ
Каркас: 7 паттернов FWA (upcoding, phantom, unbundling, duplicates, impossible-combo, quantum-inflate, low-entropy services). Для каждой клиники строим risk-score, ранжируем, далее ручное расследование. Минимум одна находка с конкретным примером и объяснением паттерна — обязательное требование задания.