hccinfhir 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,8 +8,10 @@ def has_any_hcc(hcc_list: list[str], hcc_set: set[str]) -> int:
8
8
  def create_demographic_interactions(demographics: Demographics) -> dict:
9
9
  """Creates common demographic-based interactions"""
10
10
  interactions = {}
11
- is_female = demographics.category.startswith('F')
12
- is_male = demographics.category.startswith('M')
11
+ # Determine sex from demographics.sex instead of category
12
+ # Category can start with 'NEM'/'NEF' for new enrollees, not just 'M'/'F'
13
+ is_female = demographics.sex in ('F', '2')
14
+ is_male = demographics.sex in ('M', '1')
13
15
  is_aged = not demographics.non_aged
14
16
 
15
17
  # Original Disability interactions
@@ -33,21 +35,32 @@ def create_demographic_interactions(demographics: Demographics) -> dict:
33
35
  nemcaid = True
34
36
  ne_origds = int(demographics.age >= 65 and (demographics.orec is not None and demographics.orec == "1"))
35
37
 
38
+ fbd = demographics.fbd
39
+
36
40
  # Four mutually exclusive groups
37
41
  interactions.update({
38
42
  f'NMCAID_NORIGDIS_{demographics.category}': int(not nemcaid and not ne_origds),
39
43
  f'MCAID_NORIGDIS_{demographics.category}': int(nemcaid and not ne_origds),
40
44
  f'NMCAID_ORIGDIS_{demographics.category}': int(not nemcaid and ne_origds),
41
- f'MCAID_ORIGDIS_{demographics.category}': int(nemcaid and ne_origds)
45
+ f'MCAID_ORIGDIS_{demographics.category}': int(nemcaid and ne_origds),
46
+ f'FBD_NORIGDIS_{demographics.category}': int(fbd and not ne_origds),
47
+ f'FBD_ORIGDIS_{demographics.category}': int(fbd and ne_origds),
48
+ f'ND_PBD_NORIGDIS_{demographics.category}': int(not fbd and not ne_origds),
49
+ f'ND_PBD_ORIGDIS_{demographics.category}': int(not fbd and ne_origds)
42
50
  })
43
51
 
52
+ # output only non-zero interactions
53
+ interactions = {k: v for k, v in interactions.items() if v > 0}
54
+
44
55
  return interactions
45
56
 
46
57
  def create_dual_interactions(demographics: Demographics) -> dict:
47
58
  """Creates dual status interactions"""
48
59
  interactions = {}
49
- is_female = demographics.category.startswith('F')
50
- is_male = demographics.category.startswith('M')
60
+ # Determine sex from demographics.sex instead of category
61
+ # Category can start with 'NEM'/'NEF' for new enrollees, not just 'M'/'F'
62
+ is_female = demographics.sex in ('F', '2')
63
+ is_male = demographics.sex in ('M', '1')
51
64
  is_aged = not demographics.non_aged
52
65
 
53
66
  if demographics.fbd:
@@ -77,12 +90,17 @@ def create_hcc_counts(hcc_set: set[str]) -> dict:
77
90
  counts[f'D{i}'] = int(hcc_count == i)
78
91
  counts['D10P'] = int(hcc_count >= 10)
79
92
 
93
+ # output only non-zero counts
94
+ counts = {k: v for k, v in counts.items() if v > 0}
95
+
80
96
  return counts
81
97
 
82
98
  def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
83
99
  """Creates disease categories based on model version"""
100
+ categories = {}
101
+
84
102
  if model_name == "CMS-HCC Model V28":
