Skip to contents

Introduction

Treatment response analysis is crucial in oncology research for evaluating therapeutic efficacy and understanding patient outcomes. This vignette demonstrates specialized functions in ClinicoPathDescriptives for analyzing treatment response data, including waterfall plots, swimmer plots, and RECIST criteria implementation.

Oncology Response Evaluation

RECIST Criteria

Response Evaluation Criteria in Solid Tumors (RECIST) provides standardized guidelines for measuring treatment response:

  • Complete Response (CR): Disappearance of all target lesions
  • Partial Response (PR): ≥30% decrease in sum of target lesion diameters
  • Progressive Disease (PD): ≥20% increase in sum of target lesion diameters
  • Stable Disease (SD): Neither PR nor PD criteria met
library(ClinicoPath)
library(dplyr)
library(ggplot2)
library(knitr)

# Load both datasets
data(treatmentResponse)
data(histopathology)

# Examine treatment response data structure
head(treatmentResponse)
summary(treatmentResponse$ResponseValue)

Dataset Preparation

# Create comprehensive treatment response dataset
response_data <- treatmentResponse %>%
  mutate(
    # RECIST classification based on percentage change
    RECIST_Response = case_when(
      is.na(ResponseValue) ~ "Not Evaluable",
      ResponseValue <= -30 ~ "Partial Response (PR)",
      ResponseValue > -30 & ResponseValue < 20 ~ "Stable Disease (SD)",
      ResponseValue >= 20 ~ "Progressive Disease (PD)"
    ),
    # Additional response categories
    Response_Category = case_when(
      is.na(ResponseValue) ~ "Missing",
      ResponseValue < 0 ~ "Tumor Shrinkage",
      ResponseValue == 0 ~ "No Change",
      ResponseValue > 0 ~ "Tumor Growth"
    ),
    # Best response categories
    Best_Response = case_when(
      ResponseValue <= -50 ~ "Major Response",
      ResponseValue <= -30 ~ "Partial Response",
      ResponseValue > -30 & ResponseValue < 20 ~ "Stable Disease",
      ResponseValue >= 20 & ResponseValue < 50 ~ "Progressive Disease",
      ResponseValue >= 50 ~ "Rapid Progression",
      TRUE ~ "Not Evaluable"
    ),
    # Extract patient number for sorting
    PatientNum = as.numeric(gsub("PT", "", PatientID))
  ) %>%
  arrange(ResponseValue)

# Summary statistics
cat("Treatment Response Dataset Summary:\n")
cat("Total Patients:", nrow(response_data), "\n")
cat("Evaluable Patients:", sum(!is.na(response_data$ResponseValue)), "\n")
cat("Missing Data:", sum(is.na(response_data$ResponseValue)), "\n\n")

# Response distribution
table(response_data$RECIST_Response)

Basic Response Analysis

Response Distribution

# Analyze response categories
reportcat(
  data = response_data,
  vars = c("RECIST_Response", "Best_Response", "Response_Category")
)

Summary Statistics by Response Category

# Continuous variable summary by response
summarydata(
  data = response_data,
  vars = "ResponseValue",
  date_vars = NULL,
  grvar = "RECIST_Response"
)

Waterfall Plot Analysis

The waterfall plot is the gold standard for visualizing individual patient treatment responses in oncology trials.

# Basic waterfall plot
waterfall(
  data = response_data,
  patientID = "PatientID",
  responseVar = "ResponseValue",
  timeVar = NULL,
  showWaterfallPlot = TRUE
)

Advanced Waterfall Plot with Response Categories

# Create waterfall with color coding by response category
waterfall(
  data = response_data,
  patientID = "PatientID", 
  responseVar = "ResponseValue",
  timeVar = NULL,
  showWaterfallPlot = TRUE
)

Swimmer Plot Analysis

Swimmer plots show individual patient timelines, treatment durations, and clinical events over time.

