R — Getting Started

R Programming Tutorials

Welcome! This is not just code snippets. You’ll learn R the way professionals do: by understanding why we use each concept, with real-world stories, before writing a single line of code.

Whether you’re a clinical programmer, SAS expert, statistician, or complete beginner—this tutorial is designed for you.


1. Introduction to R: Why Does It Matter?

The Story

Imagine you’re a clinical researcher with patient data from 5,000 subjects. You need to: - Clean messy data (missing values, typos, wrong formats) - Calculate statistics (mean, median, confidence intervals) - Create visualizations (plots, charts, tables) - Generate a report with all results

In SAS: You’d write 50+ lines of PROC code, manage libraries, and wait for compilation.

In R: You load a dataset, clean it, analyze it, and visualize it—all in 20 lines. The results appear instantly.

R is a language designed by statisticians FOR statisticians. It’s like having a super-smart assistant who: - Understands data intuitively - Can do complex math instantly - Creates beautiful visualizations - Scales from small datasets to millions of rows - Works on any device (Windows, Mac, Linux)

Why clinicians and SAS programmers switch to R

Task SAS R
Load CSV 15 lines 1 line
Calculate summary stats 10 lines 1 line
Create a plot 20 lines 2 lines
Manipulate data 25 lines 3 lines
Share code Email .sas files Share one .R file

Getting Started

Installation is simple: 1. Download R from r-project.org 2. Download RStudio (free) from posit.co — it makes R much easier 3. Open RStudio and you’re ready

First command:

print("Hello, R!")  # This prints to the console

That’s it! R is waiting for your instructions.


2. Variables & Operators: Labeled Boxes for Your Data

The Concept: What is a Variable?

Think of a variable like a labeled box in a warehouse: - You label it: salary - You put a value in it: 50000 - Later, you can open that box and use what’s inside: salary * 1.1 (10% raise)

Real-World Story: A Clinical Example

You’re a clinical programmer analyzing patient lab values. You have: - Patient age: 45 years - Weight: 85 kg - Drug dose: 250 mg

Instead of repeating these numbers everywhere, you store them in variables:

patient_age <- 45
patient_weight <- 85
drug_dose <- 250

# Now calculate dose per kg
dose_per_kg <- drug_dose / patient_weight
print(dose_per_kg)  # Output: 2.94 mg/kg

Why this matters: If the dose changes from 250 to 300, you only change ONE number. The calculation updates automatically.

Operators: What can you do with variables?

Arithmetic Operators (Math)

Operator Meaning Example Result
+ Add 10 + 5 15
- Subtract 10 - 5 5
* Multiply 10 * 5 50
/ Divide 10 / 5 2
^ Power 2 ^ 3 8
%% Remainder 10 %% 3 1

Comparison Operators (Is this true or false?)

Operator Meaning Example Result
== Equal to? 10 == 10 TRUE
!= Not equal? 10 != 5 TRUE
> Greater than? 10 > 5 TRUE
< Less than? 10 < 5 FALSE
>= Greater or equal? 10 >= 10 TRUE
<= Less or equal? 5 <= 10 TRUE

Practical Hospital Example

A hospital tracks: - Patient hemoglobin: 8.5 g/dL - Normal range: 12-16 g/dL (for adult males)

hemoglobin <- 8.5
is_anemic <- hemoglobin < 12
print(is_anemic)  # TRUE (patient needs blood transfusion)

# Calculate how much higher it needs to be
needed_increase <- 12 - hemoglobin
print(needed_increase)  # 3.5 g/dL

This is how clinical algorithms work: compare, calculate, decide.


3. Data Types: Different Kinds of Information

The Concept

Just as you wouldn’t store a patient’s name in the same way as their age, R has different data types for different kinds of information.

The Four Main Types

1. Numeric (Numbers with decimals)

height <- 175.5  # Patient height in cm
weight <- 82.3   # Patient weight in kg
BMI <- weight / (height/100)^2
print(BMI)  # 26.8

When to use: Measurements, calculations, weights, heights, lab values, temperatures

2. Integer (Whole numbers)

num_patients <- 156  # Exact count
num_visits <- 3
year_of_birth <- 1980L  # The 'L' forces integer type

When to use: Counts, IDs, years, frequencies

3. Character (Text/Strings)

