"""가격 이상치 제거. 레퍼런스는 반복 z-score(정규분포 가정)를 썼으나, 가격 분포는 한쪽으로 치우친(로그정규) 경우가 많아 취약하다. 대신 분포 가정이 없는 **IQR(사분위 범위) 방식**을 쓴다(stdlib 만, numpy 불필요). 목적: '엉뚱한 초저가(액세서리/오매칭)·초고가(묶음)'가 최저가 산정을 오염시키는 것을 막는다. """ import statistics from services.search.contract import NormalizedProduct def remove_price_outliers( products: list[NormalizedProduct], k: float = 1.5, min_items: int = 4 ) -> tuple[list[NormalizedProduct], list[NormalizedProduct]]: """IQR 기반 이상치 제거. [Q1 - k·IQR, Q3 + k·IQR] 밖을 제거. 데이터가 min_items 미만이면 통계가 무의미하므로 그대로 통과. (kept, removed) 반환.""" prices = [p.price for p in products if p.price and p.price > 0] if len(prices) < min_items: return list(products), [] q1, _, q3 = statistics.quantiles(prices, n=4) iqr = q3 - q1 lo, hi = q1 - k * iqr, q3 + k * iqr kept, removed = [], [] for p in products: if p.price is None or lo <= p.price <= hi: kept.append(p) else: removed.append(p) return kept, removed