Skill Tracer
Skills › Scientific Agent Skills › imaging-data-commons › parquet_access_guide.md

parquet_access_guide.md

226 lines · skills/imaging-data-commons/references/parquet_access_guide.md at 5f2bc03

Open on GitHub
1# Direct Parquet Access Guide for IDC
2
3**Reviewed 2026-09-30:** idc-index-data 24.2.2 (IDC data version v24), DuckDB 1.5
4
5All idc-index metadata tables are published as Parquet files to a public GCS bucket with unrestricted CORS access. This enables metadata queries with DuckDB or pandas without installing idc-index.
6
7**Limitation:** download helpers (`download_from_selection()`), viewer URLs (`get_viewer_URL()`), and citation generation require the idc-index client and are not available from raw Parquet files.
8
9**This is not the first no-install option to reach for.** It still needs DuckDB installed. This GCS artifact set contains only the `clinical_index` dictionary; full clinical and BigQuery tables are exported separately to public S3 (below). For ad-hoc metadata with nothing installed, the REST API (`rest_api_guide.md`) needs no install at all and reaches `clinical.<table>` through `POST /sql`.

Direct Parquet Access Guide for IDC

Reviewed 2026-09-30: idc-index-data 24.2.2 (IDC data version v24), DuckDB 1.5

All idc-index metadata tables are published as Parquet files to a public GCS bucket with unrestricted CORS access. This enables metadata queries with DuckDB or pandas without installing idc-index.

Limitation: download helpers (download_from_selection()), viewer URLs (get_viewer_URL()), and citation generation require the idc-index client and are not available from raw Parquet files.

This is not the first no-install option to reach for. It still needs DuckDB installed. This GCS artifact set contains only the clinical_index dictionary; full clinical and BigQuery tables are exported separately to public S3 (below). For ad-hoc metadata with nothing installed, the REST API (rest_api_guide.md) needs no install at all and reaches clinical.<table> through POST /sql.

Raw lines 1 to 10
11## When to Use This Guide
13Load this guide when you need to:
14- Pin queries to a specific IDC data version (see *Pinning to a Specific Version* below) rather than whatever the hosted API currently serves
15- Return more rows than the REST `/sql` ceiling of 10 000
16- Run heavy or repeated local DuckDB analysis without driving the hosted API
17- Query IDC metadata where DuckDB is available but idc-index is not
19For downloads, viewer URLs, and citations, use idc-index as documented in the main SKILL.md.

When to Use This Guide

Load this guide when you need to:

  • Pin queries to a specific IDC data version (see Pinning to a Specific Version below) rather than whatever the hosted API currently serves
  • Return more rows than the REST /sql ceiling of 10 000
  • Run heavy or repeated local DuckDB analysis without driving the hosted API
  • Query IDC metadata where DuckDB is available but idc-index is not

For downloads, viewer URLs, and citations, use idc-index as documented in the main SKILL.md.

Raw lines 11 to 20
21## URL Pattern
23```
24https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts/{filename}.parquet
25```
27`current/` always resolves to the latest data release. To pin to a specific version, replace `current` with the data version number (e.g., `23.10.1`).

URL Pattern

https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts/{filename}.parquet

current/ always resolves to the latest data release. To pin to a specific version, replace current with the data version number (e.g., 23.10.1).

Raw lines 21 to 28
29## Available Files
31| File | Approximate Size | Description |
32|------|-----------------|-------------|
33| `idc_index.parquet` | ~70 MB | Primary index (all DICOM series metadata) |
34| `volume_geometry_index.parquet` | ~5 MB | 3D geometry validation for CT/MR/PT series |
35| `rtstruct_index.parquet` | ~2 MB | RT Structure Set ROI metadata |
36| `seg_index.parquet` | ~6 MB | DICOM Segmentation cross-references |
37| `sm_index.parquet` | ~2 MB | Slide microscopy series metadata |
38| `contrast_index.parquet` | ~1 MB | Contrast agent metadata |
39| `ann_index.parquet` | ~0.2 MB | Microscopy annotation series metadata |
40| `ann_group_index.parquet` | ~0.5 MB | Annotation group metadata |
41| `collections_index.parquet` | — | Collection-level metadata |
42| `analysis_results_index.parquet` | — | Derived dataset metadata |
43| `clinical_index.parquet` | ~0.2 MB | Clinical data column dictionary |
44| `ct_index.parquet` | — | CT acquisition/reconstruction parameters |
45| `mr_index.parquet` | — | MR sequence/acquisition parameters |
46| `pt_index.parquet` | — | PET acquisition/radiopharmaceutical parameters |
47| `prior_versions_index.parquet` | — | Series from previous IDC releases |
48| `sm_instance_index.parquet` | — | Slide microscopy instance metadata |
49| `version_metadata_index.parquet` | — | IDC release timestamps |
51**Note:** the main index file is named `idc_index.parquet`, not `index.parquet`. Reference it with an alias in SQL queries (e.g., `FROM read_parquet(...) AS index`).