patient_name <- "John Smith"
diagnosis <- "Type 2 Diabetes"
hospital <- 'Mayo Clinic'  # Single or double quotes both work

When to use: Names, diagnoses, categories, notes, addresses

4. Logical (True/False)

is_diabetic <- TRUE
requires_insulin <- FALSE

When to use: Yes/no questions, eligibility checks, flags

Real-World Clinical Dataset Example

A clinical trial tracks different information:

# Patient ID 001
patient_id <- 1001              # Integer: unique identifier
patient_name <- "Alice Johnson" # Character: person's name
age <- 52.7                     # Numeric: age in years
has_hypertension <- TRUE        # Logical: yes/no flag
systolic_bp <- 145              # Integer: blood pressure
diastolic_bp <- 92.5            # Numeric: blood pressure
treatment_group <- "Drug_A"     # Character: category
enrolled_date <- "2024-01-15"   # Character: date as text (we'll do better later)

Check What Type Your Data Is

R has a helper function to tell you:

class(52.7)           # "numeric"
class(1001)           # "numeric" (even though it's a whole number!)
class(1001L)          # "integer" (because of the L)
class("Alice")        # "character"
class(TRUE)           # "logical"

4. Vectors: Your First Data Container

The Concept: A List of Items (All the Same Type)

A vector is like a shopping list: - All items are groceries (same type) - Each item is separate but in the same list - You can count them, sort them, or find specific ones

Think of patient data from a clinical trial:

# Cholesterol levels for 5 patients
cholesterol <- c(210, 195, 240, 185, 220)

That’s a vector of 5 numbers.

Real Hospital Example: A Patient’s Lab Values Over Time

A diabetic patient has their glucose measured weekly:

# Glucose readings (mg/dL) over 6 weeks
week_1 <- 245
week_2 <- 238
week_3 <- 225
week_4 <- 212
week_5 <- 198
week_6 <- 185

# OLD WAY (tedious):
avg <- (week_1 + week_2 + week_3 + week_4 + week_5 + week_6) / 6

# NEW WAY (using a vector):
glucose_readings <- c(245, 238, 225, 212, 198, 185)
avg <- mean(glucose_readings)  # Automatic!
print(avg)  # 217.17

What Can You Do With Vectors?

Get statistics instantly

cholesterol <- c(210, 195, 240, 185, 220, 205)

mean(cholesterol)      # Average: 209.17 (normal for many)
median(cholesterol)    # Middle value: 212.5
sd(cholesterol)        # How spread out: 19.89
min(cholesterol)       # Lowest: 185
max(cholesterol)       # Highest: 240
sum(cholesterol)       # Total: 1255
length(cholesterol)    # How many: 6

Get specific items

cholesterol <- c(210, 195, 240, 185, 220, 205)

cholesterol[1]        # First patient: 210
cholesterol[3]        # Third patient: 240
cholesterol[c(1,3)]   # First AND third: 210, 240
cholesterol[-2]       # Everyone EXCEPT second: 210, 240, 185, 220, 205

Filter by condition

high_cholesterol <- cholesterol > 200
print(high_cholesterol)  # TRUE TRUE TRUE FALSE TRUE TRUE

# Get the actual values
cholesterol[cholesterol > 200]  # 210, 240, 220, 205

Real Clinical Use Case: Finding High-Risk Patients

# Blood pressure readings for 8 patients
systolic <- c(120, 145, 138, 148, 110, 155, 142, 130)

# Who has stage 2 hypertension (>140)?
high_bp <- systolic > 140
print(high_bp)  # FALSE TRUE FALSE TRUE FALSE TRUE TRUE FALSE

# How many patients are at risk?
num_at_risk <- sum(high_bp)  # Counts TRUE values
print(num_at_risk)  # 3 patients need intervention

# What are their exact readings?
risky_readings <- systolic[systolic > 140]
print(risky_readings)  # 145, 148, 155, 142

This is how clinical alerts work—R automatically finds the patients who need attention!


5. Data Frames: Spreadsheets in R

The Concept: Organizing Data Like Excel

A data frame is like an Excel spreadsheet: - Rows = individual records (patients, visits, observations) - Columns = variables (name, age, diagnosis, treatment) - Different columns can have different types (numbers, text, dates)