# Prepare data for swimmer plot by merging with clinical data
# Create synthetic timeline data based on available information
swimmer_data <- histopathology %>%
  select(ID, Group, OverallTime, Outcome, Death, TStage, Grade) %>%
  mutate(
    PatientID = paste0("PT", sprintf("%04d", ID)),
    Treatment_Duration = pmax(1, OverallTime * runif(n(), 0.6, 1.0)), # Synthetic treatment duration
    Event_Time = ifelse(Outcome == 1, OverallTime, NA),
    Response_Time = Treatment_Duration * runif(n(), 0.3, 0.8), # Synthetic response assessment time
    Treatment_Status = case_when(
      Death == "DOĞRU" ~ "Discontinued - Death",
      Outcome == 1 ~ "Discontinued - Progression", 
      TRUE ~ "Ongoing"
    )
  ) %>%
  filter(ID <= 50) %>%  # Limit to first 50 patients for clarity
  arrange(desc(OverallTime))

# Generate swimmer plot
swimmerplot(
  data = swimmer_data,
  patientID = "PatientID",
  startTime = "Treatment_Duration",
  endTime = "OverallTime",
  responseVar = NULL,
  sortVariable = NULL,
  milestone1Date = NULL,
  milestone2Date = NULL,
  milestone3Date = NULL,
  milestone4Date = NULL,
  milestone5Date = NULL
)

Response Correlation Analysis

Response by Patient Characteristics

# Merge response data with clinical characteristics
combined_data <- response_data %>%
  left_join(
    histopathology %>% 
      select(ID, Group, Age, Sex, Grade, TStage, LVI, PNI, LymphNodeMetastasis) %>%
      mutate(PatientID = paste0("PT", sprintf("%04d", ID))),
    by = "PatientID"
  ) %>%
  filter(!is.na(Group))  # Keep only patients with clinical data

# Response by treatment group
crosstable(
  data = combined_data,
  vars = "RECIST_Response",
  group = "Group"
)

Response by Pathological Characteristics

# Response by tumor grade
crosstable(
  data = combined_data,
  vars = "RECIST_Response",
  group = "Grade"
)
# Response by T-stage
crosstable(
  data = combined_data,
  vars = "RECIST_Response",
  group = "TStage" 
)

Advanced Response Visualizations

Response Flow Analysis

# Alluvial diagram showing response patterns
alluvial(
  data = combined_data %>% filter(!is.na(RECIST_Response)),
  vars = c("Group", "Grade", "TStage", "RECIST_Response"),
  condensationvar = "RECIST_Response"
)

Age-Response Correlation

# Age pyramid by response category
combined_data_age <- combined_data %>%
  filter(!is.na(RECIST_Response) & RECIST_Response != "Not Evaluable")

agepyramid(
  data = combined_data_age,
  age = "Age",
  gender = "Sex",
  female = "Female"
)

Response Kinetics Analysis

Response Magnitude Analysis

# Analyze response magnitude by patient subgroups
response_magnitude <- combined_data %>%
  filter(!is.na(ResponseValue)) %>%
  mutate(
    Age_Group = case_when(
      Age < 50 ~ "< 50 years",
      Age >= 50 ~ "≥ 50 years"
    ),
    High_Grade = ifelse(Grade == "3", "Grade 3", "Grade 1-2")
  )

# Response by age group
summarydata(
  data = response_magnitude,
  vars = "ResponseValue",
  date_vars = NULL,
  grvar = "Age_Group"
)
# Response by tumor grade
summarydata(
  data = response_magnitude,
  vars = "ResponseValue",
  date_vars = NULL,
  grvar = "High_Grade"
)

Predictive Biomarker Analysis

# Merge with biomarker data
biomarker_response <- combined_data %>%
  left_join(
    histopathology %>%
      select(ID, MeasurementA, MeasurementB) %>%
      mutate(PatientID = paste0("PT", sprintf("%04d", ID))),
    by = "PatientID"
  ) %>%
  filter(!is.na(MeasurementA) & !is.na(ResponseValue)) %>%
  mutate(
    MeasurementA_Level = ifelse(MeasurementA > median(MeasurementA), "High", "Low"),
    MeasurementB_Level = ifelse(MeasurementB > median(MeasurementB), "High", "Low")
  )

