10  loops

11 Introduction to Loops in R Programming

Loops are fundamental programming constructs that allow us to execute code repeatedly. In pharmaceutical data science and research using R, loops are essential for processing clinical trial data, analyzing drug interactions, calculating pharmacokinetic parameters, and automating repetitive analytical tasks.

11.1 Types of Loops in R

11.1.1 1. For Loops

For loops iterate over a sequence of items for a predetermined number of times.

11.1.1.1 Basic Syntax (R)

for (variable in sequence) {
  # code to execute
}

11.1.1.2 Example 1: Processing Patient Data

# Patient IDs in a clinical trial
patient_ids <- c("PT001", "PT002", "PT003", "PT004", "PT005")

# Processing each patient
for (patient_id in patient_ids) {
  cat("Processing data for patient:", patient_id, "\n")
  # Simulate data processing
  cat("  - Vital signs checked\n")
  cat("  - Adverse events recorded\n")
  cat("  - Drug concentration measured\n")
  cat("\n")
}
Processing data for patient: PT001 
  - Vital signs checked
  - Adverse events recorded
  - Drug concentration measured

Processing data for patient: PT002 
  - Vital signs checked
  - Adverse events recorded
  - Drug concentration measured

Processing data for patient: PT003 
  - Vital signs checked
  - Adverse events recorded
  - Drug concentration measured

Processing data for patient: PT004 
  - Vital signs checked
  - Adverse events recorded
  - Drug concentration measured

Processing data for patient: PT005 
  - Vital signs checked
  - Adverse events recorded
  - Drug concentration measured

11.1.1.3 Example 2: Drug Dosage Calculations

# Different patient weights (kg)
patient_weights <- c(65, 72, 58, 80, 95, 68)

# Drug dosage:  5 mg/kg
dose_per_kg <- 5

cat("Patient Dosage Calculations:\n")
Patient Dosage Calculations:
cat(strrep("-", 40), "\n")
---------------------------------------- 
for (i in seq_along(patient_weights)) {
  weight <- patient_weights[i]
  dosage <- weight * dose_per_kg
  cat(sprintf("Patient %d: %dkg → %dmg\n", i, weight, dosage))
}
Patient 1: 65kg → 325mg
Patient 2: 72kg → 360mg
Patient 3: 58kg → 290mg
Patient 4: 80kg → 400mg
Patient 5: 95kg → 475mg
Patient 6: 68kg → 340mg

11.1.1.4 Example 3: Pharmacokinetic Parameter Calculation

# Drug concentration over time (mg/L)
concentrations <- c(100, 87, 75, 65, 57, 49, 43)
time_points <- c(0, 1, 2, 3, 4, 5, 6)  # hours

cat("Pharmacokinetic Analysis:\n")
Pharmacokinetic Analysis:
cat(strrep("-", 30), "\n")
------------------------------ 
# Calculate half-life using multiple time points
initial_conc <- concentrations[1]
target_conc <- initial_conc / 2

for (i in 2:length(concentrations)) {
  if (concentrations[i] <= target_conc) {
    # Linear interpolation for half-life
    t_half <- time_points[i-1] + 
              (time_points[i] - time_points[i-1]) * 
              (target_conc - concentrations[i-1]) / 
              (concentrations[i] - concentrations[i-1])
    
    cat(sprintf("Estimated half-life: %. 2f hours\n", t_half))
    break
  }
}
Estimated half-life: %.0 2f hours

11.1.1.5 Example 4: Creating Data Frame with Loop

# Create a comprehensive patient dataset
patients <- data.frame(
  patient_id = character(0),
  age = numeric(0),
  weight = numeric(0),
  dose = numeric(0),
  bmi = numeric(0),
  stringsAsFactors = FALSE
)

# Patient data
patient_data <- list(
  list(id = "PT001", age = 45, weight = 70, height = 1.75),
  list(id = "PT002", age = 32, weight = 65, height = 1.68),
  list(id = "PT003", age = 58, weight = 80, height = 1.82),
  list(id = "PT004", age = 29, weight = 55, height = 1.60),
  list(id = "PT005", age = 67, weight = 90, height = 1.78)
)

# Process each patient and add to data frame
for (i in seq_along(patient_data)) {
  patient <- patient_data[[i]]
  
  # Calculate BMI
  bmi <- patient$weight / (patient$height^2)
  
  # Calculate dose (5mg/kg, max 400mg)
  dose <- min(patient$weight * 5, 400)
  
  # Add row to data frame
  patients[i, ] <- list(
    patient_id = patient$id,
    age = patient$age,
    weight = patient$weight,
    dose = dose,
    bmi = bmi
  )
}

print(patients)
  patient_id age weight dose      bmi
1      PT001  45     70  350 22.85714
2      PT002  32     65  325 23.03005
3      PT003  58     80  400 24.15167
4      PT004  29     55  275 21.48437
5      PT005  67     90  400 28.40550

