library(tidyverse)
library(gtsummary)
library(janitor)
library(skimr)

Introduction

This document contains a reproducible analysis of a simulated acute appendicitis cohort (n = 300). It was developed as a learning exercise in R/tidyverse for surgical outcomes research. Variables were designed to reflect a realistic acute care surgery dataset including patient demographics, operative characteristics, and postoperative outcomes.

set.seed(42)
n <- 300

appendicitis <- tibble(
  patient_id         = 1:n,
  age                = round(rnorm(n, mean = 38, sd = 15)) |> pmax(18) |> pmin(89),
  sex                = sample(c("Male", "Female"), n, replace = TRUE),
  bmi                = round(rnorm(n, mean = 28, sd = 6), 1) |> pmax(17) |> pmin(55),
  asa_class          = sample(c("I", "II", "III", "IV"), n, replace = TRUE,
                               prob = c(0.20, 0.45, 0.30, 0.05)),
  wbc                = round(rnorm(n, mean = 13.5, sd = 3.5), 1) |> pmax(4) |> pmin(30),
  ct_performed       = sample(c(TRUE, FALSE), n, replace = TRUE, prob = c(0.85, 0.15)),
  perforation        = sample(c(TRUE, FALSE), n, replace = TRUE, prob = c(0.25, 0.75)),
  operative_approach = sample(c("Laparoscopic", "Open", "Converted"), n, replace = TRUE,
                               prob = c(0.78, 0.12, 0.10)),
  op_time_min        = round(rnorm(n, mean = 68, sd = 22)) |> pmax(25) |> pmin(180),
  ssi                = sample(c(TRUE, FALSE), n, replace = TRUE, prob = c(0.08, 0.92)),
  los_days           = round(rnorm(n, mean = 2.1, sd = 1.4), 1) |> pmax(0.5) |> pmin(14),
  readmission_30d    = sample(c(TRUE, FALSE), n, replace = TRUE, prob = c(0.06, 0.94))
) |>
  mutate(
    sex                = factor(sex, levels = c("Male", "Female")),
    asa_class          = factor(asa_class, levels = c("I", "II", "III", "IV"), 
                                ordered = TRUE),
    operative_approach = factor(operative_approach,
                                levels = c("Laparoscopic", "Open", "Converted"))
  )

Data Preparation

Four derived variables were created from the raw dataset to support downstream analysis.

appendicitis <- appendicitis |>
  mutate(
    # Binary integer for modeling
    ssi_int      = as.integer(ssi),

    # Leukocytosis threshold at 11.0 x10³/µL
    leukocytosis = factor(
                     case_when(
                       wbc >= 11.0 ~ "Elevated",
                       wbc <  11.0 ~ "Normal"),
                     levels = c("Normal", "Elevated")),

    # WHO BMI classification
    bmi_class    = factor(
                     case_when(
                       bmi < 18.5             ~ "Underweight",
                       bmi >= 18.5 & bmi < 25 ~ "Normal",
                       bmi >= 25  & bmi < 30  ~ "Overweight",
                       bmi >= 30              ~ "Obese"),
                     levels = c("Underweight", "Normal", 
                                "Overweight", "Obese"),
                     ordered = TRUE),

    # High risk composite: perforated AND leukocytosis
    high_risk    = perforation == TRUE & wbc >= 11.0
  )

Exploratory Data Analysis

Initial dataset structure and distributional characteristics were examined prior to formal analysis.