Real Clinical Research Example

You’re conducting a diabetes study tracking:

# Create a dataset of 4 patients
patients <- data.frame(
  patient_id = c(1001, 1002, 1003, 1004),
  name = c("Alice", "Bob", "Charlie", "Diana"),
  age = c(52, 45, 58, 50),
  glucose_baseline = c(245, 198, 310, 225),
  treatment = c("Drug_A", "Drug_B", "Drug_A", "Placebo"),
  improved = c(TRUE, FALSE, TRUE, FALSE)
)

# View the full dataset
print(patients)

Output:

  patient_id    name age glucose_baseline treatment improved
1        1001   Alice  52              245    Drug_A     TRUE
2        1002     Bob  45              198    Drug_B    FALSE
3        1003 Charlie  58              310    Drug_A     TRUE
4        1004   Diana  50              225   Placebo    FALSE

Access Your Data Like a Pro

# Get one column
patients$name              # "Alice" "Bob" "Charlie" "Diana"

# Get specific rows
patients[1, ]              # First patient's entire row
patients[c(1,3), ]         # First AND third patient

# Get specific cell
patients[2, 3]             # Bob's age: 45

# Logical indexing (find all improved patients)
improved_patients <- patients[patients$improved == TRUE, ]

# Calculate average baseline glucose
avg_glucose <- mean(patients$glucose_baseline)
print(avg_glucose)  # 244.5 mg/dL

Real Hospital Workflow

# A hospital has 3 patients in a medication trial
trial_data <- data.frame(
  patient = c("P001", "P002", "P003"),
  medication = c("Aspirin_100mg", "Aspirin_100mg", "Placebo"),
  baseline_heart_rate = c(72, 88, 75),
  day_7_heart_rate = c(70, 85, 76),
  adverse_events = c("None", "Headache", "None")
)

# Question: Did Aspirin patients improve?
aspirin_group <- trial_data[trial_data$medication == "Aspirin_100mg", ]

# Calculate average improvement
baseline_avg <- mean(aspirin_group$baseline_heart_rate)
day7_avg <- mean(aspirin_group$day_7_heart_rate)
improvement <- baseline_avg - day7_avg

print(paste("Aspirin patients improved by", round(improvement, 1), "bpm"))
# Output: "Aspirin patients improved by 3.5 bpm"

6. Functions: Reusable Instructions

The Concept: A Recipe or Machine

A function is like a recipe or vending machine: - Input: Ingredients (or coins) - Process: Follow instructions (or mechanism) - Output: Result (meal or snack)

Why Functions Matter in Clinical Research

Instead of writing the same calculation 50 times:

# OLD (repetitive, error-prone)
result1 <- (value1 - min_val) / (max_val - min_val)
result2 <- (value2 - min_val) / (max_val - min_val)
result3 <- (value3 - min_val) / (max_val - min_val)
# ... repeated 50 more times!

Write it ONCE as a function:

# NEW (clean, reusable)
normalize_value <- function(value, min_val, max_val) {
  result <- (value - min_val) / (max_val - min_val)
  return(result)
}

# Now use it 50 times easily
result1 <- normalize_value(value1, 0, 100)
result2 <- normalize_value(value2, 0, 100)
result3 <- normalize_value(value3, 0, 100)

Real Clinical Example: Calculate BMI

# BMI = weight (kg) / (height (m))^2
# Instead of calculating this for every patient...

calculate_bmi <- function(weight_kg, height_cm) {
  height_m <- height_cm / 100
  bmi <- weight_kg / (height_m ^ 2)
  return(bmi)
}

# Now use it for any patient
bmi_alice <- calculate_bmi(75, 170)     # 26.0
bmi_bob <- calculate_bmi(82, 175)       # 26.8
bmi_charlie <- calculate_bmi(95, 180)   # 29.3

print(bmi_alice)    # 26.0
print(bmi_bob)      # 26.8
print(bmi_charlie)  # 29.3

Function with Default Values

In clinical trials, you often use the same dosing formula:

# Default: Adult dosing
calculate_dose <- function(patient_weight, dose_per_kg = 5) {
  total_dose <- patient_weight * dose_per_kg
  return(total_dose)
}

# Adult (75 kg): standard dose is 375 mg
adult_dose <- calculate_dose(75)  # Uses default 5 mg/kg
print(adult_dose)  # 375