11.1.2 2. While Loops

While loops continue executing as long as a condition remains true.

11.1.2.1 Basic Syntax (R)

while (condition) {
  # code to execute
}

11.1.2.2 Example 1: Drug Titration Protocol

# Initial dose and target concentration
current_dose <- 10  # mg
target_concentration <- 25  # mg/L
current_concentration <- 8  # mg/L
bioavailability <- 0.8
volume_distribution <- 5  # L

day <- 1

cat("Drug Titration Protocol:\n")
Drug Titration Protocol:
cat(strrep("-", 35), "\n")
----------------------------------- 
while (current_concentration < target_concentration && day <= 14) {
  cat(sprintf("Day %d:\n", day))
  cat(sprintf("  Current dose: %. 1fmg\n", current_dose))
  cat(sprintf("  Current concentration: %.1fmg/L\n", current_concentration))
  
  # Increase dose by 20% if below target
  if (current_concentration < target_concentration) {
    current_dose <- current_dose * 1.2
    # Simulate concentration response
    current_concentration <- (current_dose * bioavailability) / volume_distribution
  }
  
  day <- day + 1
}
Day 1:
  Current dose: %.0 1fmg
  Current concentration: 8.0mg/L
Day 2:
  Current dose: %.0 1fmg
  Current concentration: 1.9mg/L
Day 3:
  Current dose: %.0 1fmg
  Current concentration: 2.3mg/L
Day 4:
  Current dose: %.0 1fmg
  Current concentration: 2.8mg/L
Day 5:
  Current dose: %.0 1fmg
  Current concentration: 3.3mg/L
Day 6:
  Current dose: %.0 1fmg
  Current concentration: 4.0mg/L
Day 7:
  Current dose: %.0 1fmg
  Current concentration: 4.8mg/L
Day 8:
  Current dose: %.0 1fmg
  Current concentration: 5.7mg/L
Day 9:
  Current dose: %.0 1fmg
  Current concentration: 6.9mg/L
Day 10:
  Current dose: %.0 1fmg
  Current concentration: 8.3mg/L
Day 11:
  Current dose: %.0 1fmg
  Current concentration: 9.9mg/L
Day 12:
  Current dose: %.0 1fmg
  Current concentration: 11.9mg/L
Day 13:
  Current dose: %.0 1fmg
  Current concentration: 14.3mg/L
Day 14:
  Current dose: %.0 1fmg
  Current concentration: 17.1mg/L
cat(sprintf("\nFinal dose achieved: %.1fmg\n", current_dose))

Final dose achieved: 128.4mg
cat(sprintf("Final concentration: %.1fmg/L\n", current_concentration))
Final concentration: 20.5mg/L

11.1.2.3 Example 2: Quality Control Testing

# Set seed for reproducible results
set.seed(123)

# Quality control for drug batch
batch_size <- 1000
tested_units <- 0
defective_units <- 0
max_defects_allowed <- 5  # 0. 5% defect rate

cat("Quality Control Testing:\n")
Quality Control Testing:
cat(strrep("-", 30), "\n")
------------------------------ 
while (tested_units < batch_size && defective_units <= max_defects_allowed) {
  tested_units <- tested_units + 1
  
  # Simulate testing (5% chance of defect)
  is_defective <- runif(1) < 0.05
  
  if (is_defective) {
    defective_units <- defective_units + 1
    cat(sprintf("Unit %d:  DEFECTIVE (Total defects: %d)\n", 
               tested_units, defective_units))
  }
  
  # Report every 100 tests
  if (tested_units %% 100 == 0) {
    cat(sprintf("Tested %d units, %d defects found\n", 
               tested_units, defective_units))
  }
}
Unit 6:  DEFECTIVE (Total defects: 1)
Unit 18:  DEFECTIVE (Total defects: 2)
Unit 35:  DEFECTIVE (Total defects: 3)
Unit 51:  DEFECTIVE (Total defects: 4)
Unit 74:  DEFECTIVE (Total defects: 5)
Tested 100 units, 5 defects found
Unit 143:  DEFECTIVE (Total defects: 6)
if (defective_units > max_defects_allowed) {
  cat(sprintf("\nBATCH REJECTED: Too many defects (%d)\n", defective_units))
} else {
  cat(sprintf("\nBATCH APPROVED: %d defects in %d units\n", 
             defective_units, tested_units))
}

BATCH REJECTED: Too many defects (6)

11.1.3 3. Nested Loops

Nested loops are loops within loops, useful for processing multi-dimensional data.

11.1.3.1 Example 1: Clinical Trial Multi-Site Analysis

# Clinical trial data:  sites and their patients
trial_sites <- list(
  Site_A = c("PT001", "PT002", "PT003"),
  Site_B = c("PT004", "PT005", "PT006", "PT007"),
  Site_C = c("PT008", "PT009")
)

