11"""Check which IDC PatientIDs from GDC-related collections exist in the Genomic Data Commons.
22
33PatientID is only unique within a collection/project, so the GDC lookup is
4- scoped by project. IDC collection_id maps to GDC project. project_id with one
5- special case: all CPTAC sub- collections in IDC (cptac_brca, cptac_luad, ...)
6- fall under CPTAC-2 or CPTAC-3 in GDC, so those are queried together .
4+ scoped by project. The mapping from IDC collection_id to GDC project_id is
5+ explicit in IDC_TO_GDC_PROJECTS; add new collections there when they are added
6+ to the SQL query .
77"""
88
99from __future__ import annotations
2929SQL_FILE = Path (__file__ ).parent / "idc_gdc_selection.sql"
3030OUTPUT_PARQUET = "gdc_idc_mapping.parquet"
3131
32- # IDC CPTAC sub-collections map to these GDC umbrella projects.
33- GDC_CPTAC_PROJECTS = ["CPTAC-2" , "CPTAC-3" ]
32+ # Explicit mapping from IDC collection_id to GDC project_id(s).
33+ # CPTAC sub-collections in IDC all map to the GDC umbrella projects CPTAC-2 and CPTAC-3.
34+ # CDDP_EAGLE-1 uses an underscore in the GDC project name (not a hyphen like most programs).
35+ IDC_TO_GDC_PROJECTS : dict [str , list [str ]] = {
36+ # TCGA
37+ "tcga_acc" : ["TCGA-ACC" ],
38+ "tcga_blca" : ["TCGA-BLCA" ],
39+ "tcga_brca" : ["TCGA-BRCA" ],
40+ "tcga_cesc" : ["TCGA-CESC" ],
41+ "tcga_chol" : ["TCGA-CHOL" ],
42+ "tcga_coad" : ["TCGA-COAD" ],
43+ "tcga_dlbc" : ["TCGA-DLBC" ],
44+ "tcga_esca" : ["TCGA-ESCA" ],
45+ "tcga_gbm" : ["TCGA-GBM" ],
46+ "tcga_hnsc" : ["TCGA-HNSC" ],
47+ "tcga_kich" : ["TCGA-KICH" ],
48+ "tcga_kirc" : ["TCGA-KIRC" ],
49+ "tcga_kirp" : ["TCGA-KIRP" ],
50+ "tcga_lgg" : ["TCGA-LGG" ],
51+ "tcga_lihc" : ["TCGA-LIHC" ],
52+ "tcga_luad" : ["TCGA-LUAD" ],
53+ "tcga_lusc" : ["TCGA-LUSC" ],
54+ "tcga_meso" : ["TCGA-MESO" ],
55+ "tcga_ov" : ["TCGA-OV" ],
56+ "tcga_paad" : ["TCGA-PAAD" ],
57+ "tcga_pcpg" : ["TCGA-PCPG" ],
58+ "tcga_prad" : ["TCGA-PRAD" ],
59+ "tcga_read" : ["TCGA-READ" ],
60+ "tcga_sarc" : ["TCGA-SARC" ],
61+ "tcga_skcm" : ["TCGA-SKCM" ],
62+ "tcga_stad" : ["TCGA-STAD" ],
63+ "tcga_tgct" : ["TCGA-TGCT" ],
64+ "tcga_thca" : ["TCGA-THCA" ],
65+ "tcga_thym" : ["TCGA-THYM" ],
66+ "tcga_ucec" : ["TCGA-UCEC" ],
67+ "tcga_ucs" : ["TCGA-UCS" ],
68+ "tcga_uvm" : ["TCGA-UVM" ],
69+ # CPTAC (IDC per-cancer sub-collections map to GDC umbrella projects)
70+ "cptac_aml" : ["CPTAC-2" , "CPTAC-3" ],
71+ "cptac_brca" : ["CPTAC-2" , "CPTAC-3" ],
72+ "cptac_ccrcc" : ["CPTAC-2" , "CPTAC-3" ],
73+ "cptac_cm" : ["CPTAC-2" , "CPTAC-3" ],
74+ "cptac_coad" : ["CPTAC-2" , "CPTAC-3" ],
75+ "cptac_gbm" : ["CPTAC-2" , "CPTAC-3" ],
76+ "cptac_hnscc" : ["CPTAC-2" , "CPTAC-3" ],
77+ "cptac_lscc" : ["CPTAC-2" , "CPTAC-3" ],
78+ "cptac_luad" : ["CPTAC-2" , "CPTAC-3" ],
79+ "cptac_ov" : ["CPTAC-2" , "CPTAC-3" ],
80+ "cptac_pda" : ["CPTAC-2" , "CPTAC-3" ],
81+ "cptac_sar" : ["CPTAC-2" , "CPTAC-3" ],
82+ "cptac_stad" : ["CPTAC-2" , "CPTAC-3" ],
83+ "cptac_ucec" : ["CPTAC-2" , "CPTAC-3" ],
84+ # Other GDC programs
85+ "ccdi_mci" : ["CCDI-MCI" ],
86+ "varepop_apollo" : ["VAREPOP-APOLLO" ],
87+ # v24 new GDC collections
88+ "cddp_eagle_1" : ["CDDP_EAGLE-1" ],
89+ "cgci_blgsp" : ["CGCI-BLGSP" ],
90+ "cgci_htmcp_cc" : ["CGCI-HTMCP-CC" ],
91+ "cgci_htmcp_dlbcl" : ["CGCI-HTMCP-DLBCL" ],
92+ "cgci_htmcp_lc" : ["CGCI-HTMCP-LC" ],
93+ "hcmi_cmdc" : ["HCMI-CMDC" ],
94+ }
95+
96+ # Collections confirmed to have no cases in GDC (imaging-only, never registered).
97+ # These are excluded from the zero-match check in validate_gdc_matches().
98+ GDC_ABSENT_COLLECTIONS : frozenset [str ] = frozenset ({"cptac_cm" , "cptac_sar" })
3499
35100
36101def _create_session () -> requests .Session :
@@ -49,15 +114,16 @@ def _create_session() -> requests.Session:
49114
50115
51116def idc_collection_to_gdc_projects (collection_id : str ) -> list [str ]:
52- """Map an IDC collection_id to the corresponding GDC project_id(s).
117+ """Return the GDC project_id(s) for the given IDC collection_id .
53118
54- For most collections the mapping is simply upper-case + hyphens
55- (e.g. tcga_brca -> TCGA-BRCA). CPTAC is the exception: IDC has
56- per-cancer collections but GDC groups them under CPTAC-2 / CPTAC-3.
119+ Raises ValueError for unknown collections so missing mappings are caught
120+ immediately rather than silently querying the wrong GDC project.
57121 """
58- if collection_id .startswith ("cptac_" ):
59- return GDC_CPTAC_PROJECTS
60- return [collection_id .replace ("_" , "-" ).upper ()]
122+ projects = IDC_TO_GDC_PROJECTS .get (collection_id )
123+ if projects is None :
124+ msg = f"No GDC project mapping for IDC collection { collection_id !r} — add it to IDC_TO_GDC_PROJECTS"
125+ raise ValueError (msg )
126+ return projects
61127
62128
63129def run_bigquery (project_id : str | None = None ) -> pd .DataFrame :
@@ -266,6 +332,28 @@ def save_results(df: pd.DataFrame, output_path: str = OUTPUT_PARQUET) -> None:
266332 con .close ()
267333
268334
335+ def validate_gdc_matches (studies_df : pd .DataFrame ) -> None :
336+ """Raise an error if any mapped collection has zero GDC case ID matches.
337+
338+ Collections in GDC_ABSENT_COLLECTIONS are exempt (confirmed not in GDC).
339+ A zero-match result for any other collection most likely indicates a wrong
340+ GDC project ID in IDC_TO_GDC_PROJECTS.
341+ """
342+ errors : list [str ] = []
343+ for cid , group in studies_df .groupby ("collection_id" ):
344+ if cid in GDC_ABSENT_COLLECTIONS :
345+ continue
346+ if group ["gdc_case_id" ].notna ().sum () == 0 :
347+ errors .append (
348+ f" { cid } : 0/{ len (group )} rows matched — check IDC_TO_GDC_PROJECTS"
349+ )
350+ if errors :
351+ msg = "Collections with no GDC matches (expected at least one):\n " + "\n " .join (
352+ errors
353+ )
354+ raise RuntimeError (msg )
355+
356+
269357def main () -> None :
270358 """Main entry point."""
271359 project_id = os .environ .get ("GCP_PROJECT" )
@@ -298,6 +386,7 @@ def main() -> None:
298386 f" Patients: { n_patients_found } /{ n_patients } unique (collection, PatientID) pairs in GDC"
299387 )
300388
389+ validate_gdc_matches (studies_df )
301390 save_results (studies_df )
302391
303392
0 commit comments