glimpse(appendicitis)
## Rows: 300
## Columns: 17
## $ patient_id         <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ age                <dbl> 59, 30, 43, 47, 44, 36, 61, 37, 68, 37, 58, 72, 18,…
## $ sex                <fct> Female, Female, Female, Female, Female, Male, Femal…
## $ bmi                <dbl> 20.2, 26.5, 29.0, 25.6, 28.6, 26.1, 37.7, 32.3, 45.…
## $ asa_class          <ord> II, IV, II, I, II, III, III, II, II, II, III, II, I…
## $ wbc                <dbl> 16.8, 12.6, 13.8, 12.0, 21.1, 4.0, 13.8, 13.9, 14.2…
## $ ct_performed       <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TR…
## $ perforation        <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALS…
## $ operative_approach <fct> Laparoscopic, Converted, Laparoscopic, Laparoscopic…
## $ op_time_min        <dbl> 70, 34, 64, 59, 79, 56, 79, 80, 62, 67, 120, 102, 1…
## $ ssi                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ los_days           <dbl> 1.3, 3.4, 2.0, 0.8, 1.2, 1.3, 2.4, 2.9, 1.5, 1.6, 2…
## $ readmission_30d    <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ ssi_int            <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ leukocytosis       <fct> Elevated, Elevated, Elevated, Elevated, Elevated, N…
## $ bmi_class          <ord> Normal, Overweight, Overweight, Overweight, Overwei…
## $ high_risk          <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FAL…
skim(appendicitis)
Data summary
Name appendicitis
Number of rows 300
Number of columns 17
_______________________
Column type frequency:
factor 5
logical 5
numeric 7
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
sex 0 1 FALSE 2 Fem: 152, Mal: 148
asa_class 0 1 TRUE 4 II: 140, III: 86, I: 55, IV: 19
operative_approach 0 1 FALSE 3 Lap: 235, Ope: 37, Con: 28
leukocytosis 0 1 FALSE 2 Ele: 224, Nor: 76
bmi_class 0 1 TRUE 4 Obe: 104, Ove: 100, Nor: 77, Und: 19

Variable type: logical

skim_variable n_missing complete_rate mean count
ct_performed 0 1 0.83 TRU: 249, FAL: 51
perforation 0 1 0.25 FAL: 224, TRU: 76
ssi 0 1 0.07 FAL: 278, TRU: 22
readmission_30d 0 1 0.07 FAL: 279, TRU: 21
high_risk 0 1 0.19 FAL: 243, TRU: 57

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
patient_id 0 1 150.50 86.75 1.0 75.75 150.5 225.25 300.0 ▇▇▇▇▇
age 0 1 38.37 13.60 18.0 28.00 38.0 48.00 79.0 ▇▇▅▂▁
bmi 0 1 27.66 5.83 17.0 24.00 27.6 31.83 47.4 ▅▇▆▂▁
wbc 0 1 13.51 3.68 4.0 10.85 13.8 15.90 25.7 ▂▆▇▃▁
op_time_min 0 1 68.17 22.24 25.0 50.75 67.0 85.00 147.0 ▅▇▆▂▁
los_days 0 1 2.22 1.28 0.5 1.20 2.1 3.00 7.0 ▇▆▃▁▁
ssi_int 0 1 0.07 0.26 0.0 0.00 0.0 0.00 1.0 ▇▁▁▁▁

Stratified Descriptive Statistics

Length of Stay by Operative Approach

Length of stay was non-normally distributed and is reported as median with interquartile range.

appendicitis |>
  group_by(operative_approach) |>
  summarise(
    n          = n(),
    median_los = round(median(los_days), 2),
    q25        = round(quantile(los_days, 0.25), 2),
    q75        = round(quantile(los_days, 0.75), 2)
  )
## # A tibble: 3 × 5
##   operative_approach     n median_los   q25   q75
##   <fct>              <int>      <dbl> <dbl> <dbl>
## 1 Laparoscopic         235       2.1    1.2  3.05
## 2 Open                  37       2.2    1.4  2.9 
## 3 Converted             28       2.05   1.1  3.02

SSI by Perforation Status - the proportion of patients developing SSI was examined. I stratified the dataset by perforation status. Results are expressed as proportions (events/N) rather rates, as no time in denominator was captured.

appendicitis |>
  group_by(perforation) |>
  summarise(
    n         = n(),
    ssi_n     = sum(ssi_int),
    ssi_prop  = round(mean(ssi_int) * 100, 1)
  )
## # A tibble: 2 × 4
##   perforation     n ssi_n ssi_prop
##   <lgl>       <int> <int>    <dbl>
## 1 FALSE         224    16      7.1
## 2 TRUE           76     6      7.9

Multiple Outcomes by Operative Approach

appendicitis |>
  summarise(
    n = n(),
    ssi_prop = round(mean(ssi_int) * 100, 1),
    readmit_prop = round(mean(readmission_30d)*100, 1),
    mean_op_time = round(mean(op_time_min),1),
    perf_prop = round(mean(perforation)*100, 1)
  )
## # A tibble: 1 × 5
##       n ssi_prop readmit_prop mean_op_time perf_prop
##   <int>    <dbl>        <dbl>        <dbl>     <dbl>
## 1   300      7.3            7         68.2      25.3

SSI Prop by Perforation Status and Operative Approach