# Child (30 kg): might need different dose
child_dose <- calculate_dose(30, dose_per_kg = 3)  # Custom 3 mg/kg
print(child_dose)  # 90

7. Control Flow: Making Decisions

The Concept: If-Then Logic

Programs need to make decisions like a nurse: - IF patient temperature > 38°C, THEN alert doctor - IF glucose < 70 mg/dL, THEN give sugar tablet - IF medication available, THEN prescribe it

Real Hospital Alert System

patient_temp <- 39.2  # Celsius

if (patient_temp > 38.5) {
  print("ALERT: Fever detected! Notify physician!")
} else if (patient_temp > 37.5) {
  print("WARNING: Slight elevation. Monitor closely.")
} else {
  print("Normal temperature.")
}

# Output: "ALERT: Fever detected! Notify physician!"

Clinical Decision Tree Example

# Diabetes risk assessment
age <- 52
fasting_glucose <- 245
family_history <- TRUE

if (fasting_glucose > 200 & age > 45) {
  risk <- "High"
} else if (fasting_glucose > 150) {
  risk <- "Moderate"
} else {
  risk <- "Low"
}

print(paste("Risk level:", risk))
# Output: "Risk level: High"

Switch Statement (When You Have Many Options)

# Lab test result classification
test_result <- 8.5  # Hemoglobin g/dL

if (test_result < 7) {
  severity <- "Critical"
} else if (test_result < 8) {
  severity <- "Severe"
} else if (test_result < 10) {
  severity <- "Moderate"
} else if (test_result < 12) {
  severity <- "Mild"
} else {
  severity <- "Normal"
}

# OR use switch with intervals:
get_severity <- function(hgb) {
  if (hgb < 7) return("Critical")
  if (hgb < 8) return("Severe")
  if (hgb < 10) return("Moderate")
  if (hgb < 12) return("Mild")
  return("Normal")
}

print(get_severity(8.5))  # "Moderate"

8. Loops: Repeat Tasks Efficiently

The Concept: Do Something Over and Over

A loop is like reading a checklist: - “Check blood pressure for patient 1” - “Check blood pressure for patient 2” - …repeat for all 100 patients

Real Clinical Example: Patient Screening

You need to check 5 patients’ lab results:

# WITHOUT loop (tedious)
glucose_1 <- 245
status_1 <- if (glucose_1 > 200) "At Risk" else "Normal"

glucose_2 <- 198
status_2 <- if (glucose_2 > 200) "At Risk" else "Normal"

glucose_3 <- 310
status_3 <- if (glucose_3 > 200) "At Risk" else "Normal"
# ... repeat 47 more times for 50 patients!

# WITH loop (clean)
glucose_readings <- c(245, 198, 310, 185, 225)

for (glucose in glucose_readings) {
  if (glucose > 200) {
    print(paste("Glucose:", glucose, "→ AT RISK"))
  } else {
    print(paste("Glucose:", glucose, "→ Normal"))
  }
}

# Output:
# Glucose: 245 → AT RISK
# Glucose: 198 → Normal
# Glucose: 310 → AT RISK
# Glucose: 185 → Normal
# Glucose: 225 → AT RISK

Loop Over Rows in a Data Frame

# Check all patients in a clinical trial
trial <- data.frame(
  patient = c("P001", "P002", "P003"),
  bmi = c(24.5, 32.1, 26.8)
)

for (i in 1:nrow(trial)) {
  patient_id <- trial[i, "patient"]
  bmi_value <- trial[i, "bmi"]
  
  if (bmi_value > 30) {
    print(paste(patient_id, "is obese (BMI =", bmi_value, ")"))
  } else {
    print(paste(patient_id, "is normal (BMI =", bmi_value, ")"))
  }
}

# Output:
# P001 is normal (BMI = 24.5 )
# P002 is obese (BMI = 32.1 )
# P003 is normal (BMI = 26.8 )

9. String Handling: Working With Text

The Concept: Text Manipulation

Clinical data often includes notes, diagnoses, and comments. R can: - Combine text (concatenate) - Extract parts of text - Search for patterns - Replace words

Real Clinical Notes Example

first_name <- "John"
last_name <- "Smith"
diagnosis <- "Type 2 Diabetes"