Available Files

FileApproximate SizeDescription
idc_index.parquet~70 MBPrimary index (all DICOM series metadata)
volume_geometry_index.parquet~5 MB3D geometry validation for CT/MR/PT series
rtstruct_index.parquet~2 MBRT Structure Set ROI metadata
seg_index.parquet~6 MBDICOM Segmentation cross-references
sm_index.parquet~2 MBSlide microscopy series metadata
contrast_index.parquet~1 MBContrast agent metadata
ann_index.parquet~0.2 MBMicroscopy annotation series metadata
ann_group_index.parquet~0.5 MBAnnotation group metadata
collections_index.parquet—Collection-level metadata
analysis_results_index.parquet—Derived dataset metadata
clinical_index.parquet~0.2 MBClinical data column dictionary
ct_index.parquet—CT acquisition/reconstruction parameters
mr_index.parquet—MR sequence/acquisition parameters
pt_index.parquet—PET acquisition/radiopharmaceutical parameters
prior_versions_index.parquet—Series from previous IDC releases
sm_instance_index.parquet—Slide microscopy instance metadata
version_metadata_index.parquet—IDC release timestamps

Note: the main index file is named idc_index.parquet, not index.parquet. Reference it with an alias in SQL queries (e.g., FROM read_parquet(...) AS index).

Raw lines 29 to 52
53## Prerequisites
55Install the Python `duckdb` package, using whatever installer manages the environment you are
56running in.
58DuckDB reads Parquet directly from HTTPS URLs using HTTP range requests — no GCS client library or authentication required.

Prerequisites

Install the Python duckdb package, using whatever installer manages the environment you are running in.

DuckDB reads Parquet directly from HTTPS URLs using HTTP range requests — no GCS client library or authentication required.

Raw lines 53 to 59
60## Basic Queries
62```python
63import duckdb
65BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"
67# Discover modalities and series counts
68duckdb.sql(f"""
69 SELECT Modality, COUNT(*) as series_count, ROUND(SUM(series_size_MB)/1000, 1) as size_GB
70 FROM read_parquet('{BASE}/idc_index.parquet')
71 GROUP BY Modality
72 ORDER BY series_count DESC
73""").df()
75# Collections with CT data, ordered by size
76duckdb.sql(f"""
77 SELECT collection_id,
78 COUNT(DISTINCT PatientID) as patients,
79 COUNT(*) as series,
80 ROUND(SUM(series_size_MB)/1000, 1) as size_GB
81 FROM read_parquet('{BASE}/idc_index.parquet')
82 WHERE Modality = 'CT'
83 GROUP BY collection_id
84 ORDER BY size_GB DESC
85 LIMIT 10
86""").df()
87```

Basic Queries

import duckdb

BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"

# Discover modalities and series counts
duckdb.sql(f"""
    SELECT Modality, COUNT(*) as series_count, ROUND(SUM(series_size_MB)/1000, 1) as size_GB
    FROM read_parquet('{BASE}/idc_index.parquet')
    GROUP BY Modality
    ORDER BY series_count DESC
""").df()