appendicitis |>
  group_by(perforation, operative_approach) |>
  summarize(
    n        = n(),
    ssi_prop = round(mean(ssi_int) * 100, 1),
    .groups  = "drop"
  )
## # A tibble: 6 × 4
##   perforation operative_approach     n ssi_prop
##   <lgl>       <fct>              <int>    <dbl>
## 1 FALSE       Laparoscopic         177      6.8
## 2 FALSE       Open                  26     11.5
## 3 FALSE       Converted             21      4.8
## 4 TRUE        Laparoscopic          58     10.3
## 5 TRUE        Open                  11      0  
## 6 TRUE        Converted              7      0

###Module 4 Exercise Plan ###Exercise 4.1 — pivot_longer(): Reshape all binary complication variables into long format ###Exercise 4.2 — group_by() + summarize() on long data: Compute complication proportions across all outcomes simultaneously — a powerful pattern for complication profile tables ###Exercise 4.3 — pivot_wider(): Reshape back to wide format — confirming round-trip integrity ###Exercise 4.4 — Applied clinical task: Generate a complication profile stratified by operative approach using the long format

##Exercise 4.1 - piovt_longer()

Data Reshaping

Wide to Long Format

Binary complication variables were reshaped to long format to facilitate simultaneous computation of complication proportions across all outcomes.

# Identify our binary complication variables
complications_long <- appendicitis |>
  select(patient_id, operative_approach, perforation,
         ssi, readmission_30d, ct_performed) |>
  pivot_longer(
    cols      = c(ssi, readmission_30d, ct_performed),
    names_to  = "complication",
    values_to = "value"
  )

# Inspect the result
glimpse(complications_long)
## Rows: 900
## Columns: 5
## $ patient_id         <int> 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, …
## $ operative_approach <fct> Laparoscopic, Laparoscopic, Laparoscopic, Converted…
## $ perforation        <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ complication       <chr> "ssi", "readmission_30d", "ct_performed", "ssi", "r…
## $ value              <lgl> FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALS…
head(complications_long, 12)
## # A tibble: 12 × 5
##    patient_id operative_approach perforation complication    value
##         <int> <fct>              <lgl>       <chr>           <lgl>
##  1          1 Laparoscopic       FALSE       ssi             FALSE
##  2          1 Laparoscopic       FALSE       readmission_30d FALSE
##  3          1 Laparoscopic       FALSE       ct_performed    TRUE 
##  4          2 Converted          FALSE       ssi             FALSE
##  5          2 Converted          FALSE       readmission_30d FALSE
##  6          2 Converted          FALSE       ct_performed    TRUE 
##  7          3 Laparoscopic       FALSE       ssi             FALSE
##  8          3 Laparoscopic       FALSE       readmission_30d FALSE
##  9          3 Laparoscopic       FALSE       ct_performed    TRUE 
## 10          4 Laparoscopic       FALSE       ssi             FALSE
## 11          4 Laparoscopic       FALSE       readmission_30d FALSE
## 12          4 Laparoscopic       FALSE       ct_performed    TRUE

Exercise 4.2 — Summarize Across All Complications Simultaneously

####This is where long format becomes powerful. Instead of writing a separate summarize() call for each complication, you compute all proportions in one pipeline:

complications_long |>
  filter(complication %in% c("ssi", "readmission_30d")) |>
  mutate(complication = factor(complication,
                               levels = c("ssi", "readmission_30d"),
                               labels = c("SSI", "30-day Readmission"))) |>
  group_by(complication) |>
  summarise(
    n          = n(),
    events     = sum(value),
    prop       = round(mean(value) * 100, 1)
  )
## # A tibble: 2 × 4
##   complication           n events  prop
##   <fct>              <int>  <int> <dbl>
## 1 SSI                  300     22   7.3
## 2 30-day Readmission   300     21   7

###Exercise 4.3 — Complication Proportion Stratified by Operative Approach ####Same pattern, add operative_approach to group_by():

complications_long |>
  filter(complication %in% c("ssi", "readmission_30d")) |>
  mutate(complication = factor(complication,
                               levels = c("ssi", "readmission_30d"),
                               labels = c("SSI", "30-day Readmission"))) |>
  group_by(operative_approach, complication) |>
  summarise(
    n      = n(),
    events = sum(value),
    prop_percent   = round(mean(value) * 100, 1),
    .groups = "drop"
  )
