ancestryaudit 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dana Yergaliyeva
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,278 @@
1
+ Metadata-Version: 2.4
2
+ Name: ancestryaudit
3
+ Version: 0.1.0
4
+ Summary: Bias detection and correction framework for genomic cancer AI. Detects ancestry-linked performance gaps in CNV-based cancer classifiers and applies supervised fine-tuning correction.
5
+ Home-page: https://github.com/DanYerga/ancestryaudit
6
+ Author: Dana Yergaliyeva
7
+ Author-email: dyergaliyeva.08@gmail.com
8
+ Keywords: bioinformatics,cancer genomics,copy number variation,ancestry bias,fairness,machine learning,TCGA
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: numpy>=1.21
19
+ Requires-Dist: pandas>=1.3
20
+ Requires-Dist: scikit-learn>=1.0
21
+ Requires-Dist: scipy>=1.7
22
+ Requires-Dist: shap>=0.41
23
+ Requires-Dist: matplotlib>=3.4
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: classifier
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: keywords
31
+ Dynamic: license-file
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
35
+
36
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21290874.svg)](https://doi.org/10.5281/zenodo.21290874)
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
38
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
39
+
40
+ # AncestryAudit
41
+
42
+ **Bias detection and correction framework for genomic cancer AI.**
43
+
44
+ Detects ancestry-linked performance gaps in copy number variation (CNV)-based
45
+ cancer classifiers, applies supervised fine-tuning correction, and generates
46
+ structured audit reports.
47
+
48
+ Developed from research on ancestry bias in TCGA-LIHC/STAD classification
49
+ (Yergaliyeva, 2026).
50
+
51
+ ---
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ pip install ancestryaudit
57
+ # or from source:
58
+ pip install -e .
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Input Format
64
+
65
+ AncestryAudit works on any CNV feature matrix:
66
+
67
+ | Format | Shape | Notes |
68
+ |--------|-------|-------|
69
+ | `np.ndarray` | `(n_samples, n_genes)` | Continuous copy-number values |
70
+ | `pd.DataFrame` | `(n_samples, n_genes)` | Column names = gene identifiers |
71
+
72
+ **Column values:** continuous copy number (e.g. TCGA ABSOLUTE pipeline output,
73
+ where 2.0 = normal diploid, >2 = amplification, <2 = deletion).
74
+
75
+ **Labels:** binary integer (0 or 1), one per sample.
76
+
77
+ **Models:** any scikit-learn compatible estimator with `fit` / `predict` interface.
78
+
79
+ ---
80
+
81
+ ## Quick Start
82
+
83
+ ```python
84
+ from ancestryaudit import AncestryAuditFramework
85
+ from sklearn.linear_model import LogisticRegression
86
+
87
+ framework = AncestryAuditFramework()
88
+
89
+ # Step 1: Detect ancestry-linked performance gap
90
+ report = framework.audit(
91
+ LogisticRegression(max_iter=1000),
92
+ X_western, y_western, # source (training) population
93
+ X_asian, y_asian # target (evaluation) population
94
+ )
95
+ print(f"Gap: {report.gap_pp:.2f}pp, p={report.p_value:.4f}")
96
+ print(f"Recommendation: {report.recommendation}")
97
+ ```
98
+
99
+ Output:
100
+ ```
101
+ Gap: +2.39pp, p=0.0069
102
+ Recommendation: correction_required
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Full Pipeline
108
+
109
+ ```python
110
+ from ancestryaudit import AncestryAuditFramework
111
+ from sklearn.linear_model import LogisticRegression
112
+
113
+ framework = AncestryAuditFramework(
114
+ random_state=42,
115
+ n_bootstrap=1000,
116
+ threshold_pp=2.0, # minimum gap to trigger correction_required
117
+ threshold_p=0.05 # maximum p-value to trigger correction_required
118
+ )
119
+
120
+ # ── Step 1: Filter population-stratification noise (optional) ──────────────
121
+ X_western_filtered, kept_genes, filter_log = framework.filter_stratification_noise(
122
+ X_western_df, # pd.DataFrame with gene names as columns
123
+ gene_list # list of gene name strings
124
+ )
125
+ print(f"Removed {filter_log['n_removed']} junk genes, kept {filter_log['n_kept']}")
126
+
127
+ # ── Step 2: Audit ──────────────────────────────────────────────────────────
128
+ audit_report = framework.audit(
129
+ LogisticRegression(max_iter=1000),
130
+ X_western_filtered, y_western,
131
+ X_asian_filtered, y_asian
132
+ )
133
+ print(audit_report)
134
+ # AuditReport(gap=+2.39pp, p=0.0069, d=1.52, 95%CI=[0.80, 4.10],
135
+ # recommendation='correction_required')
136
+
137
+ # ── Step 3: Correct ────────────────────────────────────────────────────────
138
+ if audit_report.recommendation == "correction_required":
139
+ corrected_model, correction_report = framework.correct(
140
+ LogisticRegression(max_iter=1000),
141
+ X_western_filtered, y_western,
142
+ X_asian_labeled, y_asian_labeled, # labeled Asian samples
143
+ n_samples=75 # how many to include
144
+ )
145
+ print(correction_report)
146
+ # CorrectionReport(delta=+3.51pp, p=0.0012, n_used=75, all_positive=True)
147
+
148
+ # ── Step 4: Validate ───────────────────────────────────────────────────────
149
+ validation_report = framework.validate(
150
+ corrected_model,
151
+ X_asian_holdout, y_asian_holdout # never seen during correction
152
+ )
153
+ print(validation_report)
154
+ # ValidationReport(pre_gap=+2.39pp, post_gap=-1.12pp, improvement=+3.51pp)
155
+
156
+ # ── Step 5: Report ─────────────────────────────────────────────────────────
157
+ report_dict = framework.generate_report("my_audit_report.json")
158
+ framework.summary()
159
+ ```
160
+
161
+ ---
162
+
163
+ ## API Reference
164
+
165
+ ### `AncestryAuditFramework`
166
+
167
+ | Method | Description | Returns |
168
+ |--------|-------------|---------|
169
+ | `audit(model, X_source, y_source, X_target, y_target)` | Detect gap | `AuditReport` |
170
+ | `filter_stratification_noise(X, gene_list)` | Remove OR/pseudogene columns | `(X_filtered, kept_genes, filter_log)` |
171
+ | `correct(model, X_source, y_source, X_target_labeled, y_target_labeled, n_samples)` | Fine-tune correction | `(corrected_model, CorrectionReport)` |
172
+ | `validate(corrected_model, X_holdout, y_holdout)` | Post-correction audit | `ValidationReport` |
173
+ | `generate_report(save_path)` | Full JSON report | `dict` |
174
+ | `summary()` | Print pipeline summary | `str` |
175
+
176
+ ### `AuditReport` fields
177
+
178
+ | Field | Type | Description |
179
+ |-------|------|-------------|
180
+ | `gap_pp` | float | Accuracy gap in percentage points (positive = source better) |
181
+ | `p_value` | float | Two-sided p-value from bootstrap t-test |
182
+ | `cohen_d` | float | Effect size |
183
+ | `ci_95` | tuple | 95% bootstrap CI on gap_pp |
184
+ | `source_accuracy` | float | Model accuracy on held-out source data |
185
+ | `target_accuracy` | float | Model accuracy on target data |
186
+ | `n_source` | int | Source sample count |
187
+ | `n_target` | int | Target sample count |
188
+ | `recommendation` | str | `"correction_required"` or `"no_action"` |
189
+
190
+ ### `CorrectionReport` fields
191
+
192
+ | Field | Type | Description |
193
+ |-------|------|-------------|
194
+ | `delta_pp` | float | Mean accuracy improvement on target holdout (pp) |
195
+ | `p_value` | float | Two-sided t-test on per-seed deltas vs 0 |
196
+ | `n_used` | int | Target samples used (min of n_samples and available) |
197
+ | `seed_robustness` | dict | mean, sd, min, max, n_positive across 10 seeds |
198
+ | `all_positive` | bool | True if all seeds showed positive correction |
199
+ | `baseline_accuracy` | float | Source-only accuracy on full target |
200
+ | `corrected_accuracy` | float | Estimated corrected accuracy on full target |
201
+
202
+ ### `ValidationReport` fields
203
+
204
+ | Field | Type | Description |
205
+ |-------|------|-------------|
206
+ | `pre_gap` | float | Performance gap before correction (pp) |
207
+ | `post_gap` | float | Performance gap after correction (pp) |
208
+ | `correction_magnitude` | float | pre_gap - post_gap |
209
+ | `improvement_pp` | float | Accuracy improvement on target (pp) |
210
+ | `pre_accuracy_target` | float | Target accuracy before correction |
211
+ | `post_accuracy_target` | float | Target accuracy after correction |
212
+
213
+ ---
214
+
215
+ ## Filtering Details
216
+
217
+ `filter_stratification_noise` removes three gene categories that are known
218
+ to reflect population-level genetic drift rather than cancer biology:
219
+
220
+ - **Olfactory receptor genes** (`OR*`) β€” CNV in these clusters varies by
221
+ ancestral migration history, not cancer type
222
+ - **Pseudogenes** (`*P`, `*P1`, `*P2`, …) β€” non-functional, high
223
+ population-stratification signal
224
+ - **Uncharacterized loci** (names containing `.`) β€” clone-based placeholder
225
+ identifiers with no interpretable biological information
226
+
227
+ **Required Methods disclosure:** The feature space was defined using all
228
+ samples prior to train/test split, which constitutes a bounded form of
229
+ data snooping. No label information was used in this step (Kaufman et al., 2012).
230
+
231
+ ---
232
+
233
+ ## Expected Results
234
+
235
+ > **Disclaimer:** Audit results depend on the model, train/test split,
236
+ > and preprocessing pipeline provided. Results will differ from published
237
+ > paper figures, which used a 7-algorithm ensemble with specific PCA
238
+ > preprocessing. The library is designed for arbitrary input β€”
239
+ > directional consistency (not numerical identity) with paper results
240
+ > is the correct validation criterion.
241
+
242
+ **Validated reproduction test (Yergaliyeva, 2026):**
243
+
244
+ When provided with the exact paper train/test split (White n=338 train,
245
+ n=113 test; Asian n=242 evaluation) and gene-aligned PCA features,
246
+ the library reproduces paper results with 0.003pp numerical precision:
247
+
248
+ | Metric | Paper | Library |
249
+ |--------|-------|---------|
250
+ | Mean PGI | +2.39pp | +2.393pp |
251
+ | Algorithms positive | 7/7 | 7/7 |
252
+ | Direction | positive | positive |
253
+
254
+ All 6 API methods (import, audit, correct, validate, report, filter)
255
+ pass independent correctness tests on synthetic CNV data.
256
+
257
+ > **Note on train/test splits:** Provide the full dataset and let the
258
+ > framework handle splitting internally. Passing only the training
259
+ > portion causes the framework to re-split a subset, producing different
260
+ > model boundaries and non-comparable gap values.
261
+
262
+ ---
263
+
264
+ ## Citation
265
+
266
+ If you use AncestryAudit in research, please cite:
267
+
268
+ ```
269
+ Yergaliyeva, D. (2026). Ancestry-linked bias in genomic cancer AI:
270
+ Transfer learning correction for East Asian populations.
271
+ [Manuscript in preparation]
272
+ ```
273
+
274
+ ---
275
+
276
+ ## License
277
+
278
+ MIT License. Copyright (c) 2026 Dana Yergaliyeva.
@@ -0,0 +1,243 @@
1
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21290874.svg)](https://doi.org/10.5281/zenodo.21290874)
2
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
3
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/)
4
+
5
+ # AncestryAudit
6
+
7
+ **Bias detection and correction framework for genomic cancer AI.**
8
+
9
+ Detects ancestry-linked performance gaps in copy number variation (CNV)-based
10
+ cancer classifiers, applies supervised fine-tuning correction, and generates
11
+ structured audit reports.
12
+
13
+ Developed from research on ancestry bias in TCGA-LIHC/STAD classification
14
+ (Yergaliyeva, 2026).
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ pip install ancestryaudit
22
+ # or from source:
23
+ pip install -e .
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Input Format
29
+
30
+ AncestryAudit works on any CNV feature matrix:
31
+
32
+ | Format | Shape | Notes |
33
+ |--------|-------|-------|
34
+ | `np.ndarray` | `(n_samples, n_genes)` | Continuous copy-number values |
35
+ | `pd.DataFrame` | `(n_samples, n_genes)` | Column names = gene identifiers |
36
+
37
+ **Column values:** continuous copy number (e.g. TCGA ABSOLUTE pipeline output,
38
+ where 2.0 = normal diploid, >2 = amplification, <2 = deletion).
39
+
40
+ **Labels:** binary integer (0 or 1), one per sample.
41
+
42
+ **Models:** any scikit-learn compatible estimator with `fit` / `predict` interface.
43
+
44
+ ---
45
+
46
+ ## Quick Start
47
+
48
+ ```python
49
+ from ancestryaudit import AncestryAuditFramework
50
+ from sklearn.linear_model import LogisticRegression
51
+
52
+ framework = AncestryAuditFramework()
53
+
54
+ # Step 1: Detect ancestry-linked performance gap
55
+ report = framework.audit(
56
+ LogisticRegression(max_iter=1000),
57
+ X_western, y_western, # source (training) population
58
+ X_asian, y_asian # target (evaluation) population
59
+ )
60
+ print(f"Gap: {report.gap_pp:.2f}pp, p={report.p_value:.4f}")
61
+ print(f"Recommendation: {report.recommendation}")
62
+ ```
63
+
64
+ Output:
65
+ ```
66
+ Gap: +2.39pp, p=0.0069
67
+ Recommendation: correction_required
68
+ ```
69
+
70
+ ---
71
+
72
+ ## Full Pipeline
73
+
74
+ ```python
75
+ from ancestryaudit import AncestryAuditFramework
76
+ from sklearn.linear_model import LogisticRegression
77
+
78
+ framework = AncestryAuditFramework(
79
+ random_state=42,
80
+ n_bootstrap=1000,
81
+ threshold_pp=2.0, # minimum gap to trigger correction_required
82
+ threshold_p=0.05 # maximum p-value to trigger correction_required
83
+ )
84
+
85
+ # ── Step 1: Filter population-stratification noise (optional) ──────────────
86
+ X_western_filtered, kept_genes, filter_log = framework.filter_stratification_noise(
87
+ X_western_df, # pd.DataFrame with gene names as columns
88
+ gene_list # list of gene name strings
89
+ )
90
+ print(f"Removed {filter_log['n_removed']} junk genes, kept {filter_log['n_kept']}")
91
+
92
+ # ── Step 2: Audit ──────────────────────────────────────────────────────────
93
+ audit_report = framework.audit(
94
+ LogisticRegression(max_iter=1000),
95
+ X_western_filtered, y_western,
96
+ X_asian_filtered, y_asian
97
+ )
98
+ print(audit_report)
99
+ # AuditReport(gap=+2.39pp, p=0.0069, d=1.52, 95%CI=[0.80, 4.10],
100
+ # recommendation='correction_required')
101
+
102
+ # ── Step 3: Correct ────────────────────────────────────────────────────────
103
+ if audit_report.recommendation == "correction_required":
104
+ corrected_model, correction_report = framework.correct(
105
+ LogisticRegression(max_iter=1000),
106
+ X_western_filtered, y_western,
107
+ X_asian_labeled, y_asian_labeled, # labeled Asian samples
108
+ n_samples=75 # how many to include
109
+ )
110
+ print(correction_report)
111
+ # CorrectionReport(delta=+3.51pp, p=0.0012, n_used=75, all_positive=True)
112
+
113
+ # ── Step 4: Validate ───────────────────────────────────────────────────────
114
+ validation_report = framework.validate(
115
+ corrected_model,
116
+ X_asian_holdout, y_asian_holdout # never seen during correction
117
+ )
118
+ print(validation_report)
119
+ # ValidationReport(pre_gap=+2.39pp, post_gap=-1.12pp, improvement=+3.51pp)
120
+
121
+ # ── Step 5: Report ─────────────────────────────────────────────────────────
122
+ report_dict = framework.generate_report("my_audit_report.json")
123
+ framework.summary()
124
+ ```
125
+
126
+ ---
127
+
128
+ ## API Reference
129
+
130
+ ### `AncestryAuditFramework`
131
+
132
+ | Method | Description | Returns |
133
+ |--------|-------------|---------|
134
+ | `audit(model, X_source, y_source, X_target, y_target)` | Detect gap | `AuditReport` |
135
+ | `filter_stratification_noise(X, gene_list)` | Remove OR/pseudogene columns | `(X_filtered, kept_genes, filter_log)` |
136
+ | `correct(model, X_source, y_source, X_target_labeled, y_target_labeled, n_samples)` | Fine-tune correction | `(corrected_model, CorrectionReport)` |
137
+ | `validate(corrected_model, X_holdout, y_holdout)` | Post-correction audit | `ValidationReport` |
138
+ | `generate_report(save_path)` | Full JSON report | `dict` |
139
+ | `summary()` | Print pipeline summary | `str` |
140
+
141
+ ### `AuditReport` fields
142
+
143
+ | Field | Type | Description |
144
+ |-------|------|-------------|
145
+ | `gap_pp` | float | Accuracy gap in percentage points (positive = source better) |
146
+ | `p_value` | float | Two-sided p-value from bootstrap t-test |
147
+ | `cohen_d` | float | Effect size |
148
+ | `ci_95` | tuple | 95% bootstrap CI on gap_pp |
149
+ | `source_accuracy` | float | Model accuracy on held-out source data |
150
+ | `target_accuracy` | float | Model accuracy on target data |
151
+ | `n_source` | int | Source sample count |
152
+ | `n_target` | int | Target sample count |
153
+ | `recommendation` | str | `"correction_required"` or `"no_action"` |
154
+
155
+ ### `CorrectionReport` fields
156
+
157
+ | Field | Type | Description |
158
+ |-------|------|-------------|
159
+ | `delta_pp` | float | Mean accuracy improvement on target holdout (pp) |
160
+ | `p_value` | float | Two-sided t-test on per-seed deltas vs 0 |
161
+ | `n_used` | int | Target samples used (min of n_samples and available) |
162
+ | `seed_robustness` | dict | mean, sd, min, max, n_positive across 10 seeds |
163
+ | `all_positive` | bool | True if all seeds showed positive correction |
164
+ | `baseline_accuracy` | float | Source-only accuracy on full target |
165
+ | `corrected_accuracy` | float | Estimated corrected accuracy on full target |
166
+
167
+ ### `ValidationReport` fields
168
+
169
+ | Field | Type | Description |
170
+ |-------|------|-------------|
171
+ | `pre_gap` | float | Performance gap before correction (pp) |
172
+ | `post_gap` | float | Performance gap after correction (pp) |
173
+ | `correction_magnitude` | float | pre_gap - post_gap |
174
+ | `improvement_pp` | float | Accuracy improvement on target (pp) |
175
+ | `pre_accuracy_target` | float | Target accuracy before correction |
176
+ | `post_accuracy_target` | float | Target accuracy after correction |
177
+
178
+ ---
179
+
180
+ ## Filtering Details
181
+
182
+ `filter_stratification_noise` removes three gene categories that are known
183
+ to reflect population-level genetic drift rather than cancer biology:
184
+
185
+ - **Olfactory receptor genes** (`OR*`) β€” CNV in these clusters varies by
186
+ ancestral migration history, not cancer type
187
+ - **Pseudogenes** (`*P`, `*P1`, `*P2`, …) β€” non-functional, high
188
+ population-stratification signal
189
+ - **Uncharacterized loci** (names containing `.`) β€” clone-based placeholder
190
+ identifiers with no interpretable biological information
191
+
192
+ **Required Methods disclosure:** The feature space was defined using all
193
+ samples prior to train/test split, which constitutes a bounded form of
194
+ data snooping. No label information was used in this step (Kaufman et al., 2012).
195
+
196
+ ---
197
+
198
+ ## Expected Results
199
+
200
+ > **Disclaimer:** Audit results depend on the model, train/test split,
201
+ > and preprocessing pipeline provided. Results will differ from published
202
+ > paper figures, which used a 7-algorithm ensemble with specific PCA
203
+ > preprocessing. The library is designed for arbitrary input β€”
204
+ > directional consistency (not numerical identity) with paper results
205
+ > is the correct validation criterion.
206
+
207
+ **Validated reproduction test (Yergaliyeva, 2026):**
208
+
209
+ When provided with the exact paper train/test split (White n=338 train,
210
+ n=113 test; Asian n=242 evaluation) and gene-aligned PCA features,
211
+ the library reproduces paper results with 0.003pp numerical precision:
212
+
213
+ | Metric | Paper | Library |
214
+ |--------|-------|---------|
215
+ | Mean PGI | +2.39pp | +2.393pp |
216
+ | Algorithms positive | 7/7 | 7/7 |
217
+ | Direction | positive | positive |
218
+
219
+ All 6 API methods (import, audit, correct, validate, report, filter)
220
+ pass independent correctness tests on synthetic CNV data.
221
+
222
+ > **Note on train/test splits:** Provide the full dataset and let the
223
+ > framework handle splitting internally. Passing only the training
224
+ > portion causes the framework to re-split a subset, producing different
225
+ > model boundaries and non-comparable gap values.
226
+
227
+ ---
228
+
229
+ ## Citation
230
+
231
+ If you use AncestryAudit in research, please cite:
232
+
233
+ ```
234
+ Yergaliyeva, D. (2026). Ancestry-linked bias in genomic cancer AI:
235
+ Transfer learning correction for East Asian populations.
236
+ [Manuscript in preparation]
237
+ ```
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MIT License. Copyright (c) 2026 Dana Yergaliyeva.