# Adverse events per patient (simulated)
adverse_events <- list(
  PT001 = c("Headache", "Nausea"),
  PT002 = c("Fatigue"),
  PT003 = character(0),
  PT004 = c("Dizziness", "Nausea"),
  PT005 = c("Headache"),
  PT006 = c("Fatigue", "Insomnia"),
  PT007 = character(0),
  PT008 = c("Nausea"),
  PT009 = c("Headache", "Dizziness")
)

cat("Clinical Trial Adverse Events Summary:\n")
Clinical Trial Adverse Events Summary:
cat(strrep("=", 45), "\n")
============================================= 
total_patients <- 0
total_events <- 0

# Outer loop:  iterate through sites
for (site_name in names(trial_sites)) {
  patients <- trial_sites[[site_name]]
  cat(sprintf("\n%s:\n", site_name))
  cat(strrep("-", 20), "\n")
  site_events <- 0
  
  # Inner loop: iterate through patients at each site
  for (patient in patients) {
    patient_events <- adverse_events[[patient]]
    if (is.null(patient_events)) patient_events <- character(0)
    
    site_events <- site_events + length(patient_events)
    total_events <- total_events + length(patient_events)
    
    if (length(patient_events) > 0) {
      cat(sprintf("  %s: %s\n", patient, paste(patient_events, collapse = ", ")))
    } else {
      cat(sprintf("  %s: No adverse events\n", patient))
    }
  }
  
  total_patients <- total_patients + length(patients)
  cat(sprintf("  Site total: %d events in %d patients\n", 
             site_events, length(patients)))
}

Site_A:
-------------------- 
  PT001: Headache, Nausea
  PT002: Fatigue
  PT003: No adverse events
  Site total: 3 events in 3 patients

Site_B:
-------------------- 
  PT004: Dizziness, Nausea
  PT005: Headache
  PT006: Fatigue, Insomnia
  PT007: No adverse events
  Site total: 5 events in 4 patients

Site_C:
-------------------- 
  PT008: Nausea
  PT009: Headache, Dizziness
  Site total: 3 events in 2 patients
cat(sprintf("\nTrial Summary:\n"))

Trial Summary:
cat(sprintf("Total patients: %d\n", total_patients))
Total patients: 9
cat(sprintf("Total adverse events: %d\n", total_events))
Total adverse events: 11
cat(sprintf("Average events per patient: %.2f\n", total_events/total_patients))
Average events per patient: 1.22

11.1.3.2 Example 2: Drug Interaction Matrix

# Common drugs in cardiology
drugs <- c("Aspirin", "Metoprolol", "Lisinopril", "Atorvastatin", "Warfarin")

# Interaction severity (0=None, 1=Mild, 2=Moderate, 3=Severe)
interaction_matrix <- matrix(c(
  0, 1, 0, 1, 3,  # Aspirin
  1, 0, 2, 0, 1,  # Metoprolol
  0, 2, 0, 0, 2,  # Lisinopril
  1, 0, 0, 0, 1,  # Atorvastatin
  3, 1, 2, 1, 0   # Warfarin
), nrow = 5, byrow = TRUE)

severity_labels <- c("None", "Mild", "Moderate", "Severe")

cat("Drug Interaction Matrix:\n")
Drug Interaction Matrix:
cat(strrep("=", 50), "\n")
================================================== 
# Print header
cat(sprintf("%-12s", "Drug"))
Drug        
for (drug in drugs) {
  cat(sprintf("%-12s", drug))
}
Aspirin     Metoprolol  Lisinopril  AtorvastatinWarfarin    
cat("\n")
# Nested loops to print the matrix
for (i in seq_along(drugs)) {
  cat(sprintf("%-12s", drugs[i]))
  for (j in seq_along(drugs)) {
    severity <- interaction_matrix[i, j] + 1  # +1 for 1-based indexing
    label <- severity_labels[severity]
    cat(sprintf("%-12s", label))
  }
  cat("\n")
}
Aspirin     None        Mild        None        Mild        Severe      
Metoprolol  Mild        None        Moderate    None        Mild        
Lisinopril  None        Moderate    None        None        Moderate    
AtorvastatinMild        None        None        None        Mild        
Warfarin    Severe      Mild        Moderate    Mild        None        
# Find severe interactions
cat("\nSevere Drug Interactions:\n")

Severe Drug Interactions:
cat(strrep("-", 30), "\n")
------------------------------ 
for (i in 1:length(drugs)) {
  for (j in (i+1):length(drugs)) {  # Avoid duplicates
    if (j <= length(drugs) && interaction_matrix[i, j] == 3) {
      cat(sprintf("⚠️  %s + %s:  SEVERE\n", drugs[i], drugs[j]))
    }
  }
}
⚠️  Aspirin + Warfarin:  SEVERE