## # A tibble: 6 × 5
##   operative_approach complication           n events prop_percent
##   <fct>              <fct>              <int>  <int>        <dbl>
## 1 Laparoscopic       SSI                  235     18          7.7
## 2 Laparoscopic       30-day Readmission   235     14          6  
## 3 Open               SSI                   37      3          8.1
## 4 Open               30-day Readmission    37      4         10.8
## 5 Converted          SSI                   28      1          3.6
## 6 Converted          30-day Readmission    28      3         10.7

Wilson CI for converted SSI proportion

prop.test(x = 1, n = 28, conf.level = 0.95)

Converted cases - n = 28, 1 event (3.6%)

prop.test(x = 1, n = 28, conf.level = 0.95)

Hypothetical larger converted cohort - n = 100, ~4 events (4%)

prop.test(x = 4, n = 100, conf.level = 0.95)

Hypothetical NSQIP-scale cohort - n = 1000, ~36 events (3.6%)

prop.test(x = 36, n = 1000, conf.level = 0.95)

prop.test(x = 1, n = 28, p = 0.08, conf.level = 0.95)

###Adding CI Reporting to Your R Markdown Document

# CI width narrows as N increases - same proportion
prop.test(x = 1,  n = 28,   conf.level = 0.95)  # n=28
## 
##  1-sample proportions test with continuity correction
## 
## data:  1 out of 28, null probability 0.5
## X-squared = 22.321, df = 1, p-value = 2.306e-06
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.001867118 0.202390833
## sample estimates:
##          p 
## 0.03571429
prop.test(x = 4,  n = 100,  conf.level = 0.95)  # n=100
## 
##  1-sample proportions test with continuity correction
## 
## data:  4 out of 100, null probability 0.5
## X-squared = 82.81, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.01289087 0.10511152
## sample estimates:
##    p 
## 0.04
prop.test(x = 36, n = 1000, conf.level = 0.95)  # n=1000
## 
##  1-sample proportions test with continuity correction
## 
## data:  36 out of 1000, null probability 0.5
## X-squared = 859.33, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.02569362 0.05000931
## sample estimates:
##     p 
## 0.036
# Clinically meaningful null - does converted SSI differ from overall cohort?
prop.test(x = 1, n = 28, p = 0.08, conf.level = 0.95)
## Warning in prop.test(x = 1, n = 28, p = 0.08, conf.level = 0.95): Chi-squared
## approximation may be incorrect
## 
##  1-sample proportions test with continuity correction
## 
## data:  1 out of 28, null probability 0.08
## X-squared = 0.26572, df = 1, p-value = 0.6062
## alternative hypothesis: true p is not equal to 0.08
## 95 percent confidence interval:
##  0.001867118 0.202390833
## sample estimates:
##          p 
## 0.03571429

Framework 1 — The Clinically Meaningful Difference Threshold

The most defensible approach is context-dependent. Ask: “Does the CI span a clinically meaningful difference?”If your point estimate is 3.6% SSI and the CI runs 0.6% – 17.7%, the interval includes both “reassuringly low” and “clinically concerning” values simultaneously. The data cannot distinguish between those two conclusions — the CI is too wide to be actionable.

A practical rule: If the upper and lower bounds of the CI would lead you to different clinical or administrative decisions, the CI is too wide to support a conclusion.

###Framework 2 — The Relative Width Rule #### Some epidemiologists use relative CI width as a rough heuristic: ##### Relative Width = (CI upper−CI lower)/point estimate #### For your converted SSI example: #### (17.7−0.6)/3.6 =4.75 #### The CI spans nearly 5 times the point estimate — by any standard this is uninformative. A commonly cited informal threshold is <1.0 reasonably precise, 1-2 Moderate uncertainty, report with caution, > 2.0 Wide, conclusion unreliable and if > 4.0 then the result is essentially uninformative. ##### This heuristic (mental shortcut) is not formally codified in the statistical literature to my knowledge — use it as a rapid screen, not a rigid rule.

###The Rapid Interpretation Algorithm ####In practice, when you see a CI in a paper or your own output, run through this sequence in order: ###1. Does the CI span a clinically meaningful difference? #####YES → too wide, unreliable ###2. Are there fewer than 5 events in the subgroup? ####YES → unstable, flag or combine groups ###3. Does the CI include the null and are the bounds wide? ####YES → data insufficient to conclude presence or absence of effect ###4. Is the relative width > 2? ####YES → report with explicit caution ###All NO → CI is acceptably precise for the point estimate

