|
5 | 5 |
|
6 | 6 |
|
7 | 7 | def get_mappings() -> None: |
8 | | - """Get mappings for a concept to other vocabularies.""" |
| 8 | + """Get the mappings defined for a concept.""" |
9 | 9 | print("=== Concept Mappings ===") |
10 | 10 |
|
11 | 11 | client = omophub.OMOPHub() |
12 | 12 |
|
13 | 13 | try: |
14 | | - # Type 2 diabetes mellitus (SNOMED) |
| 14 | + # Type 2 diabetes mellitus (SNOMED, standard) |
15 | 15 | concept_id = 201826 |
16 | 16 |
|
| 17 | + # `get()` returns ONE page, and it returns the response's `data` field |
| 18 | + # only -- the `meta.pagination` that would say whether more pages exist |
| 19 | + # is not part of what you get back. See get_every_mapping() below. |
| 20 | + result = client.mappings.get(concept_id) |
| 21 | + mappings = result.get("mappings", []) |
| 22 | + |
| 23 | + print(f"Mappings for concept {concept_id} (this page: {len(mappings)}):") |
| 24 | + for m in mappings[:10]: |
| 25 | + # A mapping row carries only these fields. Vocabulary id and |
| 26 | + # concept code are NOT part of it -- fetch the target concept if |
| 27 | + # you need them. |
| 28 | + print( |
| 29 | + f" {m.get('relationship_id')}: " |
| 30 | + f"{m.get('target_concept_id')} {m.get('target_concept_name')}" |
| 31 | + ) |
| 32 | + except omophub.OMOPHubError as e: |
| 33 | + print(f"API error: {e.message}") |
| 34 | + finally: |
| 35 | + client.close() |
| 36 | + |
| 37 | + |
| 38 | +def map_to_a_specific_vocabulary() -> None: |
| 39 | + """Find which ICD-10-CM codes correspond to a SNOMED concept. |
| 40 | +
|
| 41 | + Note the DIRECTION. `Maps to` always points at a *standard* concept, and |
| 42 | + ICD-10-CM is non-standard, so `target_vocabulary="ICD10CM"` on the default |
| 43 | + relationship matches nothing -- it returns an empty list rather than an |
| 44 | + error. The codes that roll up INTO a standard concept are reached with |
| 45 | + `Mapped from`. |
| 46 | + """ |
| 47 | + print("\n=== Mapping to a Specific Vocabulary ===") |
| 48 | + |
| 49 | + client = omophub.OMOPHub() |
| 50 | + |
| 51 | + try: |
| 52 | + concept_id = 201826 |
| 53 | + |
| 54 | + empty = client.mappings.get(concept_id, target_vocabulary="ICD10CM") |
| 55 | + print( |
| 56 | + f" 'Maps to' + ICD10CM: {len(empty.get('mappings', []))} rows (as expected)" |
| 57 | + ) |
| 58 | + |
| 59 | + icd_codes = list( |
| 60 | + client.mappings.get_iter( |
| 61 | + concept_id, |
| 62 | + relationship_ids=["Mapped from"], |
| 63 | + target_vocabulary="ICD10CM", |
| 64 | + ) |
| 65 | + ) |
| 66 | + print(f" 'Mapped from' + ICD10CM: {len(icd_codes)} rows") |
| 67 | + for m in icd_codes[:5]: |
| 68 | + print(f" <- {m.get('target_concept_id')} {m.get('target_concept_name')}") |
| 69 | + except omophub.OMOPHubError as e: |
| 70 | + print(f"API error: {e.message}") |
| 71 | + finally: |
| 72 | + client.close() |
| 73 | + |
| 74 | + |
| 75 | +def get_every_mapping() -> None: |
| 76 | + """Walk every page instead of trusting the first one. |
| 77 | +
|
| 78 | + This is the one to copy when building a code list: a partial code list is |
| 79 | + wrong in a way nothing in the result reveals. |
| 80 | + """ |
| 81 | + print("\n=== Every Mapping (all pages) ===") |
| 82 | + |
| 83 | + client = omophub.OMOPHub() |
| 84 | + |
| 85 | + try: |
| 86 | + concept_id = 201826 |
| 87 | + |
| 88 | + # get_iter() follows has_next to the end; it never has to guess from |
| 89 | + # the page length. |
| 90 | + all_mappings = list(client.mappings.get_iter(concept_id)) |
| 91 | + print(f" {len(all_mappings)} mappings in total") |
| 92 | + |
| 93 | + # Streaming, if you would rather not hold them all at once. |
| 94 | + for m in client.mappings.get_iter(concept_id): |
| 95 | + _ = m["target_concept_name"] |
| 96 | + except omophub.OMOPHubError as e: |
| 97 | + print(f"API error: {e.message}") |
| 98 | + finally: |
| 99 | + client.close() |
| 100 | + |
| 101 | + |
| 102 | +def value_as_concept() -> None: |
| 103 | + """Composite concepts decompose across TWO relationships. |
| 104 | +
|
| 105 | + The default returns only the first, so you learn the patient is allergic |
| 106 | + to *a drug* but not *which* drug. |
| 107 | + """ |
| 108 | + print("\n=== Value-as-Concept ===") |
| 109 | + |
| 110 | + client = omophub.OMOPHub() |
| 111 | + |
| 112 | + try: |
| 113 | + # Allergy to penicillin G |
17 | 114 | result = client.mappings.get( |
18 | | - concept_id, |
19 | | - target_vocabulary="ICD10CM", |
| 115 | + 4167462, |
| 116 | + relationship_ids=["Maps to", "Maps to value"], |
20 | 117 | ) |
21 | 118 |
|
22 | | - source = result.get("source_concept", {}) |
23 | | - mappings = result.get("mappings", []) |
24 | | - summary = result.get("mapping_summary", {}) |
| 119 | + for m in result.get("mappings", []): |
| 120 | + # `Maps to` -> the OMOP concept column; |
| 121 | + # `Maps to value` -> value_as_concept_id. |
| 122 | + column = ( |
| 123 | + "value_as_concept_id" |
| 124 | + if m.get("relationship_id") == "Maps to value" |
| 125 | + else "concept_id" |
| 126 | + ) |
| 127 | + print( |
| 128 | + f" {m.get('relationship_id')}: {m.get('target_concept_name')} -> {column}" |
| 129 | + ) |
| 130 | + except omophub.OMOPHubError as e: |
| 131 | + print(f"API error: {e.message}") |
| 132 | + finally: |
| 133 | + client.close() |
25 | 134 |
|
26 | | - source_name = source.get("concept_name", "Unknown") if source else "Unknown" |
27 | | - print(f"Mappings for '{source_name}':") |
28 | | - print(f" Total mappings: {summary.get('total_mappings', len(mappings))}") |
29 | 135 |
|
30 | | - for m in mappings[:10]: |
31 | | - target_vocab = m.get("target_vocabulary_id", "?") |
32 | | - target_code = m.get("target_concept_code", "?") |
33 | | - target_name = m.get("target_concept_name", "?") |
34 | | - print(f"\n [{target_vocab}] {target_code}") |
35 | | - print(f" Name: {target_name}") |
| 136 | +def exclude_invalid() -> None: |
| 137 | + """Deprecated mappings come back by default; pass False to drop them.""" |
| 138 | + print("\n=== Valid Mappings Only ===") |
| 139 | + |
| 140 | + client = omophub.OMOPHub() |
| 141 | + |
| 142 | + try: |
| 143 | + concept_id = 201826 |
| 144 | + with_invalid = list(client.mappings.get_iter(concept_id)) |
| 145 | + valid_only = list(client.mappings.get_iter(concept_id, include_invalid=False)) |
| 146 | + |
| 147 | + print(f" default (includes deprecated): {len(with_invalid)}") |
| 148 | + print(f" include_invalid=False: {len(valid_only)}") |
36 | 149 | except omophub.OMOPHubError as e: |
37 | 150 | print(f"API error: {e.message}") |
38 | 151 | finally: |
@@ -99,5 +212,9 @@ def lookup_by_code() -> None: |
99 | 212 |
|
100 | 213 | if __name__ == "__main__": |
101 | 214 | get_mappings() |
| 215 | + map_to_a_specific_vocabulary() |
| 216 | + get_every_mapping() |
| 217 | + value_as_concept() |
| 218 | + exclude_invalid() |
102 | 219 | map_concepts() |
103 | 220 | lookup_by_code() |
0 commit comments