11.1.4 4. Loop Control Statements

11.1.4.1 Break Statement

Exits the loop prematurely when a condition is met.

# Finding the first patient with severe adverse event
patients_data <- data.frame(
  id = c("PT001", "PT002", "PT003", "PT004", "PT005"),
  severity = c("Mild", "Moderate", "Severe", "Mild", "Severe"),
  stringsAsFactors = FALSE
)

cat("Searching for first severe adverse event:\n")
Searching for first severe adverse event:
for (i in 1:nrow(patients_data)) {
  patient <- patients_data[i, ]
  cat(sprintf("Checking patient %s:  %s\n", patient$id, patient$severity))
  
  if (patient$severity == "Severe") {
    cat(sprintf("⚠️  ALERT: First severe AE found in patient %s\n", patient$id))
    break  # Exit loop immediately
  }
}
Checking patient PT001:  Mild
Checking patient PT002:  Moderate
Checking patient PT003:  Severe
⚠️  ALERT: First severe AE found in patient PT003

11.1.4.2 Next Statement (R equivalent of continue)

Skips the current iteration and moves to the next one.

# Processing only patients with complete data
patients_lab_values <- data.frame(
  id = c("PT001", "PT002", "PT003", "PT004", "PT005"),
  creatinine = c(1.2, NA, 0.9, 1.1, 1.3),
  alt = c(25, 30, NA, 28, 35),
  stringsAsFactors = FALSE
)

cat("Processing patients with complete lab data:\n")
Processing patients with complete lab data:
cat(strrep("-", 45), "\n")
--------------------------------------------- 
for (i in 1:nrow(patients_lab_values)) {
  patient <- patients_lab_values[i, ]
  
  # Skip patients with missing data
  if (is.na(patient$creatinine) || is.na(patient$alt)) {
    cat(sprintf("%s:  Skipping - incomplete data\n", patient$id))
    next  # Skip to next iteration
  }
  
  # Process patient with complete data
  creat <- patient$creatinine
  alt <- patient$alt
  
  # Calculate estimated GFR (simplified formula)
  egfr <- 175 * (creat^(-1.154))  # Simplified calculation
  
  cat(sprintf("%s:  Creatinine=%.1f, ALT=%.0f, eGFR=%.1f\n", 
             patient$id, creat, alt, egfr))
  
  # Check for abnormal values
  if (creat > 1.2) {
    cat("  ⚠️  Elevated creatinine\n")
  }
  if (alt > 40) {
    cat("  ⚠️  Elevated ALT\n")
  }
}
PT001:  Creatinine=1.2, ALT=25, eGFR=141.8
PT002:  Skipping - incomplete data
PT003:  Skipping - incomplete data
PT004:  Creatinine=1.1, ALT=28, eGFR=156.8
PT005:  Creatinine=1.3, ALT=35, eGFR=129.3
  ⚠️  Elevated creatinine

11.1.5 5. Apply Functions (R Alternative to Loops)

R’s apply family functions often provide more efficient alternatives to loops.

11.1.5.1 Example 1: BMI Calculations using lapply

# Patient data as list
patients_list <- list(
  list(weight = 70, height = 1.75),
  list(weight = 65, height = 1.68),
  list(weight = 80, height = 1.82),
  list(weight = 55, height = 1.60),
  list(weight = 90, height = 1.78)
)

# Function to calculate BMI
calculate_bmi <- function(patient) {
  bmi <- patient$weight / (patient$height^2)
  category <- if (bmi < 18.5) "Underweight" 
              else if (bmi < 25) "Normal"
              else if (bmi < 30) "Overweight" 
              else "Obese"
  
  return(list(bmi = bmi, category = category))
}

# Calculate BMI for all patients using lapply
bmi_results <- lapply(patients_list, calculate_bmi)

cat("Patient BMI Analysis:\n")
Patient BMI Analysis:
cat(strrep("-", 25), "\n")
------------------------- 
for (i in seq_along(patients_list)) {
  patient <- patients_list[[i]]
  result <- bmi_results[[i]]
  
  cat(sprintf("Patient %d: %. 0fkg, %. 2fm → BMI: %.1f (%s)\n", 
             i, patient$weight, patient$height, 
             result$bmi, result$category))
}
Patient 1: %.0 0fkg, %.0 2fm → BMI: 22.9 (Normal)
Patient 2: %.0 0fkg, %.0 2fm → BMI: 23.0 (Normal)
Patient 3: %.0 0fkg, %.0 2fm → BMI: 24.2 (Normal)
Patient 4: %.0 0fkg, %.0 2fm → BMI: 21.5 (Normal)
Patient 5: %.0 0fkg, %.0 2fm → BMI: 28.4 (Overweight)