###exercise 4.4 return to wide fomat

complications_wide <- complications_long |>
  pivot_wider(
    names_from  = complication,
    values_from = value
  )

# Confirm dimensions match original
glimpse(complications_wide)
## Rows: 300
## Columns: 6
## $ patient_id         <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, …
## $ operative_approach <fct> Laparoscopic, Converted, Laparoscopic, Laparoscopic…
## $ perforation        <lgl> FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALS…
## $ ssi                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ readmission_30d    <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FA…
## $ ct_performed       <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TR…

Module 5 - gtsummary

###What gtsummary Does - gtsummary inspects your variable types and applies appropriate summary statistics automatically: ### Variable Type Default Summary ###Continuous , Median (IQR) ###Categorical , N (%) ###Logical N (%) TRUE

Table 1 — Cohort Descriptive Statistics

Variable Labels

# Define display labels for all analysis variables
var_labels <- list(
  age                = "Age, years",
  sex                = "Sex",
  bmi                = "BMI, kg/m²",
  asa_class          = "ASA Physical Status",
  wbc                = "WBC, ×10³/µL",
  ct_performed       = "CT Obtained",
  perforation        = "Perforated Appendicitis",
  operative_approach = "Operative Approach",
  op_time_min        = "Operative Time, minutes",
  ssi                = "Surgical Site Infection",
  los_days           = "Length of Stay, days",
  readmission_30d    = "30-day Readmission",
  leukocytosis       = "Leukocytosis (WBC ≥11.0)",
  bmi_class          = "BMI Classification",
  high_risk          = "High Risk (Perforated + Leukocytosis)"
)

###table 1a overall cohort descriptive table

appendicitis |>
  select(age, sex, bmi, bmi_class, asa_class, 
         wbc, leukocytosis, ct_performed,
         perforation, operative_approach, 
         op_time_min, ssi, los_days, readmission_30d) |>
  tbl_summary(
    label   = var_labels,
    missing = "no"
  ) |>
  bold_labels() |>
  italicize_levels() |>
  modify_caption("**Table 1. Cohort Characteristics (N=300)**")
Table 1. Cohort Characteristics (N=300)
Characteristic N = 3001
Age, years 38 (28, 48)
Sex
    Male 148 (49%)
    Female 152 (51%)
BMI, kg/m² 27.6 (24.0, 31.9)
BMI Classification
    Underweight 19 (6.3%)
    Normal 77 (26%)
    Overweight 100 (33%)
    Obese 104 (35%)
ASA Physical Status
    I 55 (18%)
    II 140 (47%)
    III 86 (29%)
    IV 19 (6.3%)
WBC, ×10³/µL 13.8 (10.8, 15.9)
Leukocytosis (WBC ≥11.0)
    Normal 76 (25%)
    Elevated 224 (75%)
CT Obtained 249 (83%)
Perforated Appendicitis 76 (25%)
Operative Approach
    Laparoscopic 235 (78%)
    Open 37 (12%)
    Converted 28 (9.3%)
Operative Time, minutes 67 (51, 85)
Surgical Site Infection 22 (7.3%)
Length of Stay, days 2.10 (1.20, 3.00)
30-day Readmission 21 (7.0%)
1 Median (Q1, Q3); n (%)

###table 1b stratified by perforation status

appendicitis |>
  select(age, sex, bmi, bmi_class, asa_class,
         wbc, leukocytosis, ct_performed,
         operative_approach, op_time_min,
         ssi, los_days, readmission_30d,
         perforation) |>
  tbl_summary(
    by      = perforation,
    label   = var_labels,
    missing = "no"
  ) |>
  bold_labels() |>
  italicize_levels() |>
  modify_header(
    stat_1 ~ "**Non-Perforated**  \nN={n}",
    stat_2 ~ "**Perforated**  \nN={n}"
  ) |>
  modify_caption("**Table 1. Cohort Characteristics Stratified by Perforation Status**")
Table 1. Cohort Characteristics Stratified by Perforation Status
Characteristic Non-Perforated
N=224
1
Perforated
N=76
1
Age, years 37 (27, 46) 40 (30, 51)
Sex

    Male 104 (46%) 44 (58%)
    Female 120 (54%) 32 (42%)
BMI, kg/m² 27.0 (23.3, 31.9) 28.9 (25.9, 31.9)
BMI Classification

    Underweight 15 (6.7%) 4 (5.3%)
    Normal 66 (29%) 11 (14%)
    Overweight 67 (30%) 33 (43%)
    Obese 76 (34%) 28 (37%)