# Collections with CT data, ordered by size
duckdb.sql(f"""
    SELECT collection_id,
           COUNT(DISTINCT PatientID) as patients,
           COUNT(*) as series,
           ROUND(SUM(series_size_MB)/1000, 1) as size_GB
    FROM read_parquet('{BASE}/idc_index.parquet')
    WHERE Modality = 'CT'
    GROUP BY collection_id
    ORDER BY size_GB DESC
    LIMIT 10
""").df()
Raw lines 60 to 88
89## Volume Geometry Validation
91`volume_geometry_index` covers single-frame CT, MR, and PT series. Each row has boolean checks for orientation, spacing, dimensions, and slice positions, plus a composite `regularly_spaced_3d_volume` flag.
93```python
94import duckdb
96BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"
98# Candidate regularly spaced CT series; verify task-specific geometry before analysis
99duckdb.sql(f"""
100 SELECT i.collection_id, i.SeriesInstanceUID, i.BodyPartExamined,
101 v.obliquity_degrees, v.regularly_spaced_3d_volume
102 FROM read_parquet('{BASE}/idc_index.parquet') i
103 JOIN read_parquet('{BASE}/volume_geometry_index.parquet') v
104 ON i.SeriesInstanceUID = v.SeriesInstanceUID
105 WHERE i.Modality = 'CT'
106 AND v.regularly_spaced_3d_volume = TRUE
107 LIMIT 10
108""").df()
110# Fraction of 3D-valid series per collection and modality
111duckdb.sql(f"""
112 SELECT i.collection_id, i.Modality,
113 COUNT(*) as total,
114 SUM(CASE WHEN v.regularly_spaced_3d_volume THEN 1 ELSE 0 END) as valid_3d,
115 ROUND(100.0 * SUM(CASE WHEN v.regularly_spaced_3d_volume THEN 1 ELSE 0 END) / COUNT(*), 1) as pct_valid
116 FROM read_parquet('{BASE}/idc_index.parquet') i
117 JOIN read_parquet('{BASE}/volume_geometry_index.parquet') v
118 ON i.SeriesInstanceUID = v.SeriesInstanceUID
119 WHERE i.Modality IN ('CT', 'MR', 'PT')
120 GROUP BY i.collection_id, i.Modality
121 ORDER BY total DESC
122 LIMIT 10
123""").df()
124```
126Key columns in `volume_geometry_index`:
128| Column | Type | Description |
129|--------|------|-------------|
130| `SeriesInstanceUID` | STRING | Join key |
131| `single_orientation` | BOOLEAN | All instances share the same ImageOrientationPatient |
132| `orthogonal_orientation` | BOOLEAN | Orientation direction cosines are orthogonal |
133| `unique_slice_positions` | BOOLEAN | No duplicate or overlapping slices |
134| `consistent_pixel_spacing` | BOOLEAN | All instances share the same PixelSpacing |
135| `consistent_image_dimensions` | BOOLEAN | All instances share the same Rows and Columns |
136| `uniform_slice_spacing` | BOOLEAN | Spacing between consecutive slices is constant |
137| `obliquity_degrees` | FLOAT | Angle between slice normal and nearest cardinal axis (0 = pure axial/sagittal/coronal) |
138| `regularly_spaced_3d_volume` | BOOLEAN | Composite: TRUE if all checks pass |

Volume Geometry Validation

volume_geometry_index covers single-frame CT, MR, and PT series. Each row has boolean checks for orientation, spacing, dimensions, and slice positions, plus a composite regularly_spaced_3d_volume flag.

import duckdb

BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"

# Candidate regularly spaced CT series; verify task-specific geometry before analysis
duckdb.sql(f"""
    SELECT i.collection_id, i.SeriesInstanceUID, i.BodyPartExamined,
           v.obliquity_degrees, v.regularly_spaced_3d_volume
    FROM read_parquet('{BASE}/idc_index.parquet') i
    JOIN read_parquet('{BASE}/volume_geometry_index.parquet') v
        ON i.SeriesInstanceUID = v.SeriesInstanceUID
    WHERE i.Modality = 'CT'
      AND v.regularly_spaced_3d_volume = TRUE
    LIMIT 10
""").df()

# Fraction of 3D-valid series per collection and modality
duckdb.sql(f"""
    SELECT i.collection_id, i.Modality,
           COUNT(*) as total,
           SUM(CASE WHEN v.regularly_spaced_3d_volume THEN 1 ELSE 0 END) as valid_3d,
           ROUND(100.0 * SUM(CASE WHEN v.regularly_spaced_3d_volume THEN 1 ELSE 0 END) / COUNT(*), 1) as pct_valid
    FROM read_parquet('{BASE}/idc_index.parquet') i
    JOIN read_parquet('{BASE}/volume_geometry_index.parquet') v
        ON i.SeriesInstanceUID = v.SeriesInstanceUID
    WHERE i.Modality IN ('CT', 'MR', 'PT')
    GROUP BY i.collection_id, i.Modality
    ORDER BY total DESC
    LIMIT 10
""").df()