11.1.5.2 Example 2: Dose Adjustments using sapply

# Base doses for different age groups
patient_ages <- c(25, 45, 65, 75, 85)
base_dose <- 100  # mg

# Function for age-adjusted dosing
adjust_dose <- function(age, base_dose = 100) {
  if (age > 65) {
    # Reduce by 1% for every year over 65
    adjustment_factor <- 1 - (age - 65) * 0.01
    return(base_dose * adjustment_factor)
  } else {
    return(base_dose)
  }
}

# Calculate adjusted doses using sapply
adjusted_doses <- sapply(patient_ages, adjust_dose, base_dose = base_dose)

cat("Age-Adjusted Dosing:\n")
Age-Adjusted Dosing:
cat(strrep("-", 25), "\n")
------------------------- 
for (i in seq_along(patient_ages)) {
  age <- patient_ages[i]
  base <- base_dose
  adjusted <- adjusted_doses[i]
  adjustment <- ((adjusted - base) / base) * 100
  
  cat(sprintf("Age %d: %dmg → %. 1fmg (%+.1f%%)\n", 
             age, base, adjusted, adjustment))
}
Age 25: 100mg → %.0 1fmg (+0.0%)
Age 45: 100mg → %.0 1fmg (+0.0%)
Age 65: 100mg → %.0 1fmg (+0.0%)
Age 75: 100mg → %.0 1fmg (-10.0%)
Age 85: 100mg → %.0 1fmg (-20.0%)

11.2 Advanced Loop Patterns in R Programming

11.2.1 1. Data Validation with Comprehensive Checks

validate_patient_data <- function(patients_df) {
  required_fields <- c('id', 'age', 'weight', 'height')
  errors <- character(0)
  valid_rows <- logical(nrow(patients_df))
  
  for (i in 1:nrow(patients_df)) {
    patient <- patients_df[i, ]
    patient_errors <- character(0)
    
    # Check required fields
    for (field in required_fields) {
      if (is.na(patient[[field]]) || is.null(patient[[field]])) {
        patient_errors <- c(patient_errors, paste("Missing", field))
      }
    }
    
    # Validate data ranges
    if (! is.na(patient$age)) {
      if (patient$age < 18 || patient$age > 100) {
        patient_errors <- c(patient_errors, 
                           paste("Invalid age:", patient$age))
      }
    }
    
    if (!is.na(patient$weight)) {
      if (patient$weight < 30 || patient$weight > 300) {
        patient_errors <- c(patient_errors, 
                           paste("Invalid weight:", patient$weight, "kg"))
      }
    }
    
    if (!is.na(patient$height)) {
      if (patient$height < 1.0 || patient$height > 2.5) {
        patient_errors <- c(patient_errors, 
                           paste("Invalid height:", patient$height, "m"))
      }
    }
    
    if (length(patient_errors) > 0) {
      error_msg <- sprintf("Patient %d (%s): %s", 
                          i, 
                          ifelse(is.na(patient$id), "Unknown", patient$id),
                          paste(patient_errors, collapse = ", "))
      errors <- c(errors, error_msg)
      valid_rows[i] <- FALSE
    } else {
      valid_rows[i] <- TRUE
    }
  }
  
  return(list(
    valid_patients = patients_df[valid_rows, , drop = FALSE],
    errors = errors
  ))
}

# Example usage
test_patients <- data.frame(
  id = c('PT001', 'PT002', 'PT003', 'PT004'),
  age = c(45, 150, NA, 35),  # PT002 has invalid age, PT003 missing age
  weight = c(70, 65, 80, 55),
  height = c(1.75, 1.68, 1.82, NA),  # PT004 missing height
  stringsAsFactors = FALSE
)

validation_result <- validate_patient_data(test_patients)

cat("Data Validation Results:\n")
Data Validation Results:
cat(strrep("=", 30), "\n")
============================== 
cat(sprintf("Valid patients: %d\n", nrow(validation_result$valid_patients)))
Valid patients: 1
cat(sprintf("Invalid patients: %d\n", length(validation_result$errors)))
Invalid patients: 3
if (length(validation_result$errors) > 0) {
  cat("\nValidation Errors:\n")
  for (error in validation_result$errors) {
    cat(sprintf("❌ %s\n", error))
  }
}

Validation Errors:
❌ Patient 2 (PT002): Invalid age: 150
❌ Patient 3 (PT003): Missing age
❌ Patient 4 (PT004): Missing height
if (nrow(validation_result$valid_patients) > 0) {
  cat("\nValid Patients:\n")
  print(validation_result$valid_patients)
}

Valid Patients:
     id age weight height
1 PT001  45     70   1.75

11.2.2 2. Pharmacokinetic Simulation with Loops

