Skip to content

RTpipeline Output Format - Quick Reference

Quick reference for the most common tasks.


πŸ“ Where to Start

All aggregated results are in: {output_dir}/_RESULTS/

_RESULTS/
β”œβ”€β”€ dvh_metrics.xlsx         ← All dose-volume metrics
β”œβ”€β”€ radiomics_ct.xlsx        ← All CT radiomic features
β”œβ”€β”€ radiomics_mr.xlsx        ← All MR radiomic features
β”œβ”€β”€ radiomics_robustness_summary.xlsx ← Cohort stability summary (if enabled)
β”œβ”€β”€ radiomics_robustness_summary_raw_values.parquet ← Raw perturbation rows
β”œβ”€β”€ case_metadata.xlsx       ← Patient/treatment info
└── qc_reports.xlsx          ← Quality control summary

πŸ”₯ Most Common Tasks

Load DVH Metrics

import pandas as pd

dvh = pd.read_excel("_RESULTS/dvh_metrics.xlsx")

# Filter by structure
bladder = dvh[dvh['Structure'].str.contains('bladder', case=False)]

# Get specific metrics
v50_values = bladder['V50Gy']  # Volume receiving β‰₯50 Gy
dmean_values = bladder['Dmean_Gy']  # Mean dose

Load Radiomics Features

radiomics = pd.read_excel("_RESULTS/radiomics_ct.xlsx")

# Get only original (most stable) features
original_features = radiomics.filter(regex='^original_')

# Get shape features
shape_features = radiomics.filter(regex='shape')

# Filter by structure
ptv = radiomics[radiomics['Structure'].str.contains('ptv', case=False)]

Load Robustness Summary

robustness = pd.read_excel(
    "_RESULTS/radiomics_robustness_summary.xlsx",
    sheet_name="robust_features"
)

# Select the exact ROI/source used by the downstream model; classifications are
# intentionally not pooled across heterogeneous structures or sources.
selected = robustness[
    (robustness["structure"] == "GTV_primary")
    & (robustness["segmentation_source"] == "Custom")
]
robust_feature_names = selected["feature_name"].tolist()

Compare Auto vs Manual Segmentation

# Load DVH
dvh = pd.read_excel("_RESULTS/dvh_metrics.xlsx")

# Separate by source
manual = dvh[dvh['ROI_Type'] == 'MANUAL']
auto = dvh[dvh['ROI_Type'] == 'TOTALSEGMENTATOR']

# Merge and compare
comparison = manual.merge(
    auto,
    on=['PatientID', 'CourseID', 'Structure'],
    suffixes=('_manual', '_auto')
)

# Volume difference
comparison['volume_diff_%'] = 100 * (
    comparison['ROI_Volume_cc_auto'] - comparison['ROI_Volume_cc_manual']
) / comparison['ROI_Volume_cc_manual']

πŸ“Š Key Column Reference

DVH Metrics (dvh_metrics.xlsx)

Column Description Example Use
Dmean_Gy Mean dose Parallel organ toxicity (lung, kidney)
Dmax_Gy Maximum dose Serial organ toxicity (spinal cord)
D2ccGy Dose received by the hottest 2 cmΒ³, in Gy, bounded by Dmin and Dmax Bladder, rectum and bowel dose reporting
D2cc_status available, roi_below_2cc, or the dose-grid coverage status when coverage is incomplete Identify computable whole-ROI values
D0.03ccGy, D0.03cc_status Hottest 0.03 cmΒ³ dose and availability status Small-volume dose reporting
D50% Median dose Homogeneity
D95% Dose to 95% of volume Target coverage
V95% Volume receiving β‰₯95% Rx PTV coverage
V50Gy Volume receiving β‰₯50 Gy OAR sparing
ROI_Volume_cc Structure volume Normalization

dose_summary_basis identifies RTSTRUCT mean and maximum as binned or unbinned_subcentigray. The latter uses supported unbinned samples from the same raster mask and working dose grid when the histogram has at most one bin or its maximum is zero. Positive doses below 0.01 Gy remain positive. Dose-volume quantiles and DVH curves remain histogram-derived. NIfTI-mask summaries already use unbinned samples and leave this field empty.

