R learning notes

R for Data Science, Statistics & Reporting

By Imonikhe Ayeni

This reference starts with installing R and RStudio, then moves through the language, data cleaning, visualisation, statistics, modelling, reporting and Shiny. Work through the sections in order if you are new to R, or use the contents panel to jump directly to the topic you need.

Beginner to intermediateAbout 10 minutesLast updated August 2026

A note from the author

I’m Imonikhe Ayeni. I use R for statistical analysis, research and reporting work.

I put this reference together so learners can find the main ideas and examples in one place.

No matching sections were found. Try a broader search term.
01

Installing R and RStudio

R is the programming language. RStudio is an integrated development environment that makes R easier to write, run, organise and debug. Install R first, then install RStudio Desktop.

Windows

1. Open the CRAN website.
2. Choose “Download R for Windows”.
3. Select “base”.
4. Download the current Windows installer.
5. Run the installer and accept the standard options.
6. Download RStudio Desktop from Posit.
7. Install and open RStudio.
8. In the Console, run: R.version.string

macOS

1. Open the CRAN website.
2. Choose “Download R for macOS”.
3. Select the installer that matches your Mac and macOS version.
4. Run the downloaded package.
5. Download and install RStudio Desktop.
6. Open RStudio and run: R.version.string

Linux: Ubuntu or Debian

sudo apt update
sudo apt install r-base r-base-dev

# Verify
R --version

# Start R
R

# Exit R
q()

Test R inside RStudio

R.version.string
getwd()

message("R is installed and working.")
02

Understanding RStudio

RStudio normally contains four panes: the source editor, console, environment/history, and files/plots/packages/help. Write reusable code in an R script rather than relying only on the console.

Create your first script

# File > New File > R Script

name <- "Imonikhe"
message <- paste("Welcome to R,", name)

print(message)

Create an R project

1. Select File > New Project.
2. Choose New Directory.
3. Choose New Project.
4. Give the project a meaningful name.
5. Keep scripts, data and outputs inside the project.
6. Use relative paths instead of changing the working directory manually.

Recommended project folders

my-r-project/
├── data/
├── scripts/
├── outputs/
├── reports/
├── figures/
└── my-r-project.Rproj
03

Installing and loading packages

Packages extend R. Install each package once, then load it in every new R session where it is required.

Install packages

install.packages("tidyverse")
install.packages(c(
  "readxl",
  "openxlsx",
  "janitor",
  "skimr",
  "here"
))

Load packages

library(tidyverse)
library(readxl)
library(openxlsx)
library(janitor)
library(skimr)
library(here)

Check and update packages

installed.packages()[, c("Package", "Version")]
update.packages(ask = FALSE)
04

R fundamentals

R commonly uses the assignment operator <-. Objects can store numbers, text, Boolean values, dates, vectors, tables, functions and models.

Variables and data types

name <- "Imonikhe"
age <- 38L
height <- 1.78
is_learning <- TRUE
missing_value <- NA

class(name)
class(age)
class(height)
class(is_learning)

Arithmetic and comparisons

a <- 10
b <- 3

a + b
a - b
a * b
a / b
a %% b
a ^ b

a > b
a == 10
a != b

Strings

message <- "R for Data Science"

tolower(message)
toupper(message)
nchar(message)
grepl("Data", message)

paste("Learning", "R")
paste0("Score: ", 95)
05

Vectors, factors, lists and matrices

A vector stores values of the same basic type. Factors represent categories. Lists can contain different object types. Matrices store two-dimensional values of one type.

Vectors

scores <- c(78, 82, 91, 65, 88)

scores[1]
scores[2:4]
scores[scores >= 80]

length(scores)
mean(scores)
median(scores)
sum(scores)

Factors

gender <- factor(c(
  "Female",
  "Male",
  "Female",
  "Another way"
))

levels(gender)
table(gender)

Lists and matrices

patient <- list(
  name = "John",
  age = 65,
  diagnosis = "Diabetes"
)