simulate_drug_concentration <- function(dose, ka, ke, vd, time_points) {
  # Simulate drug concentration over time using one-compartment model
  # dose: dose in mg
  # ka: absorption rate constant (1/h)
  # ke: elimination rate constant (1/h)
  # vd: volume of distribution (L)
  
  concentrations <- numeric(length(time_points))
  
  for (i in seq_along(time_points)) {
    t <- time_points[i]
    
    if (ka != ke) {
      # One-compartment model with first-order absorption
      conc <- (dose/vd) * (ka/(ka-ke)) * (exp(-ke*t) - exp(-ka*t))
    } else {
      # Special case when ka = ke
      conc <- (dose/vd) * ka * t * exp(-ke*t)
    }
    
    concentrations[i] <- max(0, conc)  # Concentration can't be negative
  }
  
  return(concentrations)
}

# Simulation parameters
dose <- 500  # mg
ka <- 1.5    # absorption rate (1/h)
ke <- 0.2    # elimination rate (1/h)
vd <- 50     # volume of distribution (L)

time_points <- seq(0, 24, by = 0.5)  # 0 to 24 hours, every 0.5h

concentrations <- simulate_drug_concentration(dose, ka, ke, vd, time_points)

cat("Pharmacokinetic Simulation:\n")
Pharmacokinetic Simulation:
cat(strrep("=", 35), "\n")
=================================== 
cat(sprintf("Dose: %dmg\n", dose))
Dose: 500mg
cat(sprintf("Absorption rate constant: %. 1f/h\n", ka))
Absorption rate constant: %.0 1f/h
cat(sprintf("Elimination rate constant: %.1f/h\n", ke))
Elimination rate constant: 0.2/h
cat(sprintf("Volume of distribution: %dL\n", vd))
Volume of distribution: 50L
cat("\n")
# Find key pharmacokinetic parameters
max_conc <- max(concentrations)
max_time <- time_points[which.max(concentrations)]
half_life <- log(2) / ke

cat(sprintf("Maximum concentration (Cmax): %.2f mg/L\n", max_conc))
Maximum concentration (Cmax): 7.33 mg/L
cat(sprintf("Time to maximum (Tmax): %.1f hours\n", max_time))
Time to maximum (Tmax): 1.5 hours
cat(sprintf("Elimination half-life:  %.2f hours\n", half_life))
Elimination half-life:  3.47 hours
# Print concentration at key time points
key_times <- c(0, 0.5, 1, 2, 4, 8, 12, 24)
cat("\nConcentration at key time points:\n")

Concentration at key time points:
for (t in key_times) {
  # Find closest time point
  closest_idx <- which.min(abs(time_points - t))
  actual_time <- time_points[closest_idx]
  conc <- concentrations[closest_idx]
  cat(sprintf("T = %4.1fh: %6.2f mg/L\n", actual_time, conc))
}
T =  0.0h:   0.00 mg/L
T =  0.5h:   4.99 mg/L
T =  1.0h:   6.87 mg/L
T =  2.0h:   7.16 mg/L
T =  4.0h:   5.16 mg/L
T =  8.0h:   2.33 mg/L
T = 12.0h:   1.05 mg/L
T = 24.0h:   0.09 mg/L

11.2.3 3. Clinical Trial Enrollment Simulation

# Set seed for reproducibility
set.seed(456)

simulate_trial_enrollment <- function(target_enrollment, 
                                    sites, 
                                    max_weeks = 52) {
  # Initialize tracking variables
  total_enrolled <- 0
  week <- 0
  enrollment_log <- data.frame(
    week = integer(0),
    site = character(0),
    patients_enrolled = integer(0),
    cumulative_enrolled = integer(0),
    stringsAsFactors = FALSE
  )
  
  cat("Clinical Trial Enrollment Simulation\n")
  cat(strrep("=", 40), "\n")
  cat(sprintf("Target enrollment: %d patients\n", target_enrollment))
  cat(sprintf("Number of sites: %d\n", length(sites)))
  cat("\n")
  
  while (total_enrolled < target_enrollment && week < max_weeks) {
    week <- week + 1
    week_enrolled <- 0
    
    # Each site attempts to enroll patients
    for (site in names(sites)) {
      # Random enrollment based on site capacity
      site_capacity <- sites[[site]]
      enrolled_this_week <- rpois(1, lambda = site_capacity)
      
      if (enrolled_this_week > 0) {
        total_enrolled <- total_enrolled + enrolled_this_week
        week_enrolled <- week_enrolled + enrolled_this_week
        
        # Log the enrollment
        new_row <- data.frame(
          week = week,
          site = site,
          patients_enrolled = enrolled_this_week,
          cumulative_enrolled = total_enrolled,
          stringsAsFactors = FALSE
        )
        enrollment_log <- rbind(enrollment_log, new_row)
        
        # Stop if target reached
        if (total_enrolled >= target_enrollment) {
          break
        }
      }
    }
    
    # Report progress every 4 weeks
    if (week %% 4 == 0) {
      cat(sprintf("Week %d: %d patients enrolled (Total: %d/%.0f%%)\n", 
                 week, week_enrolled, total_enrolled,
                 (total_enrolled/target_enrollment)*100))
    }
  }
  
  cat("\n")
  if (total_enrolled >= target_enrollment) {
    cat(sprintf("✅ Target enrollment reached in week %d!\n", week))
  } else {
    cat(sprintf("⚠️  Enrollment incomplete after %d weeks (%d/%d patients)\n", 
               max_weeks, total_enrolled, target_enrollment))
  }
  
  return(enrollment_log)
}

