---
title: "Module 4 Solution — Date & Text Handling"
format:
  html:
    css: "../dark-theme.css"
    include-in-header: |
      <script>
        // Immediate theme application to prevent flash
        (function() {
          const savedTheme = localStorage.getItem('theme') || 'light';
          document.documentElement.setAttribute('data-bs-theme', savedTheme);
        })();
      </script>
    include-after-body: |
      <button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme">🌙</button>
      <script>
        function toggleTheme() {
          const html = document.documentElement;
          const currentTheme = html.getAttribute('data-bs-theme');
          const newTheme = currentTheme === 'dark' ? 'light' : 'dark';

          html.setAttribute('data-bs-theme', newTheme);
          localStorage.setItem('theme', newTheme);

          // Update button icon
          const button = document.querySelector('.theme-toggle');
          button.innerHTML = newTheme === 'dark' ? '☀️' : '🌙';
        }

        // Initialize theme on load
        document.addEventListener('DOMContentLoaded', function() {
          const savedTheme = localStorage.getItem('theme') || 'light';
          document.documentElement.setAttribute('data-bs-theme', savedTheme);
          const button = document.querySelector('.theme-toggle');
          if (button) {
            button.innerHTML = savedTheme === 'dark' ? '☀️' : '🌙';
          }
        });
      </script>
editor: visual
---
# ✅ Module 4 — Date & Text Handling (Solution)
## Integrating R4DS Chapters 14 (Strings), 15 (Regular Expressions), 16 (Factors)

## 📦 Setup and Libraries

```{r}
library(dplyr)
library(tibble)
library(lubridate)
library(stringr)    # R4DS Chapter 14: Strings
library(forcats)    # R4DS Chapter 16: Factors
```

## ✅ Exercise 1 Solution: Date Parsing Challenge

```{r}
# Create the dataset
ae_raw <- tibble(
  USUBJID = c("001-001", "001-002", "001-003", "001-004", "001-005", "001-006"),
  AEDECOD = c("HEADACHE", "NAUSEA", "FATIGUE", "DIZZINESS", "RASH", "COUGH"),
  AESTDTC_MESSY = c("2024-01-20", "25/01/2024", "01/18/2024", "20240122", "2024/01/25", "Jan 26, 2024"),
  RFSTDTC = rep("2024-01-15", 6)
)

# Solution: Parse dates and calculate study days
ae_dates <- ae_raw %>%
  mutate(
    # Parse AESTDTC_MESSY using different lubridate functions
    AESTDT = case_when(
      str_detect(AESTDTC_MESSY, "^\\d{4}-\\d{2}-\\d{2}$") ~ ymd(AESTDTC_MESSY),
      str_detect(AESTDTC_MESSY, "^\\d{2}/\\d{2}/\\d{4}$") ~ dmy(AESTDTC_MESSY),
      str_detect(AESTDTC_MESSY, "^\\d{2}/\\d{2}/\\d{4}$") ~ mdy(AESTDTC_MESSY),
      str_detect(AESTDTC_MESSY, "^\\d{8}$") ~ ymd(AESTDTC_MESSY),
      str_detect(AESTDTC_MESSY, "^[A-Za-z]{3}\\s+\\d{2},\\s+\\d{4}$") ~ mdy(AESTDTC_MESSY),
      TRUE ~ as.Date(NA)
    ),

    # Parse RFSTDTC
    RFSTDT = ymd(RFSTDTC),

    # Calculate study day
    AESTDY = as.numeric(AESTDT - RFSTDT) + 1
  )

print("✅ Parsed dates and study days:")
print(ae_dates)
```

## ✅ Exercise 2 Solution: Study Day Categories

```{r}
# Add study day categories
ae_with_categories <- ae_dates %>%
  mutate(
    # Create study day categories
    STUDYDAY_PERIOD = case_when(
      is.na(AESTDY) ~ "Unknown",
      AESTDY <= 0 ~ "Pre-treatment",
      AESTDY <= 7 ~ "Week 1",
      AESTDY <= 14 ~ "Week 2",
      AESTDY <= 28 ~ "Month 1",
      TRUE ~ "After Month 1"
    ),

    # Create early AE flag (within first 7 days)
    EARLY_AE = ifelse(AESTDY >= 1 & AESTDY <= 7, "Y", "N")
  )

print("✅ With study day categories:")
print(ae_with_categories)
```

## ✅ Exercise 3 Solution: String Cleaning Challenge (R4DS Chapter 14)