ASA Physical Status

    I 42 (19%) 13 (17%)
    II 105 (47%) 35 (46%)
    III 65 (29%) 21 (28%)
    IV 12 (5.4%) 7 (9.2%)
WBC, ×10³/µL 13.8 (10.7, 16.1) 14.1 (11.1, 15.8)
Leukocytosis (WBC ≥11.0)

    Normal 57 (25%) 19 (25%)
    Elevated 167 (75%) 57 (75%)
CT Obtained 187 (83%) 62 (82%)
Operative Approach

    Laparoscopic 177 (79%) 58 (76%)
    Open 26 (12%) 11 (14%)
    Converted 21 (9.4%) 7 (9.2%)
Operative Time, minutes 67 (53, 86) 69 (49, 81)
Surgical Site Infection 16 (7.1%) 6 (7.9%)
Length of Stay, days 2.10 (1.20, 3.10) 2.10 (1.30, 2.95)
30-day Readmission 16 (7.1%) 5 (6.6%)
1 Median (Q1, Q3); n (%)

###Table 1C — Add Overall Column and P-values

appendicitis |>
  select(age, sex, bmi, bmi_class, asa_class,
         wbc, leukocytosis, ct_performed,
         operative_approach, op_time_min,
         ssi, los_days, readmission_30d,
         perforation) |>
  tbl_summary(
    by      = perforation,
    label   = var_labels,
    missing = "no"
  ) |>
  add_overall() |>
  add_p() |>
  bold_labels() |>
  italicize_levels() |>
  bold_p(t = 0.05) |>
  modify_header(
    stat_1 ~ "**Non-Perforated**  \nN={n}",
    stat_2 ~ "**Perforated**  \nN={n}"
  ) |>
  modify_caption("**Table 1. Cohort Characteristics Stratified by Perforation Status**")
Table 1. Cohort Characteristics Stratified by Perforation Status
Characteristic Overall
N = 300
1
Non-Perforated
N=224
1
Perforated
N=76
1
p-value2
Age, years 38 (28, 48) 37 (27, 46) 40 (30, 51) 0.11
Sex


0.084
    Male 148 (49%) 104 (46%) 44 (58%)
    Female 152 (51%) 120 (54%) 32 (42%)
BMI, kg/m² 27.6 (24.0, 31.9) 27.0 (23.3, 31.9) 28.9 (25.9, 31.9) 0.11
BMI Classification


0.035
    Underweight 19 (6.3%) 15 (6.7%) 4 (5.3%)
    Normal 77 (26%) 66 (29%) 11 (14%)
    Overweight 100 (33%) 67 (30%) 33 (43%)
    Obese 104 (35%) 76 (34%) 28 (37%)
ASA Physical Status


0.7
    I 55 (18%) 42 (19%) 13 (17%)
    II 140 (47%) 105 (47%) 35 (46%)
    III 86 (29%) 65 (29%) 21 (28%)
    IV 19 (6.3%) 12 (5.4%) 7 (9.2%)
WBC, ×10³/µL 13.8 (10.8, 15.9) 13.8 (10.7, 16.1) 14.1 (11.1, 15.8) >0.9
Leukocytosis (WBC ≥11.0)


>0.9
    Normal 76 (25%) 57 (25%) 19 (25%)
    Elevated 224 (75%) 167 (75%) 57 (75%)
CT Obtained 249 (83%) 187 (83%) 62 (82%) 0.7
Operative Approach


0.8
    Laparoscopic 235 (78%) 177 (79%) 58 (76%)
    Open 37 (12%) 26 (12%) 11 (14%)
    Converted 28 (9.3%) 21 (9.4%) 7 (9.2%)
Operative Time, minutes 67 (51, 85) 67 (53, 86) 69 (49, 81) 0.4
Surgical Site Infection 22 (7.3%) 16 (7.1%) 6 (7.9%) 0.8
Length of Stay, days 2.10 (1.20, 3.00) 2.10 (1.20, 3.10) 2.10 (1.30, 2.95) 0.9
30-day Readmission 21 (7.0%) 16 (7.1%) 5 (6.6%) 0.9
1 Median (Q1, Q3); n (%)
2 Wilcoxon rank sum test; Pearson’s Chi-squared test; Fisher’s exact test