import os
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.")5 AE Specific Analysis
This guide demonstrates AE specific analysis showing individual adverse event terms. For design philosophy and architecture details, see ae_summary.qmd.
5.1 Setup
from csrlite import load_plan, study_plan_to_ae_specific
from csrlite.ae.ae_specific import ae_specific_ard, ae_specific_df, ae_specific_rtf, ae_specific5.2 What’s Different?
AE Summary shows high-level event categories (e.g., “Any AE”, “Serious AE”).
AE Specific shows detailed listings of individual adverse event terms (e.g., “APPLICATION SITE ERYTHEMA”, “DIARRHOEA”).
5.3 StudyPlan-Driven Workflow
study_plan = load_plan("studies/xyz123/yaml/plan_xyz123.yaml")
study_plan.get_plan_df().filter(pl.col("analysis") == "ae_specific")2026-02-03 15:26:05,109 - csrlite.common.plan - INFO - Successfully loaded dataset 'adsl' from 'studies/xyz123/yaml/../../../data/adsl.parquet'
2026-02-03 15:26:05,113 - csrlite.common.plan - INFO - Successfully loaded dataset 'adae' from 'studies/xyz123/yaml/../../../data/adae.parquet'
2026-02-03 15:26:05,115 - csrlite.common.plan - INFO - Successfully loaded dataset 'adie' from 'studies/xyz123/yaml/../../../data/adie.parquet'
2026-02-03 15:26:05,116 - csrlite.common.plan - INFO - Successfully loaded dataset 'adpd' from 'studies/xyz123/yaml/../../../data/adpd.parquet'
| analysis | population | observation | parameter | group |
|---|---|---|---|---|
| str | str | str | str | str |
| "ae_specific" | "apat" | "week12" | "any" | "trt01a" |
| "ae_specific" | "apat" | "week12" | "rel" | "trt01a" |
| "ae_specific" | "apat" | "week12" | "ser" | "trt01a" |
| "ae_specific" | "apat" | "week24" | "any" | "trt01a" |
| "ae_specific" | "apat" | "week24" | "rel" | "trt01a" |
| "ae_specific" | "apat" | "week24" | "ser" | "trt01a" |
output_files = study_plan_to_ae_specific(study_plan)studies/xyz123/rtf/ae_specific_apat_week12_any.rtf
studies/xyz123/rtf/ae_specific_apat_week12_rel.rtf
studies/xyz123/rtf/ae_specific_apat_week12_ser.rtf
studies/xyz123/rtf/ae_specific_apat_week24_any.rtf
studies/xyz123/rtf/ae_specific_apat_week24_rel.rtf
studies/xyz123/rtf/ae_specific_apat_week24_ser.rtf
5.3.1 week12_any
5.3.2 week12_rel
5.3.3 week12_ser
5.3.4 week24_any
5.3.5 week24_rel
5.3.6 week24_ser
5.4 Complete Pipeline
adsl = pl.read_parquet("data/adsl.parquet")
adae = pl.read_parquet("data/adae.parquet")
ae_specific(
population=adsl,
observation=adae,
population_filter="SAFFL = 'Y'",
observation_filter="TRTEMFL = 'Y'",
parameter_filter=None,
id=("USUBJID", "Subject ID"),
group=("TRT01A", "Treatment Group"),
ae_term=("AEDECOD", "Adverse Event"),
title=[
"Analysis of Specific Adverse Events",
"(Treatment-Emergent, Safety Population)"
],
footnote=[
"Every participant is counted a single time for each applicable row and column."
],
source=["Source: ADSL and ADAE datasets"],
output_file="studies/xyz123/rtf/ae_specific.rtf",
total=True,
missing_group="error"
)studies/xyz123/rtf/ae_specific.rtf
'studies/xyz123/rtf/ae_specific.rtf'
5.5 Step-by-Step Pipeline
5.5.1 Step 1: Generate ARD
Key Parameters: - ae_term: Tuple (variable_name, label) for AE term column (default: (“AEDECOD”, “Adverse Event”)) - parameter_filter: Optional SQL WHERE clause for filtering AEs (e.g., “AESER = ‘Y’” for serious AEs, “AEREL in [‘RELATED’, ‘PROBABLY RELATED’]” for related AEs)
_ard = ae_specific_ard(
population=adsl,
observation=adae,
population_filter="SAFFL = 'Y'",
observation_filter="TRTEMFL = 'Y'",
parameter_filter=None,
id=("USUBJID", "Subject ID"),
group=("TRT01A", "Treatment Group"),
ae_term=("AEDECOD", "Adverse Event"),
total=True,
missing_group="error"
)
_ard| __index__ | __group__ | __value__ |
|---|---|---|
| enum | enum | str |
| "Participants in population" | "Placebo" | "86" |
| "Participants in population" | "Xanomeline High Dose" | "84" |
| "Participants in population" | "Xanomeline Low Dose" | "84" |
| "Participants in population" | "Total" | "254" |
| " with one or more adverse e… | "Placebo" | " 65 ( 75.6)" |
| … | … | … |
| "Wound" | "Total" | " 1 ( 0.4)" |
| "Wound haemorrhage" | "Placebo" | " 0 ( 0.0)" |
| "Wound haemorrhage" | "Xanomeline High Dose" | " 1 ( 1.2)" |
| "Wound haemorrhage" | "Xanomeline Low Dose" | " 0 ( 0.0)" |
| "Wound haemorrhage" | "Total" | " 1 ( 0.4)" |
Output Structure: Long format with __index__, __group__, __value__ columns.
5.5.2 Step 2: Transform to Display Format
_df = ae_specific_df(_ard)
_df| Term | Placebo | Xanomeline High Dose | Xanomeline Low Dose | Total |
|---|---|---|---|---|
| enum | str | str | str | str |
| "Participants in population" | "86" | "84" | "84" | "254" |
| " with one or more adverse e… | " 65 ( 75.6)" | " 76 ( 90.5)" | " 77 ( 91.7)" | "218 ( 85.8)" |
| " with no adverse events" | " 21 ( 24.4)" | " 8 ( 9.5)" | " 7 ( 8.3)" | " 36 ( 14.2)" |
| "" | "" | "" | "" | "" |
| "Abdominal discomfort" | " 0 ( 0.0)" | " 1 ( 1.2)" | " 0 ( 0.0)" | " 1 ( 0.4)" |
| … | … | … | … | … |
| "Vomiting" | " 3 ( 3.5)" | " 7 ( 8.3)" | " 3 ( 3.6)" | "13 ( 5.1)" |
| "Weight decreased" | " 0 ( 0.0)" | " 1 ( 1.2)" | " 0 ( 0.0)" | " 1 ( 0.4)" |
| "Wolff-parkinson-white syndrome" | " 0 ( 0.0)" | " 0 ( 0.0)" | " 1 ( 1.2)" | " 1 ( 0.4)" |
| "Wound" | " 0 ( 0.0)" | " 0 ( 0.0)" | " 1 ( 1.2)" | " 1 ( 0.4)" |
| "Wound haemorrhage" | " 0 ( 0.0)" | " 1 ( 1.2)" | " 0 ( 0.0)" | " 1 ( 0.4)" |
Output Structure: Wide format with “Term” column showing AE terms and treatment groups as columns.
5.5.3 Step 3: Generate RTF Output
ae_specific_rtf(
_df,
title=[
"Analysis of Specific Adverse Events",
"(Treatment-Emergent, Safety Population)"
],
footnote=[
"Every participant is counted a single time for each applicable row and column."
],
source=["Source: ADSL and ADAE datasets"],
col_rel_width=[4, 2, 2, 2, 2]
).write_rtf("studies/xyz123/rtf/ae_specific_step.rtf")studies/xyz123/rtf/ae_specific_step.rtf