# Define sites with their enrollment capacity (patients per week on average)
trial_sites <- list(
  "Site_A" = 2.5,  # Academic medical center
  "Site_B" = 1.8,  # Community hospital
  "Site_C" = 3.1,  # Large clinical research center
  "Site_D" = 1.2,  # Small clinic
  "Site_E" = 2.0   # Regional hospital
)

# Run simulation
enrollment_log <- simulate_trial_enrollment(
  target_enrollment = 100,
  sites = trial_sites,
  max_weeks = 30
)
Clinical Trial Enrollment Simulation
======================================== 
Target enrollment: 100 patients
Number of sites: 5

Week 4: 12 patients enrolled (Total: 39/39%)
Week 8: 21 patients enrolled (Total: 100/100%)

✅ Target enrollment reached in week 8!
# Summary by site
if (nrow(enrollment_log) > 0) {
  cat("\nEnrollment Summary by Site:\n")
  cat(strrep("-", 30), "\n")
  
  for (site in unique(enrollment_log$site)) {
    site_data <- enrollment_log[enrollment_log$site == site, ]
    total_by_site <- sum(site_data$patients_enrolled)
    weeks_active <- nrow(site_data)
    
    cat(sprintf("%s: %d patients (%d weeks active)\n", 
               site, total_by_site, weeks_active))
  }
}

Enrollment Summary by Site:
------------------------------ 
Site_A: 23 patients (8 weeks active)
Site_B: 17 patients (7 weeks active)
Site_C: 34 patients (8 weeks active)
Site_D: 11 patients (6 weeks active)
Site_E: 15 patients (8 weeks active)

11.3 Best Practices for Loops in Pharmaceutical R Programming

11.3.1 1. Vectorization vs Loops

# Example:  Calculating dose adjustments

# Sample data
patient_weights <- c(60, 75, 80, 65, 90, 55, 70, 85)
base_dose_mg_per_kg <- 5

cat("Comparison:  Loop vs Vectorized Operations\n")
Comparison:  Loop vs Vectorized Operations
cat(strrep("=", 45), "\n")
============================================= 
# Method 1: Using a loop (less efficient)
cat("Method 1: Using for loop\n")
Method 1: Using for loop
doses_loop <- numeric(length(patient_weights))

start_time <- Sys.time()
for (i in seq_along(patient_weights)) {
  doses_loop[i] <- patient_weights[i] * base_dose_mg_per_kg
}
loop_time <- Sys.time() - start_time

cat(sprintf("Loop result: %s\n", paste(doses_loop, collapse = ", ")))
Loop result: 300, 375, 400, 325, 450, 275, 350, 425
cat(sprintf("Time taken: %.6f seconds\n\n", as.numeric(loop_time)))
Time taken: 0.002595 seconds
# Method 2: Using vectorization (more efficient)
cat("Method 2: Using vectorization\n")
Method 2: Using vectorization
start_time <- Sys.time()
doses_vectorized <- patient_weights * base_dose_mg_per_kg
vector_time <- Sys.time() - start_time

cat(sprintf("Vectorized result: %s\n", paste(doses_vectorized, collapse = ", ")))
Vectorized result: 300, 375, 400, 325, 450, 275, 350, 425
cat(sprintf("Time taken: %.6f seconds\n", as.numeric(vector_time)))
Time taken: 0.000623 seconds
# Verify results are identical
cat(sprintf("Results identical: %s\n", identical(doses_loop, doses_vectorized)))
Results identical: TRUE

11.3.2 2. Error Handling in Loops

