08 · Basic Plotting¶
plot() — R's built-in plotting function¶
Base R ships with a capable plotting system, no packages required. plot()
adapts its behavior to the type of data you give it.
ages <- c(23, 25, 22, 34, 28, 41, 19, 30)
scores <- c(88, 92, 79, 65, 85, 70, 95, 81)
plot(ages, scores)
# Opens a plot window (or saves to a device) with a scatter plot:
# ages on the x-axis, scores on the y-axis, one point per pair
Customizing a scatter plot¶
plot(ages, scores,
main = "Age vs. Test Score",
xlab = "Age",
ylab = "Score",
col = "steelblue",
pch = 16) # pch = point character; 16 is a solid filled circle
| Argument | Meaning |
|---|---|
main |
Plot title |
xlab / ylab |
Axis labels |
col |
Point/line color |
pch |
Point shape (0-25 are built-in symbols) |
type |
"p" points (default), "l" line, "b" both |
xlim / ylim |
Axis ranges, e.g. xlim = c(0, 50) |
Line plots¶
days <- 1:7
temps <- c(15, 17, 16, 19, 22, 21, 18)
plot(days, temps, type = "l", col = "darkred", lwd = 2,
main = "Temperature Over a Week", xlab = "Day", ylab = "°C")
type = "l" connects points with a line instead of drawing separate markers;
lwd controls line width.
Bar charts — barplot()¶
sales <- c(120, 90, 150, 80)
names(sales) <- c("Q1", "Q2", "Q3", "Q4")
barplot(sales,
main = "Quarterly Sales",
ylab = "Units Sold",
col = "seagreen")
barplot() expects a vector (ideally named, as above, so the names become
the bar labels automatically).
Histograms — hist()¶
set.seed(42) # makes the "random" data reproducible
exam_scores <- rnorm(200, mean = 75, sd = 10) # 200 values from a normal distribution
hist(exam_scores,
main = "Distribution of Exam Scores",
xlab = "Score",
col = "cornflowerblue",
breaks = 15) # roughly how many bars to draw
A histogram groups continuous values into bins and shows how many fall into each — the standard first look at any single numeric column's distribution.
Boxplots — boxplot()¶
group_a <- c(88, 92, 79, 65, 85)
group_b <- c(70, 95, 60, 55, 80)
boxplot(group_a, group_b,
names = c("Group A", "Group B"),
main = "Score Comparison",
ylab = "Score",
col = c("lightblue", "lightpink"))
A boxplot summarizes a distribution's median, quartiles, and outliers in one compact shape — useful for comparing two or more groups at a glance.
Saving a plot to a file¶
png("scatter.png", width = 800, height = 600) # open a PNG file device
plot(ages, scores, main = "Age vs. Score")
dev.off() # close the device -- writes the file
Every plotting call between opening a device (png(), pdf(), jpeg()) and
dev.off() gets drawn into that file instead of the interactive plot window.
Forgetting dev.off() is a common mistake — the file stays empty/locked
until you close the device.
Multiple plots in one figure¶
par(mfrow = c(1, 2)) # 1 row, 2 columns of plots
plot(ages, scores, main = "Scatter")
hist(exam_scores, main = "Histogram")
par(mfrow = c(1, 1)) # reset back to a single plot per figure
Base plotting cheat sheet¶
| Function | Use for |
|---|---|
plot(x, y) |
Scatter plot of two numeric vectors |
plot(x, y, type = "l") |
Line plot |
barplot(x) |
Bar chart of a (named) vector |
hist(x) |
Distribution of one numeric vector |
boxplot(x, y, ...) |
Compare distributions across groups |
png() / dev.off() |
Save a plot to a file |
par(mfrow = c(r, c)) |
Arrange multiple plots in a grid |
Base R plotting is quick and dependency-free, which is why it's introduced first. Level 2 introduces ggplot2, the more expressive and widely used plotting package for anything beyond a quick look at your data.
How It Actually Works¶
Base R plotting (plot(), hist(), abline(), ...) is imperative and
stateful: each call draws directly onto the currently active graphics
device (a bitmap or vector canvas R maintains internally) and mutates it in
place. plot() doesn't just draw points — it resets the device, computes
axis ranges from your data, and pushes a new "plotting region" onto R's
internal graphics state stack. Every subsequent call like abline() or
points() you make afterward doesn't redraw the whole chart; it layers
more ink onto the same device using the axis coordinate system that
plot() already established — which is exactly why call order matters and
why you can't easily "undo" one layer without restarting from plot().
Under the hood, each high-level plotting function ultimately calls into R's grid/graphics engine (a C-level device driver interface), which translates your plotting calls into device-specific drawing operations — pixel writes for PNG, vector path commands for PDF/SVG. This is also why resizing an RStudio plot pane after the fact can shift text and point sizes: the device is re-rendered at the new dimensions from the same recorded calls, not simply scaled as an image.
🔀 See this in another language¶
Exercise¶
Using the exam_scores vector generated above (rnorm(200, mean = 75, sd =
10) with set.seed(42)), produce three plots: a histogram with 20 breaks
and a title, a boxplot with a y-axis label, and a line plot of sort(exam_scores)
(the sorted scores) to visualize the distribution's shape. Save the histogram
to a file called scores_hist.png using png()/dev.off().