# Combine text
full_note <- paste(first_name, last_name, "diagnosed with", diagnosis)
print(full_note)
# Output: "John Smith diagnosed with Type 2 Diabetes"

# Extract parts
text <- "Patient: John Smith, Age: 52, Diagnosis: HTN"
name_part <- substr(text, 9, 19)  # Extract "John Smith"
print(name_part)

# Replace text
original <- "The patient has diabtes"  # Typo
corrected <- gsub("diabtes", "diabetes", original)
print(corrected)
# Output: "The patient has diabetes"

Clinical Research Example: Processing Lab Reports

# Lab report text
report <- "WBC: 7.5 K/uL, Hemoglobin: 14.2 g/dL, Platelets: 250 K/uL"

# Check if abnormal values are mentioned
is_normal <- !grepl("abnormal|low|high", report, ignore.case=TRUE)
print(is_normal)  # TRUE (no abnormal values flagged)

# Count number of tests
num_tests <- length(strsplit(report, ",")[[1]])
print(num_tests)  # 3 tests reported

10. Dates & Times: Handling Time-Based Data

The Concept: Calendar and Clock Data

Clinical research involves timing: - When was a patient enrolled? - How many days until follow-up? - What’s the time between baseline and week 12?

Real Clinical Trial Timeline

# Patient enrolled on January 15, 2024
# Follow-up scheduled 30 days later
enroll_date <- as.Date("2024-01-15")
followup_date <- enroll_date + 30

print(followup_date)  # 2024-02-14

# How many days between events?
days_passed <- followup_date - enroll_date
print(days_passed)  # 30 days

# Is follow-up overdue? (scheduled for 35 days)
if (days_passed > 35) {
  print("WARNING: Follow-up is overdue!")
}

Track Patient Visits

# A patient's visit schedule
visits <- data.frame(
  visit_date = as.Date(c("2024-01-15", "2024-02-14", "2024-04-15")),
  visit_type = c("Baseline", "Week 4", "Week 12"),
  glucose = c(245, 215, 185)
)

# How long between baseline and week 4?
days_to_week4 <- visits[2, "visit_date"] - visits[1, "visit_date"]
print(days_to_week4)  # 30 days

# Show visit dates nicely
for (i in 1:nrow(visits)) {
  date_str <- format(visits[i, "visit_date"], "%B %d, %Y")  # "January 15, 2024"
  print(paste(visits[i, "visit_type"], "→", date_str))
}

11. Apply Family: Smart Loops for Professionals

The Concept: Vectorized Operations (R’s Superpower)

Loops are slow. R has a faster way called “apply family”:

# SLOW approach (loop)
numbers <- 1:10
squared <- c()
for (i in numbers) {
  squared <- c(squared, i^2)
}

# FAST approach (vectorized)
squared <- numbers^2  # R does all 10 at once!

# PROFESSIONAL approach (apply family)
squared <- sapply(numbers, function(x) x^2)

Clinical Example: Calculate Dose for Multiple Patients

# 10 patients need weight-based dosing (5 mg/kg)
patient_weights <- c(75, 82, 68, 95, 70, 88, 65, 92, 78, 85)

# Calculate dose for each using sapply
doses <- sapply(patient_weights, function(w) w * 5)
print(doses)
# Output: 375 410 340 475 350 440 325 460 390 425

More Apply Functions

# lapply returns a list
results <- lapply(c(2, 3, 4), function(x) x^2)

# tapply groups by category
glucose <- c(245, 198, 310, 185, 225, 200)
group <- c("Drug_A", "Drug_B", "Drug_A", "Drug_B", "Drug_A", "Drug_B")
avg_by_group <- tapply(glucose, group, mean)
# Drug_A: 260, Drug_B: 191.67

12. Matrices: 2D Data Structures

The Concept: Tables of Numbers

A matrix is a rectangular grid of numbers (like a spreadsheet with only numbers).

Clinical Example: Correlation Matrix

# Lab values for 4 patients
patient_data <- matrix(
  c(
    245, 185, 65,      # Patient 1: glucose, LDL, HDL
    198, 210, 55,      # Patient 2
    310, 240, 48,      # Patient 3
    225, 195, 60       # Patient 4
  ),
  nrow = 4,
  ncol = 3,
  byrow = TRUE,
  dimnames = list(
    c("P001", "P002", "P003", "P004"),
    c("Glucose", "LDL", "HDL")
  )
)