patient$name
patient[["age"]]

matrix_data <- matrix(
  1:6,
  nrow = 2,
  byrow = TRUE
)

matrix_data
06

Conditions, loops and vectorisation

R supports familiar control-flow syntax, but vectorised functions are often preferred for data analysis.

Conditions

score <- 75

if (score >= 70) {
  result <- "Distinction"
} else if (score >= 60) {
  result <- "Merit"
} else if (score >= 50) {
  result <- "Pass"
} else {
  result <- "Fail"
}

print(result)

Loops

scores <- c(70, 80, 90)

for (score in scores) {
  print(score)
}

count <- 1

while (count <= 5) {
  print(count)
  count <- count + 1
}

Vectorised alternative

scores <- c(45, 67, 82, 39, 91)

result <- ifelse(
  scores >= 50,
  "Pass",
  "Fail"
)

result
07

Writing functions

Functions make analyses reusable and easier to test. Validate inputs and return clear errors when required.

A reusable function

calculate_mean <- function(values) {
  if (length(values) == 0) {
    stop("The vector cannot be empty.")
  }

  mean(values, na.rm = TRUE)
}

calculate_mean(c(70, 80, 90, NA))

Default arguments

calculate_total <- function(price, quantity = 1) {
  price * quantity
}

calculate_total(20)
calculate_total(20, 3)

Safe error handling

safe_divide <- function(a, b) {
  tryCatch(
    {
      if (b == 0) {
        stop("Cannot divide by zero.")
      }

      a / b
    },
    error = function(error) {
      message(error$message)
      NA_real_
    }
  )
}
08

Data frames and tibbles

A data frame is a table. A tibble is the modern tidyverse version with clearer printing and convenient behaviour.

Create a tibble

library(tibble)

data <- tibble(
  name = c("Alice", "Ben", "Chidi", "David"),
  age = c(25, 32, 41, 29),
  score = c(85, 72, 91, 68)
)

data

Inspect data

head(data)
tail(data)
dim(data)
names(data)
str(data)
summary(data)

library(skimr)
skim(data)

Select rows and columns

library(dplyr)

data |>
  select(name, score)

data |>
  filter(score >= 80)

data |>
  slice(1:3)
09

Importing and exporting data

Use readr for text files, readxl for Excel, haven for SPSS/Stata/SAS, and DBI for databases.

CSV and Excel

library(readr)
library(readxl)

csv_data <- read_csv("data/patients.csv")

excel_data <- read_excel(
  "data/patients.xlsx",
  sheet = "Responses"
)

SPSS, Stata and SAS

library(haven)

spss_data <- read_sav("data/survey.sav")
stata_data <- read_dta("data/survey.dta")
sas_data <- read_sas("data/survey.sas7bdat")

Export

library(readr)
library(openxlsx)

write_csv(
  cleaned_data,
  "outputs/cleaned_data.csv"
)

write.xlsx(
  cleaned_data,
  "outputs/cleaned_data.xlsx"
)
10

Data transformation with dplyr

dplyr provides consistent verbs for selecting, filtering, creating, sorting, grouping and summarising data.

Select, filter and arrange

library(dplyr)

result <- data |>
  select(name, age, score) |>
  filter(score >= 70, age < 40) |>
  arrange(desc(score))

result

Create columns

data <- data |>
  mutate(
    score_percentage = score / 100,
    result = if_else(
      score >= 70,
      "Pass",
      "Fail"
    )
  )

Multiple categories

data <- data |>
  mutate(
    grade = case_when(
      score >= 80 ~ "Excellent",
      score >= 70 ~ "Good",
      score >= 50 ~ "Pass",
      TRUE ~ "Fail"
    )
  )
11

Cleaning data

Clean column names, identify missing values, remove genuine duplicates and convert variables deliberately.

Clean names and missing values

library(janitor)
library(dplyr)
library(tidyr)

data <- data |>
  clean_names() |>
  mutate(
    score = replace_na(score, median(score, na.rm = TRUE)),
    gender = replace_na(gender, "Unknown")
  )

