Interactive R examples
Reset code restores the original example. Each block runs independently. The first run downloads the browser-based R runtime and requires an internet connection. Long-running code cannot be interrupted; reload the page if execution stalls.
Getting started with R
R is a programming language and statistical computing environment. RStudio is an integrated development environment (IDE) for editing scripts, running R, inspecting objects, and viewing plots.
Install R and RStudio
Runnable examples on this page require no local installation. File-based analyses and saved outputs use a local installation of R and RStudio.
- Install the current version of R from CRAN. Choose the download for your operating system and follow its installer.
- Install RStudio Desktop, then open it. R must be installed first.
- In RStudio, choose File → New Project → New Directory → New Project, choose a name and location, then select Create Project.
-
Choose File → New File → R Script. Type
2 + 2, leave the cursor on that line, then click Run or press Cmd+Enter on macOS / Ctrl+Enter on Windows or Linux. You should see[1] 4in the Console. -
Save the script as
analysis.R. In the Files pane, use New Folder three times to createdata,figures, andresultsinside the project.
Console and scripts
Use the Console for temporary commands and scripts as the reproducible record of an analysis.
Your first commands
# Anything after # is a comment.
2 + 2
minutes <- 90
hours <- minutes / 60
hours
mean(c(3, 6, 9))
round(10 / 3, digits = 2)
The assignment arrow <- stores a value in an object. R usually prints
nothing when an object is created. Type the object name to see its value. The [1] in printed
output marks the position of the first displayed value; it is not part of the answer.
How to read a function call
round(10 / 3, digits = 2)roundis the function: the action R will perform.- The values inside parentheses are inputs, called arguments.
digits = 2is a named argument that changes how the function behaves.- The returned value is the result.
Messages, warnings, and errors
Messages report context or progress. Warnings identify potential problems while evaluation continues; errors stop the current expression. Read the condition message before revising the code.
Objects and data types
An object is a name that points to a value in memory. The value has a type, which tells R which operations make sense. Adding numbers makes sense; adding two student IDs does not.
| Type or class | Purpose | Example |
|---|---|---|
| logical | True or false | TRUE, FALSE |
| integer | Whole number stored explicitly as an integer | 12L |
| double | General numeric value | 12, 3.14 |
| character | Text and identifiers | "S001", "Biology" |
| factor | Categories with defined levels | Control, Treatment |
| Date | Calendar date | as.Date("2026-09-08") |
student_id <- "S001"
quiz_score <- 84
completed <- TRUE
lesson_date <- as.Date("2026-09-08")
class(student_id)
class(quiz_score)
class(completed)
class(lesson_date)
typeof(quiz_score)
class() reports the class used by most analysis methods; typeof() reports the
underlying storage type.
Identifiers are usually text
Store an ID such as "S001" as character data. Arithmetic on an ID is meaningless, and
converting it to a number can erase leading zeros.
Explicit type conversion
text_number <- "42"
as.numeric(text_number)
condition <- factor(
c("Control", "Treatment", "Control"),
levels = c("Control", "Treatment")
)
levels(condition)
Vectors and missing values
A vector is an ordered, one-dimensional collection. Ordinary atomic vectors contain one type. Many R operations are vectorized: one instruction is applied to every element.
scores <- c(68, 84, NA, 93, 76)
length(scores)
scores[2]
scores[c(1, 4)]
scores[-3]
scores + 5
scores >= 80
R indexing starts at 1. A positive index keeps a position; a negative index removes it. The missing element stays missing because R cannot determine the result of arithmetic or comparison for an unknown value.
Zero and missing are different
0- A known numeric value equal to zero.
NA- The value is unavailable or unknown.
"0"- A character string containing the symbol zero.
""- An empty character string; it is not automatically missing.
mean(scores)
mean(scores, na.rm = TRUE)
is.na(scores)
sum(is.na(scores))
sum(scores >= 80, na.rm = TRUE)
Do not test missingness with ==
NA == NA returns NA, not TRUE, because two unknown values cannot be
proven equal. Use is.na(x). Count missing values before deciding whether to remove them.
Vector constructors
1:5
seq(from = 0, to = 1, by = 0.25)
rep(c("A", "B"), times = 3)
Exercise: Convert 160, 172, and 168 centimeters to meters
height_cm <- c(160, 172, 168)
height_m <- height_cm / 100
height_m
# 1.60 1.72 1.68
Datasets and importing data
In R, rectangular data are typically stored as data frames or tibbles. Rows represent observations, columns represent variables, and cells contain individual values.
Download and import the practice file
Download r_student_scores.csv, place it inside
your project’s data folder, and run the following code.
# Run this once on your computer:
install.packages("tidyverse")
# Run this at the beginning of each new session:
library(tidyverse)
students <- read_csv(
"data/r_student_scores.csv",
show_col_types = FALSE
)
Use project-relative paths
Project-relative paths remain valid when the project directory is moved or shared; user-specific absolute
paths do not. Use getwd() to inspect the current working directory.
Inspect before analyzing
students
glimpse(students)
dim(students)
names(students)
head(students, 3)
summary(students)
colSums(is.na(students))
anyDuplicated(students$student_id)
glimpse() shows column types and sample values. dim() should return 12 rows and 6
columns. anyDuplicated() returns 0 when no repeated ID is found. These checks should precede
transformation and analysis.
Select values by name or position
students$quiz_score
students[["quiz_score"]]
students$quiz_score[4]
students[1:3, c("student_id", "quiz_score")]
The first two forms return the same score vector. The third returns the fourth score, 93. The final command returns a small two-column table. Prefer column names over numeric column positions in an analysis script.
Transform and summarize a dataset
The native pipe |> passes the result on its left to the next function call. Pipelines express
transformations in execution order.
| Verb | Question it answers |
|---|---|
select() | Which columns do I need? |
filter() | Which rows meet a condition? |
arrange() | In what order should rows appear? |
mutate() | Which new or changed columns do I need? |
summarise() | Which compact summary should I calculate? |
group_by() | For which categories should I repeat that summary? |
# Keep three columns.
students |>
select(student_id, major, quiz_score)
# Keep rows where completed is TRUE and score is at least 80.
students |>
filter(completed == TRUE, quiz_score >= 80)
# Highest quiz score first.
students |>
arrange(desc(quiz_score))
Inside filter(), commas mean AND. Use | for OR and %in% to match any
value in a set. select() changes columns; filter() changes rows.
- The first result has 12 rows and only 3 columns.
- The filter returns S002, S004, S007, S009, and S011.
- The descending arrangement begins with S004, whose quiz score is 93.
students_prepared <- students |>
mutate(
study_minutes = study_hours * 60,
major = factor(
major,
levels = c("Mathematics", "Biology", "Public Health")
)
)
major_summary <- students_prepared |>
group_by(major) |>
summarise(
students = n(),
completed = sum(completed == TRUE),
mean_hours = mean(study_hours),
mean_score = mean(quiz_score),
.groups = "drop"
)
major_summary
Assignment creates a new table named students_prepared; the original students table
is unchanged. The expression completed == TRUE produces TRUE/FALSE values, and
sum() counts the TRUE values when no completion values are missing.
| major | students | completed | mean_hours | mean_score |
|---|---|---|---|---|
| Mathematics | 4 | 4 | 3.825 | 80.25 |
| Biology | 4 | 3 | 3.050 | 76.00 |
| Public Health | 4 | 1 | 2.200 | 66.75 |
Descriptive summaries do not establish causation
These twelve synthetic observations support only descriptive summaries. Differences in group means do not establish causal effects; inference requires an appropriate design, sampling framework, uncertainty quantification, and assessment of confounding.
Exercise: Keep Biology and Public Health students with attendance at least 80%
students |>
filter(
major %in% c("Biology", "Public Health"),
attendance_pct >= 80
) |>
select(student_id, major, attendance_pct)
Visualize with ggplot2
A ggplot is built in layers. You provide a dataset, map variables to visual properties inside
aes(), choose a geometric layer, then add labels and a theme.
ggplot(DATA, aes(x = X_VARIABLE, y = Y_VARIABLE)) +
geom_...() +
labs(...) +
theme_minimal()
Uppercase tokens and ellipses are placeholders; replace them with data, variables, layers, and labels.
| Question | Variables | Starting layer |
|---|---|---|
| How many observations are in each category? | One categorical | geom_bar() |
| How is one numeric variable distributed? | One numeric | geom_histogram() |
| How do two numeric variables relate? | Two numeric | geom_point() |
| How does a number vary across groups? | Numeric + categorical | Points or geom_boxplot() |
| How does a value change through ordered time? | Time + numeric | geom_line() |
Scatterplot: two numeric variables
score_plot <- ggplot(
students,
aes(
x = study_hours,
y = quiz_score,
color = major,
shape = completed
)
) +
geom_point(size = 3, alpha = 0.85) +
labs(
title = "Study time and quiz score",
subtitle = "Synthetic data for code practice",
x = "Study time (hours)",
y = "Quiz score",
color = "Major",
shape = "Completed"
) +
theme_minimal(base_size = 12)
score_plot
Position encodes study hours and score; color and shape encode major and completion status. Association in these synthetic data does not establish causation.
Mapping versus setting
aes(color = major) maps color to a variable and creates groups plus a legend.
geom_point(color = "#315e7d") sets one fixed color for every point. A quoted color name inside
aes() is usually a mistake.
Bar chart: count rows in categories
ggplot(students, aes(x = major)) +
geom_bar(fill = "#315e7d", width = 0.7) +
labs(
title = "Students in each major",
x = NULL,
y = "Number of students"
) +
theme_minimal(base_size = 12)
Each bar has height 4 because each major appears in four rows. If you already have a summarized count column,
use geom_col() instead.
Histogram and boxplot
ggplot(students, aes(x = quiz_score)) +
geom_histogram(binwidth = 5, boundary = 0) +
labs(x = "Quiz score", y = "Number of students") +
theme_minimal()
ggplot(students, aes(x = major, y = quiz_score)) +
geom_boxplot(outlier.shape = NA) +
geom_jitter(width = 0.08, height = 0, alpha = 0.8) +
labs(x = NULL, y = "Quiz score") +
theme_minimal()
A histogram groups numeric values into intervals; changing binwidth changes the display. The
boxplot summarizes a distribution, while jittered points keep the twelve observations visible. With such a
small dataset, focus on the points and avoid strong conclusions.
Export a plot
dir.create("figures", showWarnings = FALSE)
ggsave(
filename = "figures/study-time-vs-score.png",
plot = score_plot,
width = 7,
height = 4.5,
dpi = 300
)
Exercise: Color points by completion and make one panel per major
ggplot(
students,
aes(x = attendance_pct, y = quiz_score, color = completed)
) +
geom_point(size = 3) +
facet_wrap(~ major) +
labs(
x = "Attendance (%)",
y = "Quiz score",
color = "Completed"
) +
theme_minimal()
Reproducible analysis workflow
A reproducible analysis regenerates its outputs from the same inputs and script in a fresh R session. Manual or undocumented steps prevent exact regeneration.
Recommended project structure
library(tidyverse)
# 1. Import
students <- read_csv(
"data/r_student_scores.csv",
show_col_types = FALSE
)
# 2. Inspect and validate
glimpse(students)
colSums(is.na(students))
stopifnot(nrow(students) > 0)
stopifnot(anyDuplicated(students$student_id) == 0)
stopifnot(!anyNA(students$quiz_score))
stopifnot(all(students$quiz_score >= 0))
stopifnot(all(students$quiz_score <= 100))
# 3. Prepare
students_clean <- students |>
mutate(
major = factor(
major,
levels = c("Mathematics", "Biology", "Public Health")
)
)
# 4. Summarize
major_summary <- students_clean |>
group_by(major) |>
summarise(
students = n(),
completed = sum(completed == TRUE),
mean_score = mean(quiz_score),
.groups = "drop"
)
# 5. Visualize
p <- ggplot(
students_clean,
aes(x = study_hours, y = quiz_score, color = major)
) +
geom_point(size = 3) +
labs(
title = "Study time and quiz score",
subtitle = "Synthetic tutorial data",
x = "Study time (hours)",
y = "Quiz score",
color = "Major"
) +
theme_minimal(base_size = 12)
# 6. Save outputs
dir.create("results", showWarnings = FALSE)
dir.create("figures", showWarnings = FALSE)
write_csv(major_summary, "results/major-summary.csv")
ggsave(
"figures/study-time-vs-score.png",
plot = p,
width = 7,
height = 4.5,
dpi = 300
)
# 7. Save package and R versions
capture.output(
sessionInfo(),
file = "results/session-info.txt"
)
Fresh-session test
- Save
analysis.R. - Restart R so the Environment is empty.
- Run the complete script from top to bottom.
- Confirm that the table and figure are recreated without manual editing.
If it works only before restarting, your earlier session contained an undocumented object or manual step.
Exercises and reference
Complete each exercise before opening the supplied solution, and interpret each result in words.
Exercise 1: Find students scoring above 80 and order them from highest to lowest
students |>
filter(quiz_score > 80) |>
arrange(desc(quiz_score)) |>
select(student_id, major, quiz_score)
The result has five students: S004, S011, S007, S002, and S009.
Exercise 2: Summarize attendance by completion status
students |>
group_by(completed) |>
summarise(
students = n(),
mean_attendance = mean(attendance_pct),
.groups = "drop"
)
Expected result: FALSE has 4 students with mean attendance 68.5; TRUE has 8 students
with mean attendance 88.125. Always report group counts with means, especially in a small dataset.
Exercise 3: Create and save an attendance-versus-score plot
attendance_plot <- ggplot(
students,
aes(x = attendance_pct, y = quiz_score, shape = completed)
) +
geom_point(size = 3, color = "#315e7d") +
labs(
title = "Attendance and quiz score",
subtitle = "Synthetic tutorial data",
x = "Attendance (%)",
y = "Quiz score",
shape = "Completed"
) +
theme_minimal(base_size = 12)
dir.create("figures", showWarnings = FALSE)
ggsave(
"figures/attendance-vs-score.png",
plot = attendance_plot,
width = 7,
height = 4.5,
dpi = 300
)
| Goal | Function or syntax |
|---|---|
| Create an object | name <- value |
| Combine values | c(...) |
| Inspect a table | glimpse(), head(), summary() |
| Find missing values | is.na(), sum(is.na(x)) |
| Choose columns | select() |
| Choose rows | filter() |
| Create a column | mutate() |
| Repeat a summary by category | group_by() then summarise() |
| Start a scatterplot | ggplot(data, aes(x, y)) + geom_point() |
| Get help | ?mean, help("mean") |
Learning outcomes
- the difference between R, RStudio, a script, and the Console;
- why
NA, 0,"0", and an empty string are not interchangeable; - the difference between
select()andfilter(); - the difference between mapping and setting a ggplot property; and
- why the entire script should run in a fresh session.
References and further reading
- An Introduction to R — the official R manual.
- Introduction to dplyr — official documentation for the data verbs used here.
- Introduction to ggplot2 — official explanation of the grammar of graphics.
- RStudio Projects — Posit’s guide to project-based work.
- webR — the browser-based R runtime used by the interactive examples.