print(patient_data)

# Calculate average for each column
col_means <- apply(patient_data, 2, mean)
print(col_means)
# Glucose: 244.5, LDL: 207.5, HDL: 57

13. Interactive WebR Playground

Try R code directly in your browser! No installation needed.

⏳ WebR: Not initialized — click “Run Code” to start



14. Exercises & Practice

Easy (Start Here)

1. Patient Age & Weight

# Create variables for a patient
age <- 45
weight <- 80

# Calculate age in months and weight in pounds
age_months <- age * 12
weight_pounds <- weight * 2.2

print(age_months)
print(weight_pounds)

2. Vector of Lab Values

# Create a vector of cholesterol readings
cholesterol <- c(210, 195, 240, 185, 220)

# Find average, min, max
avg <- mean(cholesterol)
highest <- max(cholesterol)
lowest <- min(cholesterol)

print(avg)
print(highest)
print(lowest)

3. Simple Control Flow

# Check blood pressure
systolic <- 145

if (systolic > 140) {
  print("Stage 2 Hypertension - Require treatment")
} else if (systolic > 130) {
  print("Stage 1 Hypertension - Monitor")
} else {
  print("Normal blood pressure")
}

Intermediate

4. Loop Through Patient Data

# Patient temperatures
temps <- c(36.8, 38.2, 39.5, 37.1, 40.1)

for (temp in temps) {
  if (temp > 39) {
    print(paste("FEVER:", temp, "°C"))
  } else {
    print(paste("Normal:", temp, "°C"))
  }
}

5. Create and Use a Function

# Function to calculate ideal body weight (Devine formula for males)
ideal_bmi <- function(height_cm) {
  height_inches <- height_cm / 2.54
  ibw <- 50 + 2.3 * (height_inches - 60)
  return(ibw)
}

# Use it
height <- 175  # cm
target_weight <- ideal_bmi(height)
print(paste("Ideal weight for", height, "cm is", round(target_weight, 1), "kg"))

Advanced

6. Data Frame Analysis

# Create a dataset and analyze
patients <- data.frame(
  id = 1:5,
  age = c(52, 45, 58, 50, 48),
  glucose = c(245, 198, 310, 225, 180),
  on_insulin = c(TRUE, FALSE, TRUE, FALSE, FALSE)
)

# Find average glucose
avg_glucose <- mean(patients$glucose)

# Find insulin users
insulin_users <- patients[patients$on_insulin == TRUE, ]

# Calculate statistics
cat("Average glucose:", avg_glucose, "\n")
cat("Number on insulin:", nrow(insulin_users), "\n")

Quick Reference Card

Concept Code Example
Assign variable x <- 10
Create vector v <- c(10, 20, 30)
Get vector mean mean(v)
Create data frame df <- data.frame(name=c('A','B'), age=c(25,30))
Get data frame column df$name
If statement if (x > 5) { ... }
For loop for (i in 1:5) { print(i) }
Define function f <- function(x) { return(x*2) }
Apply function sapply(c(1,2,3), function(x) x^2)
Read CSV df <- read.csv("file.csv")
Get today’s date Sys.Date()
Vector statistics mean(), median(), sd(), min(), max()

Resources for Continued Learning

  • Official R Manual: https://cran.r-project.org/manuals.html
  • R for Data Science (Free Book): https://r4ds.had.co.nz/
  • RStudio Education: https://education.rstudio.com/
  • Clinical R Resources: https://www.pharmaverse.org/
  • Stack Overflow (tag [r]): Best community Q&A
  • YouTube: “R for Beginners” and “Clinical R Programming”

Why R Will Transform Your Clinical Work

✅ Write analysis in 20 lines instead of 200
✅ Share reproducible code (instead of manual Excel spreadsheets)
✅ Create publication-ready graphics instantly
✅ Automate routine data cleaning and analysis
✅ Collaborate with biostatisticians seamlessly
✅ Join a global community of 2+ million R users

You’ve got this! Start with the WebR playground above, try the examples, and remember: every expert R programmer was once a beginner asking “What does <- mean?”


Back to home