5  AE Specific Analysis

Objectives

This guide demonstrates AE specific analysis showing individual adverse event terms. For design philosophy and architecture details, see ae_summary.qmd.

5.1 Setup

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.")
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_specific

5.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'
shape: (6, 5)
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
shape: (936, 3)
__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
shape: (234, 5)
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