colSums(is.na(data))

Duplicates and types

duplicated_rows <- data |>
  filter(duplicated(patient_id))

data <- data |>
  distinct(patient_id, .keep_all = TRUE) |>
  mutate(
    age = as.numeric(age),
    gender = as.factor(gender)
  )

Dates

library(lubridate)

data <- data |>
  mutate(
    appointment_date = ymd(appointment_date),
    year = year(appointment_date),
    month = month(appointment_date, label = TRUE),
    day_name = wday(appointment_date, label = TRUE)
  )
12

Grouping and summarising

Grouped summaries are central to reporting and exploratory analysis.

Group summaries

summary_table <- data |>
  group_by(gender) |>
  summarise(
    mean_score = mean(score, na.rm = TRUE),
    median_score = median(score, na.rm = TRUE),
    participants = n(),
    .groups = "drop"
  )

summary_table

Counts and percentages

distribution <- data |>
  count(gender, name = "participants") |>
  mutate(
    percentage = participants / sum(participants) * 100
  )

distribution

Across multiple columns

data |>
  summarise(
    across(
      c(age, score),
      list(
        mean = ~ mean(.x, na.rm = TRUE),
        sd = ~ sd(.x, na.rm = TRUE)
      )
    )
  )
13

Joins and reshaping

Joins combine related tables. pivot_longer and pivot_wider move between long and wide structures.

Joins

combined <- patients |>
  left_join(
    hospitals,
    by = "hospital_id"
  )

matched_only <- patients |>
  inner_join(
    hospitals,
    by = "hospital_id"
  )

Long format

library(tidyr)

long_data <- data |>
  pivot_longer(
    cols = starts_with("score_"),
    names_to = "year",
    values_to = "score"
  )

Wide format

wide_data <- long_data |>
  pivot_wider(
    names_from = year,
    values_from = score
  )
14

Visualisation with ggplot2

ggplot2 builds charts in layers: data, aesthetic mappings, geometric marks and labels.

Scatter plot

library(ggplot2)

ggplot(data, aes(x = age, y = score)) +
  geom_point() +
  labs(
    title = "Age and Score",
    x = "Age",
    y = "Score"
  ) +
  theme_minimal()

Bar chart

ggplot(distribution, aes(
  x = gender,
  y = percentage
)) +
  geom_col() +
  labs(
    title = "Participant Distribution",
    x = "Gender",
    y = "Percentage"
  ) +
  theme_minimal()

Box plot

ggplot(data, aes(
  x = response_method,
  y = score
)) +
  geom_boxplot() +
  labs(
    title = "Scores by Response Method",
    x = "Response method",
    y = "Score"
  ) +
  theme_minimal()
15

Statistical analysis

R was designed for statistical computing and includes many tests in base R.

Welch t-test

test_result <- t.test(
  score ~ response_method,
  data = data,
  var.equal = FALSE
)

test_result

Chi-square and Fisher tests

contingency_table <- table(
  data$gender,
  data$response_method
)

chi_result <- chisq.test(contingency_table)
chi_result

fisher_result <- fisher.test(contingency_table)
fisher_result

Linear regression

model <- lm(
  score ~ age + response_method + gender,
  data = data
)

summary(model)
confint(model)

Logistic regression

model <- glm(
  has_disease ~ age + blood_pressure + cholesterol,
  data = data,
  family = binomial()
)

summary(model)

predicted_probability <- predict(
  model,
  newdata = data,
  type = "response"
)
16

Machine learning with tidymodels

tidymodels provides a consistent framework for preprocessing, resampling, modelling, tuning and evaluation.

Split the data

library(tidymodels)

set.seed(42)

data_split <- initial_split(
  data,
  prop = 0.8,
  strata = has_disease
)

training_data <- training(data_split)
testing_data <- testing(data_split)

Recipe and model

