86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""场景二 · 市场业务真实性:养卡骗补检测(R9)。
|
||
|
||
检测"脉冲式增长 + 规律性衰减"的周期性造假:渠道每月新增大量用户订购,
|
||
固定周期后这些用户集中退订(骗补后弃养)。结合佣金与业务质量匹配度。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
|
||
@dataclass
|
||
class CohortPoint:
|
||
"""某新增批次(cohort)在第 N 个月的留存率。"""
|
||
|
||
month_index: int
|
||
retention: float # 0-1
|
||
|
||
|
||
@dataclass
|
||
class ChurnFinding:
|
||
cliff_month: int | None
|
||
max_drop: float
|
||
pulse_then_decay: bool
|
||
|
||
|
||
def detect_pulse_decay(
|
||
retention_curve: list[CohortPoint],
|
||
cliff_drop: float = 0.5,
|
||
) -> ChurnFinding:
|
||
"""识别留存曲线中的"断崖式集中退订"。
|
||
|
||
若某月留存相对上月骤降超过 cliff_drop(默认 50%),判为规律性衰减。
|
||
"""
|
||
ordered = sorted(retention_curve, key=lambda p: p.month_index)
|
||
max_drop = 0.0
|
||
cliff_month: int | None = None
|
||
for prev, cur in zip(ordered, ordered[1:], strict=False):
|
||
drop = prev.retention - cur.retention
|
||
if drop > max_drop:
|
||
max_drop = drop
|
||
if drop >= cliff_drop:
|
||
cliff_month = cur.month_index
|
||
return ChurnFinding(
|
||
cliff_month=cliff_month,
|
||
max_drop=round(max_drop, 3),
|
||
pulse_then_decay=cliff_month is not None,
|
||
)
|
||
|
||
|
||
def commission_quality_mismatch(
|
||
commission_paid: float,
|
||
active_ratio: float,
|
||
zero_usage_ratio: float,
|
||
) -> float:
|
||
"""佣金与业务质量不匹配度(0-1)。
|
||
|
||
active_ratio:仍活跃用户占比;zero_usage_ratio:零通话/零流量用户占比。
|
||
佣金已发但活跃低、零使用高 → 不匹配度高。
|
||
"""
|
||
if commission_paid <= 0:
|
||
return 0.0
|
||
mismatch = 0.6 * zero_usage_ratio + 0.4 * (1 - active_ratio)
|
||
return round(min(max(mismatch, 0.0), 1.0), 3)
|
||
|
||
|
||
def churn_risk_score(finding: ChurnFinding, mismatch: float) -> float:
|
||
"""综合评分:断崖退订 + 佣金质量不匹配。"""
|
||
if not finding.pulse_then_decay:
|
||
return round(0.3 * mismatch, 3)
|
||
base = 0.4 + 0.4 * finding.max_drop + 0.2 * mismatch
|
||
return round(min(base, 1.0), 3)
|
||
|
||
|
||
def build_rationale(finding: ChurnFinding, mismatch: float) -> str:
|
||
if finding.pulse_then_decay:
|
||
return (
|
||
f"渠道新增用户在第 {finding.cliff_month} 个月出现断崖式集中退订"
|
||
f"(最大单月留存骤降 {finding.max_drop:.0%}),呈"
|
||
f"'脉冲式增长 + 规律性衰减'特征;佣金与业务质量不匹配度 {mismatch:.0%},"
|
||
f"高度疑似养卡骗补(骗补后弃养)。"
|
||
)
|
||
return (
|
||
f"未见明显断崖退订,但佣金与业务质量不匹配度为 {mismatch:.0%},建议关注。"
|
||
)
|