Key columns in volume_geometry_index:

ColumnTypeDescription
SeriesInstanceUIDSTRINGJoin key
single_orientationBOOLEANAll instances share the same ImageOrientationPatient
orthogonal_orientationBOOLEANOrientation direction cosines are orthogonal
unique_slice_positionsBOOLEANNo duplicate or overlapping slices
consistent_pixel_spacingBOOLEANAll instances share the same PixelSpacing
consistent_image_dimensionsBOOLEANAll instances share the same Rows and Columns
uniform_slice_spacingBOOLEANSpacing between consecutive slices is constant
obliquity_degreesFLOATAngle between slice normal and nearest cardinal axis (0 = pure axial/sagittal/coronal)
regularly_spaced_3d_volumeBOOLEANComposite: TRUE if all checks pass
Raw lines 89 to 139
140## RT Structure Sets
142`rtstruct_index` has one row per RTSTRUCT series with aggregated ROI metadata.
144```python
145import duckdb
147BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"
149# RTSTRUCT series with ROI details
150duckdb.sql(f"""
151 SELECT i.collection_id, i.SeriesInstanceUID,
152 r.total_rois, r.ROINames, r.RTROIInterpretedTypes,
153 r.referenced_SeriesInstanceUID
154 FROM read_parquet('{BASE}/idc_index.parquet') i
155 JOIN read_parquet('{BASE}/rtstruct_index.parquet') r
156 ON i.SeriesInstanceUID = r.SeriesInstanceUID
157 WHERE i.Modality = 'RTSTRUCT'
158 LIMIT 5
159""").df()
161# Collections with the most RTSTRUCT series
162duckdb.sql(f"""
163 SELECT i.collection_id,
164 COUNT(*) as rtstruct_series,
165 ROUND(AVG(r.total_rois), 1) as avg_rois_per_struct
166 FROM read_parquet('{BASE}/idc_index.parquet') i
167 JOIN read_parquet('{BASE}/rtstruct_index.parquet') r
168 ON i.SeriesInstanceUID = r.SeriesInstanceUID
169 GROUP BY i.collection_id
170 ORDER BY rtstruct_series DESC
171 LIMIT 10
172""").df()
173```
175Key columns in `rtstruct_index`:
177| Column | Type | Description |
178|--------|------|-------------|
179| `SeriesInstanceUID` | STRING | Join key (the RTSTRUCT series) |
180| `total_rois` | INTEGER | Number of ROIs in the structure set |
181| `ROINames` | STRING (array) | Distinct ROI names (e.g., `["GTV", "Heart", "PTV"]`) |
182| `ROIGenerationAlgorithms` | STRING (array) | Distinct generation algorithms (e.g., `["AUTOMATIC", "MANUAL"]`) |
183| `RTROIInterpretedTypes` | STRING (array) | Distinct ROI types (e.g., `["GTV", "ORGAN", "PTV"]`) |
184| `referenced_SeriesInstanceUID` | STRING | SeriesInstanceUID of the referenced source image series |

RT Structure Sets

rtstruct_index has one row per RTSTRUCT series with aggregated ROI metadata.

import duckdb

BASE = "https://storage.googleapis.com/idc-index-data-artifacts/current/release_artifacts"

# RTSTRUCT series with ROI details
duckdb.sql(f"""
    SELECT i.collection_id, i.SeriesInstanceUID,
           r.total_rois, r.ROINames, r.RTROIInterpretedTypes,
           r.referenced_SeriesInstanceUID
    FROM read_parquet('{BASE}/idc_index.parquet') i
    JOIN read_parquet('{BASE}/rtstruct_index.parquet') r
        ON i.SeriesInstanceUID = r.SeriesInstanceUID
    WHERE i.Modality = 'RTSTRUCT'
    LIMIT 5
""").df()

# Collections with the most RTSTRUCT series
duckdb.sql(f"""
    SELECT i.collection_id,
           COUNT(*) as rtstruct_series,
           ROUND(AVG(r.total_rois), 1) as avg_rois_per_struct
    FROM read_parquet('{BASE}/idc_index.parquet') i
    JOIN read_parquet('{BASE}/rtstruct_index.parquet') r
        ON i.SeriesInstanceUID = r.SeriesInstanceUID
    GROUP BY i.collection_id
    ORDER BY rtstruct_series DESC
    LIMIT 10
""").df()