```{r}
# Create messy adverse event terms
ae_messy_text <- tibble(
  USUBJID = c("001-001", "001-002", "001-003", "001-004", "001-005"),
  AEDECOD_RAW = c(
    "  mild headache  ",
    "SEVERE nausea (grade 3)",
    "fatigue - moderate",
    "  DIZZINESS mild  ",
    "skin rash (MODERATE)"
  ),
  MEDICATION = c("Ibuprofen 400mg", "ondansetron 8 MG", "caffeine 200mg", "rest", "hydrocortisone 1%"),
  TREATMENT_ARM = c("Active", "Placebo", "Active", "Active", "Placebo"),
  SEVERITY = c("Mild", "Moderate", "Severe", "Mild", "Moderate")
)

# Solution: Clean and extract information
ae_cleaned <- ae_messy_text %>%
  mutate(
    # Clean adverse event terms
    AEDECOD_CLEAN = AEDECOD_RAW %>%
      str_trim() %>%                                    # Remove leading/trailing spaces
      str_to_upper() %>%                               # Convert to uppercase
      str_replace_all("\\([^)]*\\)", "") %>%           # Remove parentheses and contents
      str_replace_all(" - ", " ") %>%                  # Remove dashes
      str_replace_all("\\s+", " ") %>%                 # Replace multiple spaces with single
      str_trim(),                                      # Final trim

    # Extract severity
    SEVERITY = case_when(
      str_detect(AEDECOD_RAW, "(?i)mild") ~ "MILD",
      str_detect(AEDECOD_RAW, "(?i)moderate") ~ "MODERATE",
      str_detect(AEDECOD_RAW, "(?i)severe") ~ "SEVERE",
      TRUE ~ "UNKNOWN"
    ),

    # Extract base term (remove severity words)
    AETERM_BASE = AEDECOD_CLEAN %>%
      str_replace_all("^(MILD|MODERATE|SEVERE)\\s+", "") %>%  # Remove severity at start
      str_replace_all("\\s+(MILD|MODERATE|SEVERE)$", ""),     # Remove severity at end

    # Create specific AE flags
    HEADACHE_FLAG = ifelse(str_detect(AETERM_BASE, "HEADACHE"), "Y", "N"),
    NAUSEA_FLAG = ifelse(str_detect(AETERM_BASE, "NAUSEA"), "Y", "N"),
    FATIGUE_FLAG = ifelse(str_detect(AETERM_BASE, "FATIGUE"), "Y", "N"),

    # Extract numeric dose from medication
    DOSE_NUMERIC = as.numeric(str_extract(MEDICATION, "\\d+")),

    # Clean medication names
    MED_CLEAN = MEDICATION %>%
      str_to_upper() %>%
      str_replace_all("\\d+\\s?[A-Z%]+", "") %>%      # Remove dose information
      str_trim()
  )

print("✅ Cleaned text data:")
print(ae_cleaned)
```

## ✅ Exercise 4 Solution: Regular Expressions Practice (R4DS Chapter 15)

```{r}
# Create clinical data with patterns to validate
clinical_data <- tibble(
  USUBJID = c("001-001", "002-001", "001-ABC", "999-123", "001-99"),
  PHONE = c("(555) 123-4567", "555-123-4567", "555.123.4567", "5551234567", "invalid-phone"),
  EMAIL = c("investigator@clinic.com", "bad.email", "test@site.org", "missing@", "@incomplete.com"),
  LAB_RESULT = c("WBC: 7.2 K/uL", "Hemoglobin: 12.5 g/dL", "Glucose: 95 mg/dL", "Invalid result", ""),
  VISIT_DATE = c("2024-01-15", "2024/01/20", "15-Jan-2024", "invalid-date", "2024-13-45")
)

# Solution: Use regular expressions for validation
clinical_validated <- clinical_data %>%
  mutate(
    # Validate subject ID format (###-###)
    VALID_SUBJID = str_detect(USUBJID, "^\\d{3}-\\d{3}$"),

    # Validate phone number formats
    VALID_PHONE = str_detect(PHONE, "^(\\(\\d{3}\\)|\\d{3})[-.\\s]?\\d{3}[-.\\s]?\\d{4}$"),

    # Validate email addresses
    VALID_EMAIL = str_detect(EMAIL, "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"),

    # Extract numeric values from lab results
    LAB_VALUE = as.numeric(str_extract(LAB_RESULT, "\\d+\\.?\\d*")),

    # Extract lab test name (before colon)
    LAB_TEST = str_extract(LAB_RESULT, "^[^:]+"),

    # Validate date formats (YYYY-MM-DD only)
    VALID_DATE_FORMAT = str_detect(VISIT_DATE, "^\\d{4}-\\d{2}-\\d{2}$"),

    # Extract year from valid dates
    VISIT_YEAR = str_extract(VISIT_DATE, "^\\d{4}")
  )

print("✅ Regular expression validation results:")
print(clinical_validated)
```