process_lab_results_safe <- function(results) {
  # Process lab results with proper error handling
  
  if (length(results) == 0) {
    cat("⚠️  No lab results to process\n")
    return(invisible(NULL))
  }
  
  processed_count <- 0
  errors_count <- 0
  
  for (i in seq_along(results)) {
    # Use tryCatch for error handling
    result <- tryCatch({
      current_result <- results[[i]]
      
      # Validate structure
      if (!is.list(current_result)) {
        stop("Result must be a list")
      }
      
      if (!all(c('value', 'reference_min', 'reference_max') %in% names(current_result))) {
        stop("Missing required fields")
      }
      
      # Extract values
      value <- current_result$value
      ref_min <- current_result$reference_min
      ref_max <- current_result$reference_max
      
      # Validate values are numeric
      if (!is.numeric(value) || !is.numeric(ref_min) || !is.numeric(ref_max)) {
        stop("Values must be numeric")
      }
      
      # Determine status
      status <- if (value < ref_min) "Low" 
               else if (value > ref_max) "High" 
               else "Normal"
      
      cat(sprintf("Result %d: %. 1f (Ref: %.1f-%.1f) - %s\n", 
                 i, value, ref_min, ref_max, status))
      
      processed_count <<- processed_count + 1
      "SUCCESS"
      
    }, error = function(e) {
      cat(sprintf("❌ Error processing result %d:  %s\n", i, e$message))
      errors_count <<- errors_count + 1
      "ERROR"
    })
  }
  
  cat(sprintf("\nProcessing Summary:\n"))
  cat(sprintf("Successfully processed:  %d\n", processed_count))
  cat(sprintf("Errors encountered: %d\n", errors_count))
}

# Example usage with mixed valid/invalid data
lab_data <- list(
  list(value = 4.5, reference_min = 3.5, reference_max = 5.5),
  list(value = 2.1, reference_min = 3.5, reference_max = 5.5),  # Low
  list(value = 6.8, reference_min = 3.5, reference_max = 5.5),  # High
  list(value = "invalid"),  # Invalid format
  list(value = 4.2, reference_min = 3.5, reference_max = 5.5),
  list(value = 3.8)  # Missing reference values
)

process_lab_results_safe(lab_data)
Result 1: %.0 1f (Ref: 3.5-5.5) - Normal
Result 2: %.0 1f (Ref: 3.5-5.5) - Low
Result 3: %.0 1f (Ref: 3.5-5.5) - High
❌ Error processing result 4:  Missing required fields
Result 5: %.0 1f (Ref: 3.5-5.5) - Normal
❌ Error processing result 6:  Missing required fields

Processing Summary:
Successfully processed:  0
Errors encountered: 2

11.3.3 3. Progress Tracking for Long Operations

analyze_large_dataset <- function(dataset, batch_size = 1000) {
  # Analyze large dataset with progress indication
  
  total_records <- length(dataset)
  processed <- 0
  
  cat(sprintf("Processing %d records in batches of %d.. .\n", 
             total_records, batch_size))
  cat(strrep("-", 50), "\n")
  
  # Process in batches
  for (start_idx in seq(1, total_records, batch_size)) {
    end_idx <- min(start_idx + batch_size - 1, total_records)
    batch <- dataset[start_idx:end_idx]
    
    # Simulate processing each record in batch
    for (i in seq_along(batch)) {
      # Simulate some processing time
      Sys.sleep(0.001)  # Very short delay for demonstration
      processed <- processed + 1
    }
    
    # Show progress
    progress <- (processed / total_records) * 100
    cat(sprintf("Progress: %d/%d (%.1f%%) - Batch %d complete\n", 
               processed, total_records, progress,
               ceiling(start_idx / batch_size)))
  }
  
  cat("✅ Analysis complete!\n")
  
  return(processed)
}

# Example with simulated dataset (smaller for demonstration)
set.seed(789)
sample_dataset <- replicate(5000, list(
  id = paste0("PT", sprintf("%04d", sample(1:9999, 1))),
  value = rnorm(1, 100, 15)
), simplify = FALSE)

# Process the dataset
# processed_count <- analyze_large_dataset(sample_dataset, batch_size = 1000)

11.4 Summary

Loops are essential tools in pharmaceutical R programming for:

  • Data Processing: Iterating through patient records, lab results, and clinical data
  • Calculations: Computing dosages, pharmacokinetic parameters, and statistical measures
  • Quality Control: Validating data integrity and checking compliance
  • Simulations: Modeling drug behavior and treatment outcomes
  • Analysis: Processing large datasets and generating reports

11.4.1 Key Takeaways for R:

  1. Choose the right approach: Consider vectorization before loops
  2. Use apply functions when appropriate for better performance
  3. Always validate data before processing
  4. Include error handling with tryCatch() for robust code
  5. Add progress indicators for long-running operations
  6. Use meaningful variable names for clarity
  7. Consider data. frame operations instead of loops when possible

11.4.2 R-Specific Best Practices:

  • Vectorization is usually faster than loops in R
  • Pre-allocate vectors/lists to improve performance
  • Use lapply(), sapply(), mapply() for functional programming
  • Avoid growing objects inside loops (use indexing instead)
  • Consider data.table for very large datasets

Remember that efficient loop design and choosing the right approach (loops vs vectorization vs apply functions) can significantly impact the performance and reliability of pharmaceutical data analysis systems in R.