BigQuery Guide for IDC
**Tested with:** IDC data version v23
BigQuery Guide for IDC
Tested with: IDC data version v23
For most queries and downloads, use idc-index (see main SKILL.md). This guide covers BigQuery for advanced use cases requiring full DICOM metadata or complex joins.
Prerequisites
Requirements:
- Google account
- Google Cloud project with billing enabled (first 1 TB/month free)
google-cloud-bigqueryPython package or BigQuery console access
Authentication setup:
# Install Google Cloud SDK, then:
gcloud auth application-default login
When to Use BigQuery
Use BigQuery instead of idc-index when you need:
- Full DICOM metadata (all 4000+ tags, not just the ~50 in idc-index)
- Complex joins across clinical data tables
- DICOM sequence attributes (nested structures)
- Queries on fields not in the idc-index mini-index
- Private DICOM elements (vendor-specific tags in OtherElements column)
- Per-segment detail from DICOM Segmentation objects —
idc-indexseg_indexgives series-level metadata, but not individual segment anatomy codes; usesegmentationsBigQuery table to query by structure name - Quantitative measurements from DICOM SR — radiomics features (volume, diameter, shape descriptors) without downloading and parsing SR files; no idc-index equivalent
- Qualitative measurements from DICOM SR — coded evaluations (malignancy rating, texture, margin) without parsing SR files; no idc-index equivalent
Accessing IDC in BigQuery
Dataset Structure
All IDC tables are in the bigquery-public-data BigQuery project.
Current version (recommended for exploration):
bigquery-public-data.idc_current.*bigquery-public-data.idc_current_clinical.*
Versioned datasets (recommended for reproducibility):
bigquery-public-data.idc_v{IDC version}.*bigquery-public-data.idc_v{IDC version}_clinical.*
Always use versioned datasets for reproducible research!
Key Tables
dicom_all
Primary table joining complete DICOM metadata with IDC-specific columns (collection_id, gcs_url, license). Contains all DICOM tags from dicom_metadata plus collection and administrative metadata. See dicom_all.sql for the exact derivation.
SELECT
collection_id,
PatientID,
StudyInstanceUID,
SeriesInstanceUID,
Modality,
BodyPartExamined,
SeriesDescription,
gcs_url,
license_short_name
FROM `bigquery-public-data.idc_current.dicom_all`
WHERE Modality = 'CT'
AND BodyPartExamined = 'CHEST'
LIMIT 10
Derived Tables
These tables are derived from DICOM objects (Segmentation and Structured Report) and have no equivalent in idc-index. Use them to query per-segment anatomy, radiomics features, and qualitative assessments without downloading DICOM files.
segmentations — one row per segment within a DICOM SEG object. Lets you search by anatomical structure name or DICOM coded concept. The idc-index seg_index gives series-level metadata; this table gives per-segment detail.
measurement_groups — one row per SR TID1500 measurement group. The parent grouping for quantitative and qualitative measurements; links measurements to segmentations and source images.
quantitative_measurements — one row per numeric measurement within an SR TID1500 group. Contains radiomics features (volume, diameter, shape descriptors, texture) extracted from DICOM SR without downloading or parsing SR files.
qualitative_measurements — one row per coded evaluation within an SR TID1500 group. Contains assessed findings (malignancy likelihood, texture, margin type) using coded concept values.
See the Derived Tables: Detailed Documentation section below for schemas, column descriptions, and query examples.
Collection Metadata
original_collections_metadata - Collection-level descriptions
SELECT
collection_id,
CancerTypes,
TumorLocations,
Subjects,
src.source_doi,
src.ImageTypes,
src.license.license_short_name
FROM `bigquery-public-data.idc_current.original_collections_metadata`,
UNNEST(Sources) AS src
WHERE CancerTypes LIKE '%Lung%'
Common Query Patterns
Find Collections by Criteria
SELECT
collection_id,
COUNT(DISTINCT PatientID) as patient_count,
COUNT(DISTINCT SeriesInstanceUID) as series_count,
ARRAY_AGG(DISTINCT Modality) as modalities
FROM `bigquery-public-data.idc_current.dicom_all`
WHERE BodyPartExamined LIKE '%BRAIN%'
GROUP BY collection_id
HAVING patient_count > 50
ORDER BY patient_count DESC
Get Download URLs
SELECT
SeriesInstanceUID,
gcs_url
FROM `bigquery-public-data.idc_current.dicom_all`
WHERE collection_id = 'rider_pilot'
AND Modality = 'CT'
Find Studies with Multiple Modalities
SELECT
StudyInstanceUID,
ARRAY_AGG(DISTINCT Modality) as modalities,
COUNT(DISTINCT SeriesInstanceUID) as series_count
FROM `bigquery-public-data.idc_current.dicom_all`
GROUP BY StudyInstanceUID
HAVING ARRAY_LENGTH(ARRAY_AGG(DISTINCT Modality)) > 1
LIMIT 100
License Filtering
SELECT
collection_id,
license_short_name,
COUNT(*) as instance_count
FROM `bigquery-public-data.idc_current.dicom_all`
WHERE license_short_name = 'CC BY 4.0'
GROUP BY collection_id, license_short_name
Find Segmentations with Source Images
SELECT
src.collection_id,
seg.SeriesInstanceUID as seg_series,
seg.SegmentedPropertyType,
src.SeriesInstanceUID as source_series,
src.Modality as source_modality
FROM `bigquery-public-data.idc_current.segmentations` seg
JOIN `bigquery-public-data.idc_current.dicom_all` src
ON seg.segmented_SeriesInstanceUID = src.SeriesInstanceUID
WHERE src.collection_id = 'qin_prostate_repeatability'
LIMIT 10
Derived Tables: Detailed Documentation
segmentations
One row per segment within a DICOM Segmentation (SEG) object. Unlike idc-index seg_index (one row per SEG series), this table exposes each labeled region individually so you can search by anatomical structure or finding type.
Key columns:
| Column | Type | Description |
|---|---|---|
SeriesInstanceUID | STRING | SEG series UID |
SOPInstanceUID | STRING | SEG instance UID |
PatientID | STRING | Patient identifier |
StudyInstanceUID | STRING | Study UID |
SegmentNumber | INTEGER | Segment index within the SEG (starting from 1) |
SegmentedPropertyCategory | RECORD | Coded category (e.g., "Anatomical Structure", "Morphologically Altered Structure") |
SegmentedPropertyType | RECORD | Specific structure (e.g., "Liver", "Kidney", "Neoplasm") |
AnatomicRegion | RECORD | Optional anatomic region modifier |
SegmentAlgorithmType | STRING | AUTOMATIC, SEMIAUTOMATIC, or MANUAL |
SegmentAlgorithmName | STRING (REPEATED) | Algorithm name array (e.g., ["TotalSegmentator"]) |
TrackingUID | STRING | Links segment to SR measurements |
TrackingID | STRING | Human-readable tracking label |
segmented_SeriesInstanceUID | STRING | Source image series UID — join to dicom_all to get collection/modality |
viewer_url | STRING | Direct IDC viewer link for the SEG |
SegmentedPropertyCategory and SegmentedPropertyType are RECORD types with sub-fields CodeValue, CodingSchemeDesignator, and CodeMeaning. Use .CodeMeaning for human-readable filtering.
idc-index gap: seg_index in idc-index has total_segments, AlgorithmName, and aggregated codes, but does not expose individual segment anatomy per row. Use this BigQuery table when you need to find SEG series that contain a specific structure (e.g., all series with a "Liver" segment).
Discover what structures are segmented across IDC:
SELECT
SegmentedPropertyCategory.CodeMeaning AS category,
SegmentedPropertyType.CodeMeaning AS structure,
SegmentAlgorithmType,
COUNT(DISTINCT SeriesInstanceUID) AS seg_series_count
FROM `bigquery-public-data.idc_current.segmentations`
GROUP BY 1, 2, 3
ORDER BY seg_series_count DESC
LIMIT 20
Find all SEG series containing a specific structure, with source image context:
SELECT
seg.SeriesInstanceUID AS seg_series,
seg.SegmentNumber,
seg.SegmentedPropertyType.CodeMeaning AS structure,
seg.SegmentAlgorithmType,
seg.SegmentAlgorithmName,
img.collection_id,
img.PatientID,
img.Modality,
seg.viewer_url
FROM `bigquery-public-data.idc_current.segmentations` seg
JOIN `bigquery-public-data.idc_current.dicom_all` img
ON seg.segmented_SeriesInstanceUID = img.SeriesInstanceUID
WHERE seg.SegmentedPropertyType.CodeMeaning = 'Liver'
AND seg.SegmentAlgorithmType = 'AUTOMATIC'
LIMIT 20
Find all segment types present in a collection:
SELECT
seg.SegmentedPropertyType.CodeMeaning AS structure,
seg.SegmentAlgorithmType,
COUNT(DISTINCT seg.SeriesInstanceUID) AS seg_series_count
FROM `bigquery-public-data.idc_current.segmentations` seg
JOIN `bigquery-public-data.idc_current.dicom_all` img
ON seg.segmented_SeriesInstanceUID = img.SeriesInstanceUID
WHERE img.collection_id = 'nlst'
GROUP BY 1, 2
ORDER BY seg_series_count DESC
Link segments to SR measurements using TrackingUID:
-- Find segments that have corresponding SR measurements
SELECT
seg.SeriesInstanceUID AS seg_series,
seg.SegmentNumber,
seg.SegmentedPropertyType.CodeMeaning AS structure,
qm.Quantity.CodeMeaning AS measurement,
ROUND(CAST(qm.Value AS FLOAT64), 2) AS value,
qm.Units.CodeMeaning AS units
FROM `bigquery-public-data.idc_current.segmentations` seg
JOIN `bigquery-public-data.idc_current.quantitative_measurements` qm
ON seg.SeriesInstanceUID = qm.segmentationSeriesUID
AND seg.SegmentNumber = qm.segmentationSegmentNumber
WHERE seg.SegmentedPropertyType.CodeMeaning = 'Neoplasm'
AND qm.Quantity.CodeMeaning = 'Volume from Voxel Summation'
LIMIT 10
quantitative_measurements
One row per numeric measurement in a DICOM SR TID1500 Measurement Report. Contains radiomics features (shape, intensity, texture) and clinical measurements (volume, diameter, SUV). These measurements are pre-extracted from SR — no download or DICOM parsing needed.
No idc-index equivalent. This table is only accessible via BigQuery.
Key columns:
| Column | Type | Description |
|---|---|---|
SOPInstanceUID | STRING | SR instance UID |
SeriesInstanceUID | STRING | SR series UID — join to dicom_all for collection/modality |
SeriesDescription | STRING | SR series description (e.g., "TotalSegmentator(v1.5.6) shape Measurements") |
PatientID | STRING | Patient identifier |
measurementGroup_number | INTEGER | Group index within the SR (0-based); join key with measurement_groups and qualitative_measurements |
Quantity | RECORD | What was measured — CodeValue, CodingSchemeDesignator, CodeMeaning (e.g., "Volume from Voxel Summation") |
Value | NUMERIC | The numeric measurement value |
Units | RECORD | Units — CodeMeaning (e.g., "cubic millimeter", "no units", "Hounsfield Unit") |
derivationModifier | RECORD | How the value was derived (e.g., "Mean", "Minimum", "Maximum") |
lateralityModifier | RECORD | Laterality qualifier |
finding | RECORD | What finding was measured — CodeMeaning (e.g., "Nodule", "Organ", "Anatomical Structure") |
findingSite | RECORD | Where the finding is — CodeMeaning (e.g., "Liver", "Esophagus", "Lung") |
trackingIdentifier | STRING | Human-readable tracking label (e.g., "Nodule 1", "Measurements group 26") |
trackingUniqueIdentifier | STRING | Tracking UID — links back to segmentations.TrackingUID |
segmentationInstanceUID | STRING | SOPInstanceUID of the referenced SEG object |
segmentationSeriesUID | STRING | SeriesInstanceUID of the referenced SEG object |
segmentationSegmentNumber | INTEGER | Segment number within the SEG — join to segmentations.SegmentNumber |
sourceSegmentedSeriesUID | STRING | Source image series — join to dicom_all.SeriesInstanceUID |
Discover available measurement types:
SELECT
Quantity.CodeMeaning AS measurement,
Units.CodeMeaning AS units,
COUNT(*) AS measurement_count,
COUNT(DISTINCT SeriesInstanceUID) AS sr_series_count
FROM `bigquery-public-data.idc_current.quantitative_measurements`
GROUP BY 1, 2
ORDER BY measurement_count DESC
LIMIT 20
Query measurements for a specific structure (e.g., liver volume across collections):
SELECT
qm.PatientID,
ROUND(CAST(qm.Value AS FLOAT64) / 1000, 1) AS volume_cm3,
img.collection_id,
qm.segmentationSeriesUID
FROM `bigquery-public-data.idc_current.quantitative_measurements` qm
JOIN `bigquery-public-data.idc_current.dicom_all` img
ON qm.sourceSegmentedSeriesUID = img.SeriesInstanceUID
WHERE qm.Quantity.CodeMeaning = 'Volume from Voxel Summation'
AND qm.findingSite.CodeMeaning = 'Liver'
ORDER BY volume_cm3 DESC
LIMIT 20
Retrieve all measurements for a specific patient and finding:
SELECT
qm.measurementGroup_number,
qm.finding.CodeMeaning AS finding,
qm.findingSite.CodeMeaning AS finding_site,
qm.lateralityModifier.CodeMeaning AS laterality,
qm.Quantity.CodeMeaning AS feature,
ROUND(CAST(qm.Value AS FLOAT64), 3) AS value,
qm.Units.CodeMeaning AS units
FROM `bigquery-public-data.idc_current.quantitative_measurements` qm
WHERE qm.PatientID = 'LIDC-IDRI-0001'
AND qm.finding.CodeMeaning = 'Nodule'
ORDER BY qm.measurementGroup_number, qm.Quantity.CodeMeaning
qualitative_measurements
One row per coded evaluation in a DICOM SR TID1500 Measurement Report. Instead of numeric values, these record assessed characteristics using coded concept pairs (e.g., Quantity="Malignancy", Value="4 out of 5 (Moderately Suspicious for Cancer)").
No idc-index equivalent. This table is only accessible via BigQuery.
Key columns:
| Column | Type | Description |
|---|---|---|
SOPInstanceUID | STRING | SR instance UID |
SeriesInstanceUID | STRING | SR series UID — join to dicom_all for collection/modality |
PatientID | STRING | Patient identifier |
measurementGroup_number | INTEGER | Group index within the SR — join key with quantitative_measurements |
Quantity | RECORD | What was assessed — CodeMeaning (e.g., "Malignancy", "Calcification", "Texture") |
Value | RECORD | The coded answer — CodeMeaning (e.g., "4 out of 5 (Moderately Suspicious for Cancer)") |
finding | RECORD | What finding was assessed — CodeMeaning (e.g., "Nodule") |
findingSite | RECORD | Anatomic site — CodeMeaning (e.g., "Lung") |
trackingIdentifier | STRING | Human-readable tracking label |
segmentationInstanceUID | STRING | SOPInstanceUID of the referenced SEG object |
segmentationSeriesUID | STRING | SeriesInstanceUID of the referenced SEG object |
| `segmentationSegmentNum |