| |
| """ |
| kappa_lambda_meta.py (v3 - rule-based) |
| ======================================== |
| Derives kappa/lambda clonality via deterministic clinical rules. |
| No classifier needed: the logic is unambiguous. |
| |
| Rules: |
| Kappa+ and Lambda- -> kappa_dominant |
| Lambda+ and Kappa- -> lambda_dominant |
| Both positive -> polyclonal |
| Both negative -> polyclonal (non-specific, no light chain restriction) |
| |
| Clinical note: kappa/lambda ratio indicates CLONALITY, not IS need directly. |
| Monoclonal patterns narrow the differential toward plasma cell dyscrasias |
| (LCDD, amyloid, myeloma cast nephropathy) which are mostly IS=No or |
| require disease-specific regimens. The ratio feeds into the IS classifier |
| as one feature among many, not as a standalone IS decision. |
| |
| Used by: infer_case_v2.py (inline rule, no pkl needed) |
| train_is_classifier.py (encodes ratio as ordinal feature) |
| """ |
|
|
| |
| |
|
|
| def derive_kappa_lambda_ratio(kappa_grade, lambda_grade): |
| """ |
| Args: |
| kappa_grade: float, predicted or true ordinal grade (0-4) |
| lambda_grade: float, predicted or true ordinal grade (0-4) |
| Returns: |
| (label, confidence) |
| label: 'kappa_dominant' | 'lambda_dominant' | 'polyclonal' |
| confidence: float (1.0 for pure rule, lower if grades are borderline) |
| """ |
| k_pos = kappa_grade > 0 |
| l_pos = lambda_grade > 0 |
|
|
| if k_pos and not l_pos: |
| label = "kappa_dominant" |
| elif l_pos and not k_pos: |
| label = "lambda_dominant" |
| else: |
| label = "polyclonal" |
|
|
| |
| |
| |
| contrast = abs(kappa_grade - lambda_grade) |
| if contrast >= 2: |
| conf = 0.90 |
| elif contrast == 1: |
| conf = 0.70 |
| else: |
| conf = 0.55 |
|
|
| return label, conf |
|
|
|
|
| if __name__ == "__main__": |
| |
| tests = [ |
| (3, 0, "kappa_dominant"), |
| (0, 2, "lambda_dominant"), |
| (2, 2, "polyclonal"), |
| (0, 0, "polyclonal"), |
| (1, 0, "kappa_dominant"), |
| ] |
| print("Rule check:") |
| all_pass = True |
| for k, l, expected in tests: |
| label, conf = derive_kappa_lambda_ratio(k, l) |
| status = "OK" if label == expected else "FAIL" |
| if status == "FAIL": all_pass = False |
| print(f" k={k} l={l} -> {label} (conf={conf:.2f}) [{status}]") |
| print(f"\nAll tests passed: {all_pass}") |
|
|