## ✅ Exercise 5 Solution: Factor Management Practice (R4DS Chapter 16)

```{r}
# Create clinical factors data
clinical_factors <- tibble(
  USUBJID = paste0("00", 1:8, "-", sprintf("%03d", 1:8)),
  SEVERITY = c("Mild", "Severe", "Moderate", "Mild", "Severe", "Moderate", "Mild", "Moderate"),
  TREATMENT = c("Placebo", "Low Dose", "High Dose", "Placebo", "Low Dose", "High Dose", "Placebo", "Low Dose"),
  OUTCOME = c("Recovered", "Ongoing", "Recovered", "Worsened", "Recovered", "Ongoing", "Recovered", "Ongoing"),
  VISIT_NAME = c("Screening", "Day 1", "Week 2", "Month 1", "Week 4", "End of Study", "Follow-up", "Unscheduled")
)

# Solution: Convert to appropriate factors
clinical_factors_clean <- clinical_factors %>%
  mutate(
    # Create ordered severity factor
    SEVERITY_FACTOR = factor(SEVERITY,
                           levels = c("Mild", "Moderate", "Severe"),
                           ordered = TRUE),

    # Create treatment factor with meaningful labels
    TREATMENT_FACTOR = factor(TREATMENT,
                            levels = c("Placebo", "Low Dose", "High Dose"),
                            labels = c("Placebo", "Low Dose (5mg)", "High Dose (10mg)")),

    # Create outcome factor
    OUTCOME_FACTOR = factor(OUTCOME),

    # Create visit factor with logical ordering
    VISIT_FACTOR = factor(VISIT_NAME,
                        levels = c("Screening", "Day 1", "Week 2", "Week 4",
                                 "Month 1", "End of Study", "Follow-up", "Unscheduled")),

    # Create risk category based on severity and treatment
    RISK_CATEGORY = case_when(
      SEVERITY == "Severe" & TREATMENT == "High Dose" ~ "High",
      SEVERITY == "Moderate" | (SEVERITY == "Severe" & TREATMENT != "High Dose") ~ "Medium",
      SEVERITY == "Mild" ~ "Low",
      TRUE ~ "Medium"
    ) %>% factor(levels = c("Low", "Medium", "High"), ordered = TRUE)
  )

print("✅ Factor management results:")
print(clinical_factors_clean)

# Factor manipulation exercises solutions:
severity_counts <- table(clinical_factors_clean$SEVERITY_FACTOR)
treatment_reordered <- fct_infreq(clinical_factors_clean$TREATMENT_FACTOR)
visit_collapsed <- fct_collapse(clinical_factors_clean$VISIT_FACTOR,
                              "Early" = c("Screening", "Day 1"),
                              "Late" = c("End of Study", "Follow-up"),
                              "Other" = c("Week 2", "Week 4", "Month 1", "Unscheduled"))

print("✅ Factor manipulation results:")
print(severity_counts)
print(levels(treatment_reordered))
print(levels(visit_collapsed))
```

## ✅ Exercise 6 Solution: AESTDY Derivation Practice