The aggregate identifies this disposition contract with aggregate_schema_version = rtpipeline-dvh-aggregate-v3. Outside-grid rows remain as nonmeasurements. All producer-defined dose-derived metrics, including D90Gy, D2ccGy and D0.03ccGy, are null; dose_metric_usable_for_dose_response is false. Volume (cmΒ³) is retained. Their status depends on the evidence published by the producer:

Published evidence Aggregate dose_metric_status and reason
Both dose_grid_coverage_status and dose_metric_status are outside_grid Preserves outside_grid and the producer's reason, normally β€œWhole-ROI metrics require complete independent dose-grid support.”
zero_dose_status is zero_dose_outside_dose_grid without that paired declaration Uses not_measurable_outside_dose_grid: β€œDose-derived metrics are null because the ROI does not intersect the selected RTDOSE grid.”

An automatic ROI containing only closed items with fewer than three distinct points is nonmeasurable_auto_sliver, with row_status = not_computed and no DVH calculation. AutoRTS requires an origin sidecar matching the RTSTRUCT file hash; selected custom model outputs record their producer source and file hash. dvh_nonmeasurement_evidence stores contour count, distinct-point counts, source and origin evidence. This class is expected nonmeasurement. Manual or unverified area-less geometry is degenerate_contours. Valid polygons retain their coverage result when stray area-less items coexist.

At exactly 2 cmΒ³, D2ccGy equals Dmin. Below 2 cmΒ³, it is empty with D2cc_status = "roi_below_2cc". Incomplete coverage leaves the whole-ROI dose empty and propagates the coverage status. Axial RTSTRUCT, rotated-grid and NIfTI-mask measurements use linear cumulative-DVH interpolation.

Radiomics Features (radiomics_ct.xlsx)

Feature Prefix Type Description
original_shape_* Geometry Volume, sphericity, elongation
original_firstorder_* Intensity Mean, std, entropy, skewness
original_glcm_* Texture Contrast, homogeneity, correlation
original_glrlm_* Texture Run length patterns
log-sigma-* Filtered Multi-scale edge detection
wavelet-* Filtered Frequency decomposition

Recommendation: Start with original_* features (most stable)


⚠️ Quality Control Quick Check

qc = pd.read_excel("_RESULTS/qc_reports.xlsx")

# Check for failures
failures = qc[qc['Overall_Status'] == 'FAIL']
print(f"{len(failures)} cases failed QC")

# Check for cropping issues
cropped = qc[qc['Structures_Fully_Cropped'].notna()]
print(f"{len(cropped)} cases have fully cropped structures")

# Check frame-of-reference issues
for_issues = qc[qc['Frame_Of_Reference_Consistent'] == False]
print(f"{len(for_issues)} cases have FOR mismatches")

🎯 Feature Selection Tips

Remove Correlated Features (r > 0.95)

import numpy as np

def remove_correlated(df, threshold=0.95):
    corr_matrix = df.corr().abs()
    upper = corr_matrix.where(
        np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)
    )
    to_drop = [col for col in upper.columns if any(upper[col] > threshold)]
    return df.drop(columns=to_drop)

# Apply
features_clean = remove_correlated(original_features)

Filter by Stability (Use Tier 1 Features)

# Tier 1 features (ICC >0.9, most reproducible)
tier1_features = [
    col for col in radiomics.columns
    if any(x in col for x in [
        'original_shape_',
        'original_firstorder_Mean',
        'original_firstorder_Median',
        'original_glcm_Energy',
        'original_glcm_Homogeneity'
    ])
]

stable_features = radiomics[tier1_features]

πŸ“¦ File Locations

For a specific patient/course:

{output_dir}/{PatientID}/{CourseID}/
β”œβ”€β”€ DICOM/CT/                    # Original DICOM
β”œβ”€β”€ NIFTI/ct.nii.gz              # CT volume
β”œβ”€β”€ Segmentation_TotalSegmentator/  # Auto-segmented organs
β”‚   β”œβ”€β”€ bladder--ct.nii.gz
β”‚   β”œβ”€β”€ rectum--ct.nii.gz
β”‚   └── [100+ more structures]
β”œβ”€β”€ RS.dcm / RS_auto(.cropped).dcm / RS_custom.dcm
β”œβ”€β”€ radiomics_ct.xlsx            # Per-course radiomics
β”œβ”€β”€ radiomics_robustness_ct.parquet  # Per-course perturbation-level robustness data
β”œβ”€β”€ dvh_metrics.xlsx             # Per-course DVH
└── qc_reports/                  # Quality control

RTSTRUCT quick guide - RS.dcm: untouched copy of the clinical RTSTRUCT imported from the TPS. - RS_auto.dcm: TotalSegmentator output on the uncropped CT grid. - RS_auto_cropped.dcm: cropped variant used when ct_cropping.use_cropped_for_* is enabled. - RS_custom.dcm: merged manual + auto + custom_structures*.yaml definitions (boolean/margin ROIs).


πŸ”§ Common Issues & Fixes

Issue: Missing radiomics features (NaN values)

Cause: ROI too small (<64 voxels) or in skip list Fix:

# Check ROI size
radiomics[['Structure', 'Volume_voxels']].sort_values('Volume_voxels')

# Check QC for cropping
qc[['PatientID', 'Structures_Fully_Cropped']]

Issue: DVH metrics don't match clinical system

Cause: Different interpolation or frame-of-reference mismatch Fix:

# Check FOR consistency
qc[['PatientID', 'Frame_Of_Reference_Consistent']]

# Compare volumes
dvh[['Structure', 'ROI_Type', 'ROI_Volume_cc']]


πŸš€ Quick Analysis Templates

Toxicity Prediction Model

import pandas as pd
from sklearn.ensemble import RandomForestClassifier

# Load data
dvh = pd.read_excel("_RESULTS/dvh_metrics.xlsx")

# Filter organ
rectum = dvh[dvh['Structure'].str.contains('rectum', case=False)]

# Features
X = rectum[['Dmean_Gy', 'D2ccGy', 'V50Gy', 'V60Gy']]

# Train (assuming you have outcome labels)
# y = outcomes['Grade2+_Toxicity']
# clf = RandomForestClassifier()
# clf.fit(X, y)

Radiomics Outcome Prediction

# Load radiomics
rad = pd.read_excel("_RESULTS/radiomics_ct.xlsx")

# Filter to target structures
ptv = rad[rad['Structure'].str.contains('ptv', case=False)]

# Original features only (most stable)
features = ptv.filter(regex='^original_')

# Remove high correlation
features_clean = remove_correlated(features, threshold=0.95)

# Standardize and train model
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X = scaler.fit_transform(features_clean)

Cohort Description (Table 1)

metadata = pd.read_excel("_RESULTS/case_metadata.xlsx")

print("Cohort Summary:")
print(f"Patients: {metadata['PatientID'].nunique()}")
print(f"Courses: {len(metadata)}")
print(f"\nSex distribution:\n{metadata['PatientSex'].value_counts()}")
print(f"\nMean prescription: {metadata['PrescriptionDose_Gy'].mean():.1f} Gy")
print(f"Mean fractions: {metadata['NumberOfFractions'].mean():.1f}")

πŸ“š Resources

  • Setup Script: ./setup_docker_project.sh
  • PyRadiomics Docs: https://pyradiomics.readthedocs.io/
  • IBSI Standards: https://theibsi.github.io/
  • Pipeline Overview: Home

πŸ†˜ Need Help?

  1. Check QC reports first: _RESULTS/qc_reports.xlsx
  2. Check logs: {logs_dir}/
  3. Review technical documentation: Architecture

Document Version: 1.0 Compatible with: rtpipeline v2.1.1+ Last Updated: 2025-11-13