85
- return {
103
+ categories = {
86
104
  'CANCER_V28': has_any_hcc(['17', '18', '19', '20', '21', '22', '23'], hcc_set),
87
105
  'DIABETES_V28': has_any_hcc(['35', '36', '37', '38'], hcc_set),
88
106
  'CARD_RESP_FAIL_V28': has_any_hcc(['211', '212', '213'], hcc_set),
@@ -96,7 +114,7 @@ def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
96
114
  'ULCER_V28': has_any_hcc(['379', '380', '381', '382'], hcc_set)
97
115
  }
98
116
  elif model_name == "CMS-HCC Model V24":
99
- return {
117
+ categories = {
100
118
  'CANCER': has_any_hcc(['8', '9', '10', '11', '12'], hcc_set),
101
119
  'DIABETES': has_any_hcc(['17', '18', '19'], hcc_set),
102
120
  'CARD_RESP_FAIL': has_any_hcc(['82', '83', '84'], hcc_set),
@@ -109,7 +127,7 @@ def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
109
127
  'PRESSURE_ULCER': has_any_hcc(['157', '158', '159'], hcc_set) # added in 2018-11-20
110
128
  }
111
129
  elif model_name == "CMS-HCC Model V22":
112
- return {
130
+ categories = {
113
131
  'CANCER': has_any_hcc(['8', '9', '10', '11', '12'], hcc_set),
114
132
  'DIABETES': has_any_hcc(['17', '18', '19'], hcc_set),
115
133
  'CARD_RESP_FAIL': has_any_hcc(['82', '83', '84'], hcc_set),
@@ -122,7 +140,7 @@ def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
122
140
  'PRESSURE_ULCER': has_any_hcc(['157', '158'], hcc_set) # added in 2012-10-19
123
141
  }
124
142
  elif model_name == "CMS-HCC ESRD Model V24":
125
- return {
143
+ categories = {
126
144
  'CANCER': has_any_hcc(['8', '9', '10', '11', '12'], hcc_set),
127
145
  'DIABETES': has_any_hcc(['17', '18', '19'], hcc_set),
128
146
  'CARD_RESP_FAIL': has_any_hcc(['82', '83', '84'], hcc_set),
@@ -135,7 +153,7 @@ def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
135
153
  'gPsychiatric_V24': has_any_hcc(['57', '58', '59', '60'], hcc_set)
136
154
  }
137
155
  elif model_name == "CMS-HCC ESRD Model V21":
138
- return {
156
+ categories = {
139
157
  'CANCER': has_any_hcc(['8', '9', '10', '11', '12'], hcc_set),
140
158
  'DIABETES': has_any_hcc(['17', '18', '19'], hcc_set),
141
159
  'IMMUNE': int('47' in hcc_set),
@@ -150,7 +168,9 @@ def get_diagnostic_categories(model_name: ModelName, hcc_set: set[str]) -> dict:
150
168
  elif model_name == "RxHCC Model V08":
151
169
  # RxModel doesn't seem to have any diagnostic category interactions
152
170
  return {}
153
- return {}
171
+
172
+ # keep the zero-valued categories. Will be filtered out later
173
+ return categories
154
174
 
155
175
  def create_disease_interactions(model_name: ModelName,
156
176
  diagnostic_cats: dict,
@@ -317,6 +337,9 @@ def create_disease_interactions(model_name: ModelName,
317
337
  'NonAged_RXHCC159': demographics.non_aged * int('159' in hcc_set),
318
338
  'NonAged_RXHCC163': demographics.non_aged * int('163' in hcc_set)
319
339
  })
340
+
341
+ # output only non-zero interactions
342
+ interactions = {k: v for k, v in interactions.items() if v > 0}
320
343
 
321
344
  return interactions
322
345
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: hccinfhir
3
- Version: 0.1.5
3
+ Version: 0.1.7
4
4
  Summary: HCC Algorithm for FHIR Resources
5
5
  Project-URL: Homepage, https://github.com/mimilabs/hccinfhir
6
6
  Project-URL: Issues, https://github.com/mimilabs/hccinfhir/issues
@@ -535,6 +535,89 @@ export_data = result.model_dump(
535
535
  )
536
536
  ```
537
537
 
538
+ ### Overriding Demographic Categorization
539
+
540
+ **Problem**: Sometimes demographic data has quality issues (e.g., ESRD patients with incorrect `orec`/`crec` codes), leading to wrong risk score calculations.
541
+
542
+ **Solution**: Use the `prefix_override` parameter to manually specify the coefficient prefix.
543
+
544
+ #### Common Use Case: ESRD Patients with Incorrect OREC/CREC
545
+
546
+ ```python
547
+ from hccinfhir import HCCInFHIR, Demographics
548
+
549
+ # ESRD dialysis patient, but source data has wrong orec/crec codes
550
+ processor = HCCInFHIR(model_name="CMS-HCC ESRD Model V24")
551
+
552
+ demographics = Demographics(
553
+ age=65,
554
+ sex="F",
555
+ orec="0", # Should be '2' or '3' for ESRD, but data is incorrect
556
+ crec="0" # Should be '2' or '3' for ESRD, but data is incorrect
557
+ )
558
+
559
+ diagnosis_codes = ["N18.6", "E11.22", "I12.0"] # ESRD + diabetes + hypertensive CKD
560
+
561
+ # Force ESRD dialysis coefficients despite incorrect orec/crec
562
+ result = processor.calculate_from_diagnosis(
563
+ diagnosis_codes,
564
+ demographics,
565
+ prefix_override='DI_' # DI_ = ESRD Dialysis
566
+ )
567
+
568
+ print(f"Risk Score with override: {result.risk_score}")
569
+ ```
570
+
571
+ #### Other Common Scenarios
572
+
573
+ ```python
574
+ # Long-term institutionalized patient not properly flagged
575
+ processor = HCCInFHIR(model_name="CMS-HCC Model V28")
576
+ demographics = Demographics(age=78, sex="M")
577
+ diagnosis_codes = ["F03.90", "I48.91", "N18.4"]
578
+
579
+ result = processor.calculate_from_diagnosis(
580
+ diagnosis_codes,
581
+ demographics,
582
+ prefix_override='INS_' # INS_ = Institutionalized
583
+ )
584
+
585
+ # New enrollee with missing flag
586
+ result = processor.calculate_from_diagnosis(
587
+ diagnosis_codes,
588
+ demographics,
589
+ prefix_override='NE_' # NE_ = New Enrollee
590
+ )
591
+ ```
592
+
593
+ #### Common Prefix Values
594
+
595
+ **CMS-HCC Models (V22, V24, V28):**
596
+ - `CNA_` - Community, Non-Dual, Aged (65+)
597
+ - `CND_` - Community, Non-Dual, Disabled (<65)
598
+ - `CFA_` - Community, Full Benefit Dual, Aged
599
+ - `CFD_` - Community, Full Benefit Dual, Disabled
600
+ - `CPA_` - Community, Partial Benefit Dual, Aged
601
+ - `CPD_` - Community, Partial Benefit Dual, Disabled
602
+ - `INS_` - Long-Term Institutionalized
603
+ - `NE_` - New Enrollee
604
+ - `SNPNE_` - Special Needs Plan New Enrollee
605
+
606
+ **ESRD Models (V21, V24):**
607
+ - `DI_` - Dialysis (standard)
608
+ - `DNE_` - Dialysis New Enrollee
609
+ - `GI_` - Graft, Institutionalized
610
+ - `GNE_` - Graft, New Enrollee
611
+ - `GFPA_`, `GFPN_`, `GNPA_`, `GNPN_` - Graft with various dual/age combinations
612
+
613
+ **RxHCC Model (V08):**
614
+ - `Rx_CE_LowAged_` - Community, Low Income, Aged
615
+ - `Rx_CE_NoLowAged_` - Community, Not Low Income, Aged
616
+ - `Rx_NE_Lo_` - New Enrollee, Low Income
617
+ - `Rx_CE_LTI_` - Community, Long-Term Institutionalized
618
+
619
+ See [CLAUDE.md](./CLAUDE.md#coefficient-prefix-reference) for complete prefix reference.
620
+
538
621
  ### Custom Filtering Rules
539
622
 
540
623
  ```python
@@ -544,7 +627,7 @@ from hccinfhir.filter import apply_filter
544
627
  filtered_data = apply_filter(
545
628
  service_data,
546
629
  include_inpatient=True,
547
- include_outpatient=True,
630
+ include_outpatient=True,
548
631
  eligible_cpt_hcpcs_file="custom_procedures.csv"
549
632
  )
550
633
  ```
@@ -665,6 +748,12 @@ FROM mimi_ws_1.cmspayment.ra_eligible_cpt_hcpcs
665
748
  WHERE is_included = 'yes' AND YEAR(mimi_src_file_date) = 2025;
666
749
  ```
667
750
 
751
+ `hcc_is_chronic.csv`
752
+ ```sql
753
+ SELECT hcc, is_chronic, model_version, model_domain
754
+ FROM cmspayment.ra_report_to_congress
755
+ WHERE mimi_src_file_name = '2024riskadjustmentinma-rtc.pdf'
756
+ ```
668
757
 
669
758
  ## 🧪 Testing
670
759
 
@@ -1,20 +1,21 @@
1
1
  hccinfhir/__init__.py,sha256=G_5m6jm3_BK5NdcZWoi0NEKJEsE_LjAU1RaLaL9xNPU,1043
2
- hccinfhir/datamodels.py,sha256=Vp64Blwq8qWe-q1slCK8jMO9SIErUgkXusGZ0laXiWo,5936
2
+ hccinfhir/datamodels.py,sha256=EHkuWMhmHBt8GfVP3lrxfSogu-qZQzeforFzp0Bn_bM,7714
3
3
  hccinfhir/extractor.py,sha256=xL9c2VT-e2I7_c8N8j4Og42UEgVuCzyn9WFp3ntM5Ro,1822
4
- hccinfhir/extractor_837.py,sha256=Jhxb5glRA8Yi8lbRwdltggXCvr_84nlsZEMRIgxyy4A,13169
5
- hccinfhir/extractor_fhir.py,sha256=Rg_L0Vg5tz_L2VJ_jvZwWz6RMlPAkHwj4LiK-OWQvrQ,8458
4
+ hccinfhir/extractor_837.py,sha256=Irp0ROWgZv6jru9w5sdkoTSYHDTh0v_I_xYRhWdHOjw,13037
5
+ hccinfhir/extractor_fhir.py,sha256=wUN3vTm1oTZ-KvfcDebnpQMxAC-7YlRKv12Wrv3p85A,8490
6
6
  hccinfhir/filter.py,sha256=j_yD2g6RBXVUV9trKkWzsQ35x3fRvfKUPvEXKUefI64,2007
7
- hccinfhir/hccinfhir.py,sha256=6jvLoJF1TcqTGXbDA-_l3os6-xO68SKRr2l26LjzlgM,6982
8
- hccinfhir/model_calculate.py,sha256=fPhSqfxN9-9EPyp8z-O8XZWMXmLU6Hun7OhXAgsgDFw,5688
9
- hccinfhir/model_coefficients.py,sha256=ZsVY0S_X_BzDvcCmzCEf31v8uixbGmPAsR6nVEyCbIA,5530
10
- hccinfhir/model_demographics.py,sha256=7W5NLW7aAjpn25BJzqfP8iSouD9uA6epGguJJ6BPuC0,7043
7
+ hccinfhir/hccinfhir.py,sha256=tgNWGYvsQWOlmcnP-3yH3KfXgZtQ3IdxYfGP9SNSJb0,9879
8
+ hccinfhir/model_calculate.py,sha256=vM0b4BkLafebk7A3yFxsEONRW4GkVbUl4GxNlLz5N8Q,6742
9
+ hccinfhir/model_coefficients.py,sha256=2Y_xCjX4__B1_xkX3pp-XTOW4KEAWo6RCVOOJ7ZZajM,5931
10
+ hccinfhir/model_demographics.py,sha256=CR4WC8XVq-CI1nYJoVFc5-KXTw-pKoVlHkHqfnXlnj0,9121
11
11
  hccinfhir/model_dx_to_cc.py,sha256=guJny2Mb9z8YRNWCXGSIE3APbE06zwnA2NRkjAeUs60,1765
12
12
  hccinfhir/model_hierarchies.py,sha256=0kdBmF_8e_ikMHBDhlw2I7jT3DupHfUn6o5mWj7v3Yo,2910
13
- hccinfhir/model_interactions.py,sha256=Ev1m6d7cHpmlJ3xk6l0EHYCTIyPynbOMR9HnLPrtj_A,20344
13
+ hccinfhir/model_interactions.py,sha256=xdsTuc3ii8U_MaPpYv0SnR4eR_w72eHKUJn1mwmZHm4,21379
14
14
  hccinfhir/samples.py,sha256=cqytZ-N08cPGqnUXJfzrEhhHW2qL7SDPYLD9-CHXZIU,8446
15
15
  hccinfhir/utils.py,sha256=AAHwzMSW8O9VZp1KLcdlN3OeBbxQtqQRtbTTdrKf7M0,2784
16
16
  hccinfhir/data/__init__.py,sha256=SGiSkpGrnxbvtEFMMlk82NFHOE50hFXcgKwKUSuVZUg,45
17
- hccinfhir/data/hcc_is_chronic.csv,sha256=eVVI4_8mQNkiBiNO3kattfT_zfcV18XgmiltdzZEXSo,17720
17
+ hccinfhir/data/hcc_is_chronic.csv,sha256=Bwd-RND6SdEsKP-assoBaXnjUJAuDXhSkwWlymux72Y,19701
18
+ hccinfhir/data/hcc_is_chronic_without_esrd_model.csv,sha256=eVVI4_8mQNkiBiNO3kattfT_zfcV18XgmiltdzZEXSo,17720
18
19
  hccinfhir/data/ra_coefficients_2025.csv,sha256=I0S2hoJlfig-D0oSFxy0b3Piv7m9AzOGo2CwR6bcQ9w,215191
19
20
  hccinfhir/data/ra_coefficients_2026.csv,sha256=0gfjGgVdIEWkBO01NaAbTLMzHCYINA0rf_zl8ojngCY,288060
20
21
  hccinfhir/data/ra_dx_to_cc_2025.csv,sha256=4N7vF6VZndkl7d3Fo0cGsbAPAZdCjAizSH8BOKsZNAo,1618924
@@ -43,7 +44,7 @@ hccinfhir/sample_files/sample_eob_1.json,sha256=_NGSVR2ysFpx-DcTvyga6dFCzhQ8Vi9f
43
44
  hccinfhir/sample_files/sample_eob_2.json,sha256=FcnJcx0ApOczxjJ_uxVLzCep9THfNf4xs9Yf7hxk8e4,1769
44
45
  hccinfhir/sample_files/sample_eob_200.ndjson,sha256=CxpjeQ1DCMUzZILaM68UEhfxO0p45YGhDDoCZeq8PxU,1917986
45
46
  hccinfhir/sample_files/sample_eob_3.json,sha256=4BW4wOMBEEU9RDfJR15rBEvk0KNHyuMEh3e055y87Hc,2306
46
- hccinfhir-0.1.5.dist-info/METADATA,sha256=2FjYMUdQ67RQHeayhpmAhCuVyEGYSnUxcVbyC1mTn8w,21979
47
- hccinfhir-0.1.5.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
48
- hccinfhir-0.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
49
- hccinfhir-0.1.5.dist-info/RECORD,,
47
+ hccinfhir-0.1.7.dist-info/METADATA,sha256=o2Uur5C9layHtKuWQeEQ1THAwMpCLmEkmnVTvrOUWDE,24819
48
+ hccinfhir-0.1.7.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
49
+ hccinfhir-0.1.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
50
+ hccinfhir-0.1.7.dist-info/RECORD,,