```{r}
# Create a more complex dataset for AESTDY practice
complex_ae <- tibble(
  USUBJID = c("001-001", "001-001", "001-002", "001-002", "001-003"),
  AESEQ = c(1, 2, 1, 2, 1),
  AEDECOD = c("HEADACHE", "NAUSEA", "FATIGUE", "HEADACHE", "DIZZINESS"),
  AESTDTC = c("2024-01-20T08:30", "2024-01-25T14:15", "2024-01-18T09:00", "2024-01-22T16:30", NA),
  AEENDTC = c("2024-01-21T10:00", "2024-01-26T08:00", "2024-01-20T18:00", "2024-01-23T12:00", NA),
  RFSTDTC = c("2024-01-15T09:00", "2024-01-15T09:00", "2024-01-16T10:00", "2024-01-16T10:00", "2024-01-15T09:00")
)

# Solution: Derive comprehensive study day variables
complex_ae_derived <- complex_ae %>%
  mutate(
    # Parse start dates/times
    AESTDT = ymd_hm(AESTDTC),
    AEENDT = ymd_hm(AEENDTC),
    RFSTDT = ymd_hm(RFSTDTC),

    # Calculate study days
    AESTDY = as.numeric(as.Date(AESTDT) - as.Date(RFSTDT)) + 1,
    AEENDY = as.numeric(as.Date(AEENDT) - as.Date(RFSTDT)) + 1,

    # Calculate duration
    AE_DURATION_DAYS = as.numeric(as.Date(AEENDT) - as.Date(AESTDT)),

    # Handle missing dates
    AESTDY_SAFE = case_when(
      is.na(AESTDT) | is.na(RFSTDT) ~ NA_real_,
      TRUE ~ AESTDY
    ),

    # Create validation flags
    VALID_DATES = case_when(
      is.na(AESTDTC) ~ "Missing AE start date",
      is.na(RFSTDTC) ~ "Missing reference date",
      !is.na(AEENDTC) & AEENDT < AESTDT ~ "End date before start date",
      TRUE ~ "Complete"
    )
  )

print("✅ Complex AESTDY derivations:")
print(complex_ae_derived)
```

## ✅ Bonus Solution: Combined Date, Text, Regex, and Factors Challenge

```{r}
# Combine everything from R4DS Chapters 14, 15, 16
final_challenge <- complex_ae_derived %>%
  # Add severity and treatment factors
  mutate(
    SEVERITY = sample(c("Mild", "Moderate", "Severe"), n(), replace = TRUE),
    TREATMENT = sample(c("Placebo", "Active"), n(), replace = TRUE)
  ) %>%
  mutate(
    # Create comprehensive AE description with factors
    AE_DESCRIPTION = paste0(
      AEDECOD, " - ", SEVERITY, " severity, ",
      "Study Day ", AESTDY_SAFE,
      ifelse(!is.na(AE_DURATION_DAYS), paste0(", Duration: ", AE_DURATION_DAYS, " days"), ""),
      " (", TREATMENT, " arm)"
    ),

    # Create analysis-ready flags
    ONGOING_AE = ifelse(is.na(AEENDTC), "Y", "N"),
    EARLY_ONSET = ifelse(AESTDY_SAFE <= 7 & AESTDY_SAFE >= 1, "Y", "N"),
    LONG_DURATION = ifelse(AE_DURATION_DAYS > 7, "Y", "N"),

    # Create factors
    SEVERITY_FACTOR = factor(SEVERITY, levels = c("Mild", "Moderate", "Severe"), ordered = TRUE),
    TREATMENT_FACTOR = factor(TREATMENT, levels = c("Placebo", "Active"))
  )

print("✅ Final combined challenge:")
print(final_challenge)
```

## 📊 Summary Statistics

```{r}
cat("=== SOLUTION SUMMARY ===\n")
cat("AEs by study day period:\n")
print(table(ae_with_categories$STUDYDAY_PERIOD, useNA = "ifany"))

cat("\nAEs by severity:\n")
print(table(ae_cleaned$SEVERITY, useNA = "ifany"))

cat("\nEarly AEs (≤7 days):\n")
print(sum(ae_with_categories$EARLY_AE == "Y", na.rm = TRUE))

cat("\nRegular expression validation summary:\n")
print(summary(clinical_validated[c("VALID_SUBJID", "VALID_PHONE", "VALID_EMAIL")]))

cat("\nFactor level summary:\n")
print(summary(clinical_factors_clean$SEVERITY_FACTOR))
```

## 🎉 Module 4 Solution Complete!

**You've successfully mastered R4DS concepts:**

✅ **Date parsing** with lubridate (ymd, dmy, mdy)
✅ **Study day calculations** (AESTDY) with proper handling of missing data
✅ **String manipulation** with stringr (R4DS Chapter 14)
✅ **Regular expressions** for clinical data validation (R4DS Chapter 15)
✅ **Factor management** with forcats (R4DS Chapter 16)
✅ **Clinical pattern matching** and data quality validation
✅ **Ordered factors** for severity assessment and risk categorization
✅ **Combined operations** integrating dates, strings, regex, and factors

### 🔗 R4DS Integration Success
This module successfully integrated concepts from:

- **Chapter 14**: Strings - Complete string manipulation toolkit
- **Chapter 15**: Regular expressions - Advanced pattern matching for clinical validation
- **Chapter 16**: Factors - Categorical data management for clinical variables

**Excellent work! You're ready for Module 5: Functions & Macro Translation!** 🚀