# Response by biomarker levels
crosstable(
  data = biomarker_response,
  vars = "RECIST_Response",
  group = "MeasurementA_Level"
)

Response Duration Analysis

# Create response duration data
duration_data <- combined_data %>%
  filter(!is.na(ResponseValue)) %>%
  left_join(
    histopathology %>%
      select(ID, OverallTime) %>%
      mutate(PatientID = paste0("PT", sprintf("%04d", ID))),
    by = "PatientID"
  ) %>%
  filter(!is.na(OverallTime))

# Duration by response category
summarydata(
  data = duration_data,
  vars = "OverallTime",
  date_vars = NULL,
  grvar = "RECIST_Response"
)

Clinical Significance Assessment

Response Rate Calculations

# Calculate response rates by subgroup
response_rates <- combined_data %>%
  filter(!is.na(RECIST_Response) & RECIST_Response != "Not Evaluable") %>%
  group_by(Group) %>%
  summarise(
    Total_Patients = n(),
    PR_Count = sum(RECIST_Response == "Partial Response (PR)"),
    SD_Count = sum(RECIST_Response == "Stable Disease (SD)"),
    PD_Count = sum(RECIST_Response == "Progressive Disease (PD)"),
    Overall_Response_Rate = round(PR_Count / Total_Patients * 100, 1),
    Disease_Control_Rate = round((PR_Count + SD_Count) / Total_Patients * 100, 1),
    Progressive_Disease_Rate = round(PD_Count / Total_Patients * 100, 1),
    .groups = 'drop'
  )

kable(response_rates,
      caption = "Treatment Response Rates by Study Group",
      col.names = c("Group", "Total", "PR", "SD", "PD", 
                    "ORR (%)", "DCR (%)", "PDR (%)"))

Best Response Analysis

# Analyze best response categories
best_response_summary <- combined_data %>%
  filter(!is.na(Best_Response)) %>%
  group_by(Group, Best_Response) %>%
  summarise(Count = n(), .groups = 'drop') %>%
  group_by(Group) %>%
  mutate(Percentage = round(Count / sum(Count) * 100, 1)) %>%
  arrange(Group, desc(Count))

kable(best_response_summary,
      caption = "Best Response Distribution by Treatment Group")

Conclusions and Clinical Implications

Key Findings Summary

Based on this comprehensive treatment response analysis:

  1. Overall Response: The analysis reveals distinct response patterns between treatment groups
  2. RECIST Classification: Standard RECIST criteria provide clear categorization of responses
  3. Predictive Factors: Age, grade, and biomarker levels show associations with response
  4. Clinical Benefit: Disease control rates demonstrate treatment efficacy beyond just partial responses

Visualization Strengths

  • Waterfall Plots: Excellent for visualizing individual patient responses and overall treatment effect
  • Swimmer Plots: Essential for understanding treatment duration and timeline relationships
  • Combined Analysis: Integration with pathological and biomarker data enhances clinical relevance

Regulatory Considerations

This analysis framework supports: - FDA/EMA submission requirements for response evaluation - RECIST criteria compliance for solid tumor studies - Biomarker-stratified analysis for precision medicine approaches - Safety and efficacy endpoint documentation

Implementation in jamovi

All response analysis functions are available in jamovi:

  1. Waterfall Plots: Exploration > Patient Follow-Up Plots > Treatment Response Analysis
  2. Swimmer Plots: Exploration > Patient Follow-Up Plots > Swimmer Plot
  3. Cross-tabulations: Exploration > ClinicoPath Comparisons > Cross Tables

The jamovi interface provides point-and-click access to these specialized oncology analysis tools, making them accessible to clinical researchers without programming experience.

Further Resources

  • Clinical Workflow Vignette: For integrating response analysis into complete study workflows
  • Visualization Gallery: For advanced customization of oncology plots
  • Getting Started Guide: For basic ClinicoPathDescriptives functionality