13  Disposition Analysis

Objectives

This guide demonstrates disposition analysis showing subject enrollment and study completion status. For design philosophy and architecture details, see ae_summary.qmd.

13.1 Setup

import sys
from pathlib import Path
import polars as pl

sys.path.insert(0, 'src')

from rtflite import LibreOfficeConverter
try:
    converter = LibreOfficeConverter()
except Exception:
    converter = None
    print("WARNING: LibreOffice not found. PDF conversion will be skipped.")
from csrlite import load_plan, study_plan_to_disposition_summary
from csrlite.disposition.disposition import disposition_ard, disposition_df, disposition_rtf, disposition

13.2 What’s Different?

Disposition Analysis shows subject enrollment status, study completion, and discontinuation reasons in a hierarchical table format with indented subcategories.

13.3 StudyPlan-Driven Workflow

study_plan = load_plan("studies/xyz123/yaml/plan_xyz123.yaml")
study_plan.get_plan_df().filter(pl.col("analysis") == "disposition_summary")
2026-02-03 15:27:39,408 - csrlite.common.plan - INFO - Successfully loaded dataset 'adsl' from 'studies/xyz123/yaml/../../../data/adsl.parquet'
2026-02-03 15:27:39,411 - csrlite.common.plan - INFO - Successfully loaded dataset 'adae' from 'studies/xyz123/yaml/../../../data/adae.parquet'
2026-02-03 15:27:39,412 - csrlite.common.plan - INFO - Successfully loaded dataset 'adie' from 'studies/xyz123/yaml/../../../data/adie.parquet'
2026-02-03 15:27:39,413 - csrlite.common.plan - INFO - Successfully loaded dataset 'adpd' from 'studies/xyz123/yaml/../../../data/adpd.parquet'
shape: (1, 5)
analysis population observation parameter group
str str str str str
"disposition_summary" "enrolled" null null "trt01a"
output_files = study_plan_to_disposition_summary(study_plan)
studies/xyz123/rtf/disposition_summary_enrolled_trt01a.rtf

13.3.1 Disposition Summary

13.4 Complete Pipeline

adsl = pl.read_parquet("data/adsl.parquet")

disposition(
    population=adsl,
    population_filter=None,
    id=("USUBJID", "Subject ID"),
    group=("TRT01A", "Treatment Group"),
    ds_term=("EOSSTT", "Disposition Status"),
    dist_reason_term=("DCSREAS", "Discontinued Reason"),
    title=[
        "Disposition Summary Table",
        "(Intent-to-Treat Population)"
    ],
    footnote=["Percentages are based on the number of enrolled participants."],
    source=None,
    output_file="studies/xyz123/rtf/disposition.rtf",
    total=True,
    missing_group="error"
)
studies/xyz123/rtf/disposition.rtf
'studies/xyz123/rtf/disposition.rtf'

13.5 Step-by-Step Pipeline

13.5.1 Step 1: Generate ARD

Key Parameters: - ds_term: Tuple (column, label) for status (Completed, Ongoing, Discontinued) - dist_reason_term: Tuple (column, label) for discontinuation reason

_ard = disposition_ard(
    population=adsl,
    population_filter=None,
    id=("USUBJID", "Subject ID"),
    group=("TRT01A", "Treatment Group"),
    ds_term=("EOSSTT", "Disposition Status"),
    dist_reason_term=("DCSREAS", "Discontinued Reason"),
    total=True,
    missing_group="error"
)

_ard
shape: (48, 3)
__index__ __group__ __value__
str str str
"Enrolled" "Placebo" "86"
"Enrolled" "Xanomeline High Dose" "84"
"Enrolled" "Xanomeline Low Dose" "84"
"Enrolled" "Total" "254"
"Completed" "Placebo" " 58 ( 67.4)"
"    Lost to Follow-up" "Total" "  2 (  0.8)"
"    Physician Decision" "Total" "  3 (  1.2)"
"    Protocol Violation" "Total" "  3 (  1.2)"
"    Sponsor Decision" "Total" "  7 (  2.8)"
"    Withdrew Consent" "Total" " 27 ( 10.6)"

Output Structure: Long format with __index__, __group__, __value__ columns.

13.5.2 Step 2: Transform to Display Format

_df = disposition_df(_ard)
_df
shape: (12, 5)
Term Placebo Xanomeline High Dose Xanomeline Low Dose Total
str str str str str
"Enrolled" "86" "84" "84" "254"
"Completed" " 58 ( 67.4)" " 27 ( 32.1)" " 25 ( 29.8)" "110 ( 43.3)"
"Discontinued" " 28 ( 32.6)" " 57 ( 67.9)" " 59 ( 70.2)" "144 ( 56.7)"
"    Adverse Event" "  8 (  9.3)" " 40 ( 47.6)" " 44 ( 52.4)" " 92 ( 36.2)"
"    Death" "  2 (  2.3)" "  0 (  0.0)" "  1 (  1.2)" "  3 (  1.2)"
"    Lost to Follow-up" "  1 (  1.2)" "  0 (  0.0)" "  1 (  1.2)" "  2 (  0.8)"
"    Physician Decision" "  1 (  1.2)" "  2 (  2.4)" "  0 (  0.0)" "  3 (  1.2)"
"    Protocol Violation" "  1 (  1.2)" "  1 (  1.2)" "  1 (  1.2)" "  3 (  1.2)"
"    Sponsor Decision" "  2 (  2.3)" "  3 (  3.6)" "  2 (  2.4)" "  7 (  2.8)"
"    Withdrew Consent" "  9 ( 10.5)" "  8 (  9.5)" " 10 ( 11.9)" " 27 ( 10.6)"

Output Structure: Wide format with “Disposition Status” column showing statuses and treatment groups as columns.

13.5.3 Step 3: Generate RTF Output

disposition_rtf(
    _df,
    title=[
        "Disposition Summary Table",
        "(Intent-to-Treat Population)"
    ],
    footnote=["Percentages are based on the number of enrolled participants."],
    source=None,
    col_rel_width=[2.5, 1, 1, 1, 1]
).write_rtf("studies/xyz123/rtf/disposition_step.rtf")
studies/xyz123/rtf/disposition_step.rtf