Key columns in rtstruct_index:

ColumnTypeDescription
SeriesInstanceUIDSTRINGJoin key (the RTSTRUCT series)
total_roisINTEGERNumber of ROIs in the structure set
ROINamesSTRING (array)Distinct ROI names (e.g., ["GTV", "Heart", "PTV"])
ROIGenerationAlgorithmsSTRING (array)Distinct generation algorithms (e.g., ["AUTOMATIC", "MANUAL"])
RTROIInterpretedTypesSTRING (array)Distinct ROI types (e.g., ["GTV", "ORGAN", "PTV"])
referenced_SeriesInstanceUIDSTRINGSeriesInstanceUID of the referenced source image series
Raw lines 140 to 185
186## Pinning to a Specific Version
188```python
189import duckdb
191# Use a specific data release instead of 'current'
192VERSION = "24.2.2"
193BASE = f"https://storage.googleapis.com/idc-index-data-artifacts/{VERSION}/release_artifacts"
195duckdb.sql(f"SELECT COUNT(*) FROM read_parquet('{BASE}/idc_index.parquet')").df()
196```

Pinning to a Specific Version

import duckdb

# Use a specific data release instead of 'current'
VERSION = "24.2.2"
BASE = f"https://storage.googleapis.com/idc-index-data-artifacts/{VERSION}/release_artifacts"

duckdb.sql(f"SELECT COUNT(*) FROM read_parquet('{BASE}/idc_index.parquet')").df()
Raw lines 186 to 197
198## Full metadata and clinical exports
200The separate public bucket `idc-open-metadata` exports BigQuery tables, including per-
201collection clinical rows, per-segment metadata, and SR measurements. No Google account
202is required. Use a pinned dataset such as `idc_v24_clinical`; discover object keys first
203because a table can be one file or a directory of shards. Do not append `.parquet` blindly.
205```bash
206s5cmd --no-sign-request ls s3://idc-open-metadata/bigquery_export/idc_v24_clinical/
207s5cmd --no-sign-request ls s3://idc-open-metadata/bigquery_export/idc_v24/
208```
210For example, after confirming the key, the clinical table can be queried directly:
212```python
213import duckdb
214url = "https://idc-open-metadata.s3.amazonaws.com/bigquery_export/idc_v24_clinical/nlst_canc/000000000000.parquet"
215clinical = duckdb.sql(f"SELECT dicom_patient_id, clinical_stag FROM read_parquet('{url}') LIMIT 5").df()
216```
218Full instance tables can be very large. Inspect their schema and estimate resources before
219querying all shards; GoogleSQL from the BigQuery guide needs adaptation to DuckDB syntax.
220See [IDC files and metadata](https://learn.canceridc.dev/data/organization-of-data/files-and-metadata).

Full metadata and clinical exports

The separate public bucket idc-open-metadata exports BigQuery tables, including per- collection clinical rows, per-segment metadata, and SR measurements. No Google account is required. Use a pinned dataset such as idc_v24_clinical; discover object keys first because a table can be one file or a directory of shards. Do not append .parquet blindly.

s5cmd --no-sign-request ls s3://idc-open-metadata/bigquery_export/idc_v24_clinical/
s5cmd --no-sign-request ls s3://idc-open-metadata/bigquery_export/idc_v24/

For example, after confirming the key, the clinical table can be queried directly:

import duckdb
url = "https://idc-open-metadata.s3.amazonaws.com/bigquery_export/idc_v24_clinical/nlst_canc/000000000000.parquet"
clinical = duckdb.sql(f"SELECT dicom_patient_id, clinical_stag FROM read_parquet('{url}') LIMIT 5").df()

Full instance tables can be very large. Inspect their schema and estimate resources before querying all shards; GoogleSQL from the BigQuery guide needs adaptation to DuckDB syntax. See IDC files and metadata.

Raw lines 198 to 221
222## Resources
224- idc-index-data releases: https://github.com/ImagingDataCommons/idc-index-data/releases
225- idc-index documentation: https://idc-index.readthedocs.io/
226- IDC Portal: https://portal.imaging.datacommons.cancer.gov/

Resources

  • idc-index-data releases: https://github.com/ImagingDataCommons/idc-index-data/releases
  • idc-index documentation: https://idc-index.readthedocs.io/
  • IDC Portal: https://portal.imaging.datacommons.cancer.gov/
Raw lines 222 to 226