model_recipe <- recipe(
  has_disease ~ age + blood_pressure +
    cholesterol + gender,
  data = training_data
) |>
  step_impute_median(all_numeric_predictors()) |>
  step_impute_mode(all_nominal_predictors()) |>
  step_dummy(all_nominal_predictors()) |>
  step_normalize(all_numeric_predictors())

logistic_model <- logistic_reg() |>
  set_engine("glm") |>
  set_mode("classification")

Workflow and evaluation

model_workflow <- workflow() |>
  add_recipe(model_recipe) |>
  add_model(logistic_model)

fitted_model <- fit(
  model_workflow,
  data = training_data
)

predictions <- predict(
  fitted_model,
  testing_data,
  type = "prob"
) |>
  bind_cols(
    predict(fitted_model, testing_data),
    testing_data
  )

metrics(
  predictions,
  truth = has_disease,
  estimate = .pred_class
)
17

Time-series data

Time-series observations have an order. Create lags and rolling features carefully, and evaluate models using chronological splits.

Lag and rolling features

library(dplyr)
library(slider)

time_data <- time_data |>
  arrange(date) |>
  mutate(
    energy_lag_1 = lag(energy_use, 1),
    rolling_mean_7 = slide_dbl(
      energy_use,
      mean,
      .before = 6,
      .complete = TRUE,
      na.rm = TRUE
    )
  )

Monthly aggregation

library(lubridate)

monthly_data <- time_data |>
  mutate(month = floor_date(date, "month")) |>
  group_by(month) |>
  summarise(
    mean_energy = mean(energy_use, na.rm = TRUE),
    .groups = "drop"
  )
18

Quarto and reproducible reporting

Quarto combines narrative, R code, tables and charts in one reproducible document that can render to HTML, PDF, Word or presentations.

Quarto document header

---
title: "Analysis Report"
author: "Imonikhe Ayeni"
format:
  html:
    toc: true
    code-fold: true
execute:
  echo: true
  warning: false
---

R code chunk

```{r}
#| label: participant-summary

library(dplyr)

data |>
  summarise(
    participants = n(),
    mean_score = mean(score, na.rm = TRUE)
  )
```

Render from R

quarto::quarto_render(
  "reports/analysis.qmd"
)
19

Interactive applications with Shiny

Shiny creates interactive web applications directly from R. A basic app contains a user interface and a server function.

Minimal Shiny application

library(shiny)

ui <- fluidPage(
  titlePanel("Score Explorer"),
  sliderInput(
    "minimum_score",
    "Minimum score",
    min = 0,
    max = 100,
    value = 50
  ),
  tableOutput("filtered_table")
)

server <- function(input, output, session) {
  output$filtered_table <- renderTable({
    data |>
      filter(score >= input$minimum_score)
  })
}

shinyApp(ui, server)
20

Good R habits

Clear projects, readable pipelines, documented assumptions and reproducible package environments make work easier to review and maintain.

Recommended practices

• Use R projects and relative paths.
• Prefer clear object names over x, y or temp.
• Keep raw data unchanged.
• Put reusable logic in functions.
• Use set.seed() for reproducible random processes.
• Validate joins by checking row counts and unmatched keys.
• Comment why a decision was made, not only what the code does.
• Store package versions for important projects.

Use here for paths

library(here)

data <- readr::read_csv(
  here("data", "patients.csv")
)

readr::write_csv(
  data,
  here("outputs", "patients_clean.csv")
)

Use renv for reproducibility

install.packages("renv")

renv::init()
renv::snapshot()

# Restore packages later
renv::restore()
21

Four-week R learning plan

Build fluency by retyping, modifying and debugging examples. Use datasets relevant to your interests.

Week 1

RStudio, variables, vectors, lists, data frames, conditions, loops, functions and packages.

Week 2

Data import, dplyr, tidyr, missing data, joins, dates, grouped summaries and ggplot2.

Week 3

Statistical tests, linear models, logistic regression, survey analysis and model interpretation.

Week 4

tidymodels, time series, Quarto, Shiny and one complete end-to-end project.