Skip to content

AE summary

This example shows how to create a simplified adverse events summary table.

Imports

from importlib.resources import files

import polars as pl

import rtflite as rtf

Create data for RTF table

Load adverse events data from parquet file:

data_path = files("rtflite.data").joinpath("adae.parquet")
df = pl.read_parquet(data_path)

Process the data to create summary statistics:

# First, get the total number of subjects per treatment group
subjects_per_trt = df.group_by("TRTA").agg(pl.col("USUBJID").n_unique().alias("n_subj"))

# Count subjects with each AE by treatment group and calculate percentages
ae_t1 = (
    df.group_by(["TRTA", "AEDECOD"])
    .agg(pl.col("USUBJID").n_unique().alias("n_ae"))
    .join(subjects_per_trt, on="TRTA")
    .with_columns((pl.col("n_ae") / pl.col("n_subj") * 100).round(2).alias("pct"))
    # Only show AE terms with at least 5 subjects in one treatment group
    .filter(pl.col("n_ae") > 5)
)

# Pivot the data to create wide format with n and pct for each treatment
# First, melt the n_ae and pct columns
ae_long = ae_t1.select(["TRTA", "AEDECOD", "n_ae", "pct"]).unpivot(
    index=["TRTA", "AEDECOD"],
    on=["n_ae", "pct"],
    variable_name="var",
    value_name="value",
)

# Create combined column names for pivoting
ae_long = ae_long.with_columns((pl.col("TRTA") + "_" + pl.col("var")).alias("temp"))

# Pivot to wide format
ae_wide = ae_long.pivot(values="value", index="AEDECOD", on="temp").fill_null(0)

# Ensure columns are in the correct order
col_order = [
    "AEDECOD",
    "Placebo_n_ae",
    "Placebo_pct",
    "Xanomeline High Dose_n_ae",
    "Xanomeline High Dose_pct",
    "Xanomeline Low Dose_n_ae",
    "Xanomeline Low Dose_pct",
]

ae_t1_final = (
    ae_wide.select(col_order)
    .with_columns(pl.col(pl.Float64).cast(pl.String))
    .sort("AEDECOD")
)

print(ae_t1_final.head(10))
shape: (10, 7)
┌──────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┬─────────────┐
│ AEDECOD      ┆ Placebo_n_a ┆ Placebo_pct ┆ Xanomeline  ┆ Xanomeline  ┆ Xanomeline  ┆ Xanomeline  │
│ ---          ┆ e           ┆ ---         ┆ High        ┆ High        ┆ Low         ┆ Low         │
│ str          ┆ ---         ┆ str         ┆ Dose_n_ae   ┆ Dose_pct    ┆ Dose_n_ae   ┆ Dose_pct    │
│              ┆ str         ┆             ┆ ---         ┆ ---         ┆ ---         ┆ ---         │
│              ┆             ┆             ┆ str         ┆ str         ┆ str         ┆ str         │
╞══════════════╪═════════════╪═════════════╪═════════════╪═════════════╪═════════════╪═════════════╡
│ APPLICATION  ┆ 0.0         ┆ 0.0         ┆ 7.0         ┆ 8.86        ┆ 9.0         ┆ 11.69       │
│ SITE         ┆             ┆             ┆             ┆             ┆             ┆             │
│ DERMATITIS   ┆             ┆             ┆             ┆             ┆             ┆             │
│ APPLICATION  ┆ 0.0         ┆ 0.0         ┆ 15.0        ┆ 18.99       ┆ 12.0        ┆ 15.58       │
│ SITE         ┆             ┆             ┆             ┆             ┆             ┆             │
│ ERYTHEMA     ┆             ┆             ┆             ┆             ┆             ┆             │
│ APPLICATION  ┆ 0.0         ┆ 0.0         ┆ 9.0         ┆ 11.39       ┆ 9.0         ┆ 11.69       │
│ SITE         ┆             ┆             ┆             ┆             ┆             ┆             │
│ IRRITATION   ┆             ┆             ┆             ┆             ┆             ┆             │
│ APPLICATION  ┆ 6.0         ┆ 8.7         ┆ 22.0        ┆ 27.85       ┆ 22.0        ┆ 28.57       │
│ SITE         ┆             ┆             ┆             ┆             ┆             ┆             │
│ PRURITUS     ┆             ┆             ┆             ┆             ┆             ┆             │
│ APPLICATION  ┆ 0.0         ┆ 0.0         ┆ 6.0         ┆ 7.59        ┆ 0.0         ┆ 0.0         │
│ SITE         ┆             ┆             ┆             ┆             ┆             ┆             │
│ VESICLES     ┆             ┆             ┆             ┆             ┆             ┆             │
│ COUGH        ┆ 0.0         ┆ 0.0         ┆ 0.0         ┆ 0.0         ┆ 6.0         ┆ 7.79        │
│ DIARRHOEA    ┆ 9.0         ┆ 13.04       ┆ 0.0         ┆ 0.0         ┆ 0.0         ┆ 0.0         │
│ DIZZINESS    ┆ 0.0         ┆ 0.0         ┆ 12.0        ┆ 15.19       ┆ 8.0         ┆ 10.39       │
│ ERYTHEMA     ┆ 9.0         ┆ 13.04       ┆ 14.0        ┆ 17.72       ┆ 15.0        ┆ 19.48       │
│ HEADACHE     ┆ 7.0         ┆ 10.14       ┆ 6.0         ┆ 7.59        ┆ 0.0         ┆ 0.0         │
└──────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘

Define table format

Prepare the data for RTF output:

# Define column headers
header1 = [" ", "Placebo", "Drug High Dose", "Drug Low Dose"]
header2 = [" ", "n", "(%)", "n", "(%)", "n", "(%)"]

# Create RTF components
col_header1 = rtf.RTFColumnHeader(
    text=header1, col_rel_width=[4, 2, 2, 2], text_justification=["l", "c", "c", "c"]
)

col_header2 = rtf.RTFColumnHeader(
    text=header2,
    col_rel_width=[4] + [1] * 6,
    text_justification=["l", "c", "c", "c", "c", "c", "c"],
    border_top=[""] + ["single"] * 6,
    border_left=["single"] + ["single", ""] * 3,
)

# Create table body
tbl_body = rtf.RTFBody(
    col_rel_width=[4] + [1] * 6,
    text_justification=["l"] + ["c"] * 6,
    border_left=["single"] + ["single", ""] * 3,
)

Create the RTF document with formatting:

# Create RTF document
doc = rtf.RTFDocument(
    df=ae_t1_final,
    rtf_title=rtf.RTFTitle(
        text=[
            "Analysis of Subjects With Specific Adverse Events",
            "(Incidence > 5 Subjects in One or More Treatment Groups)",
            "ASaT",
        ]
    ),
    rtf_column_header=[col_header1, col_header2],
    rtf_body=tbl_body,
    rtf_footnote=rtf.RTFFootnote(
        text=["{^\\dagger}This is footnote 1", "This is footnote 2"],
        text_convert=[[True]],  # Enable LaTeX symbol conversion
    ),
    rtf_source=rtf.RTFSource(text=["Source: xxx"]),
)

# Output .rtf file
doc.write_rtf("example-ae-summary.rtf")