Historical Context & Motivation
The art and science of statistical graphics stretches back centuries, but the computational revolution of the late twentieth century fundamentally transformed how practitioners create visualizations. Before programmable tools existed, researchers drew plots by hand or relied on expensive mainframe batch-processing systems—a workflow that made iterative exploration prohibitively slow. The emergence of R as a free, open-source statistical computing environment democratized access to publication-quality plotting, enabling researchers and engineers alike to move from raw data to insightful graphics in seconds. Understanding the lineage of these plotting paradigms helps contextualize why R's current visualization ecosystem looks the way it does and why certain plot types have become canonical representations of data.
plot() and hist() that directly influenced R's base graphics system.The central question this lesson addresses is straightforward yet fundamental: given a dataset, how do you choose and implement the right plot type in R to effectively reveal patterns, distributions, and relationships? Whether you are profiling algorithm runtime complexity, exploring a machine-learning feature space, or summarizing simulation results, the five plot types covered here—scatter, line, bar, histogram, and boxplot—form the foundational visual vocabulary you will use daily.
Core Principles of Statistical Plotting in R
Before diving into individual plot types, it is essential to internalize the foundational principles that govern effective data visualization in R. These principles apply regardless of whether you use base R graphics or ggplot2. They determine not only which function to call but how to structure your data, map variables to visual channels, and select the appropriate geometric representation for your analytical question. A solid grasp of these ideas will let you reason about new plot types you encounter in the future, not just the five covered here.
Data ↔ Aesthetic Mapping
aes(); in base R it is implicit in function arguments.Geometry Selection
Tidy Data Prerequisite
Layered Composition
+ operator chains layers; in base R, functions like points() and lines() add to an existing canvas.Scale & Coordinate Control
Visual Overview — Five Plot Types at a Glance
The following diagram provides a visual reference for all five canonical plot types covered in this lesson. Each plot is shown with a schematic representation of its geometric elements and annotated with the type of data relationship it is best suited to reveal. Refer to this diagram throughout the lesson as a quick orientation tool.
Notice that the first two plot types (scatter and line) both place continuous variables on both axes, but they differ in a critical assumption: scatter plots make no assumption about ordering, whereas line plots assert that the x-axis represents a meaningful sequence (often time). The bar chart and histogram look superficially similar—both use rectangles—but the histogram's bars are contiguous because they represent adjacent intervals of a continuous variable, while bar chart bars are separated because their categories are discrete. The boxplot occupies a unique role as a statistical summary visualization, encoding five-number summary statistics (minimum, Q1, median, Q3, maximum) plus outliers into a compact glyph.
How R Produces Plots — Base Graphics vs. ggplot2
R provides two major plotting subsystems, and understanding their architectural differences helps you choose wisely for each task. Base R graphics follows an imperative, pen-on-paper model: you open a graphics device, draw one element at a time, and the final image is the accumulated result of sequential function calls. ggplot2 follows a declarative, grammar of graphics model: you specify what you want (data, mappings, geometries, scales), and the library figures out how to render it. In a CS analogy, base R is like writing assembly instructions for a GPU, while ggplot2 is like writing a scene description for a ray tracer.
Base R Syntax Patterns
| Plot Type | Base R Function | Key Arguments |
|---|---|---|
| Scatter | plot(x, y) | pch, col, cex, xlab, ylab, main |
| Line | plot(x, y, type="l") | lty, lwd, col |
| Bar | barplot(height) | names.arg, col, beside, horiz |
| Histogram | hist(x) | breaks, freq, col, border |
| Boxplot | boxplot(x ~ group) | notch, outline, col, horizontal |
ggplot2 Syntax Patterns
| Plot Type | ggplot2 Geom Layer | Typical Aesthetics |
|---|---|---|
| Scatter | geom_point() | aes(x, y, color, size, shape) |
| Line | geom_line() | aes(x, y, color, linetype, group) |
| Bar | geom_bar() / geom_col() | aes(x, y, fill) |
| Histogram | geom_histogram() | aes(x), bins, binwidth, fill |
| Boxplot | geom_boxplot() | aes(x, y, fill), outlier.shape |
geom_bar() computes counts from raw data (uses stat = "count" internally), while geom_col() expects pre-summarized data with explicit y-values (uses stat = "identity"). If you already have a summary table, use geom_col().The general ggplot2 template for any of these five plot types follows the same structure: ggplot(data, aes(...)) + geom_*(). Additional layers for themes (theme_minimal()), labels (labs()), and scale overrides (scale_x_continuous()) can be appended freely. This composability is ggplot2's principal advantage over base graphics for complex, multi-layered visualizations.
Detailed Breakdown — Anatomy and Options for Each Plot Type
Each of the five plot types has unique parameters and conventions that control its appearance and statistical behavior. This section provides a deeper dive into the anatomy of each plot and the key decisions you must make when constructing them. The diagram below illustrates the internal structure of a boxplot—the most statistically rich of the five types—as a reference for the summary statistics it encodes.
Key Decisions by Plot Type
- Scatter: Choose point character (
pchorshape) and transparency (alpha) to manage overplotting. Consider adding a trend line withgeom_smooth()orabline(). - Line: Ensure data is sorted by the x-variable before plotting. Use
groupaesthetic to handle multiple series; without it, ggplot2 may connect points incorrectly. - Bar: Decide between
geom_bar()(counts raw data) andgeom_col()(uses pre-computed values). For grouped bars, setposition = "dodge"; for stacked bars, use the defaultposition = "stack". - Histogram: The
binsorbinwidthparameter profoundly affects interpretation. Too few bins over-smooth the distribution; too many create noise. Sturges' rule (breaks = "Sturges") and Freedman-Diaconis (breaks = "FD") are common heuristics. - Boxplot: Use the formula interface
y ~ groupin both base R and ggplot2. Setnotch = TRUEfor a visual test of median differences between groups.
hist(x) without specifying breaks.Worked Example — Visualizing Algorithm Benchmarks
Suppose you have benchmarked three sorting algorithms (bubble sort, merge sort, and quicksort) across various input sizes and stored the results in a data frame called benchmarks with columns algorithm, n (input size), and time_ms (runtime in milliseconds). We will create all five plot types using ggplot2 to extract different insights from this data.
library(ggplot2) and then construct the data frame with set.seed(42) for reproducibility.library(ggplot2)
set.seed(42)
n_vals <- rep(c(100, 500, 1000, 5000, 10000), each = 30)
algo <- rep(rep(c("Bubble", "Merge", "Quick"), each = 10), 5)
time_ms <- ifelse(algo == "Bubble", 0.001 * n_vals^2 + rnorm(150, 0, 50),
ifelse(algo == "Merge", 0.05 * n_vals * log2(n_vals) + rnorm(150, 0, 20),
0.04 * n_vals * log2(n_vals) + rnorm(150, 0, 15)))
benchmarks <- data.frame(algorithm = algo, n = n_vals, time_ms = abs(time_ms))alpha parameter adds transparency to reveal overlapping points.ggplot(benchmarks, aes(x = n, y = time_ms, color = algorithm)) +
geom_point(alpha = 0.6, size = 2) +
labs(title = "Sorting Algorithm Runtime",
x = "Input Size (n)", y = "Time (ms)") +
theme_minimal()stat_summary() as a convenient shortcut.ggplot(benchmarks, aes(x = n, y = time_ms, color = algorithm)) +
stat_summary(fun = mean, geom = "line", linewidth = 1.2) +
stat_summary(fun = mean, geom = "point", size = 3) +
labs(title = "Mean Runtime by Input Size",
x = "Input Size (n)", y = "Mean Time (ms)") +
theme_minimal()geom_col() with pre-summarized totals to compare aggregate runtime. We first compute the totals with aggregate() or dplyr::summarise().totals <- aggregate(time_ms ~ algorithm, data = benchmarks, FUN = sum)
ggplot(totals, aes(x = algorithm, y = time_ms, fill = algorithm)) +
geom_col(width = 0.6) +
labs(title = "Total Runtime per Algorithm",
x = "Algorithm", y = "Total Time (ms)") +
theme_minimal() + theme(legend.position = "none")bins = 20 to balance detail and smoothness.qs <- benchmarks[benchmarks$algorithm == "Quick", ]
ggplot(qs, aes(x = time_ms)) +
geom_histogram(bins = 20, fill = "#34d399", color = "white") +
labs(title = "Distribution of Quicksort Runtimes",
x = "Time (ms)", y = "Count") +
theme_minimal()n == 5000 and use geom_boxplot().sub <- benchmarks[benchmarks$n == 5000, ]
ggplot(sub, aes(x = algorithm, y = time_ms, fill = algorithm)) +
geom_boxplot(outlier.shape = 21, outlier.size = 2) +
labs(title = "Runtime Distribution at n = 5000",
x = "Algorithm", y = "Time (ms)") +
theme_minimal() + theme(legend.position = "none")Strengths & Limitations of Each Plot Type
No single plot type is universally optimal. Each has strengths that make it the ideal choice for certain data configurations and limitations that can mislead if the plot is used inappropriately. The table below summarizes these trade-offs to guide your selection process.
| Plot Type | Strengths | Limitations |
|---|---|---|
| Scatter | Reveals correlations, clusters, and nonlinear relationships. Works well with color and size mappings for multivariate exploration. | Overplotting with large n; poor for categorical data; no built-in statistical summary. |
| Line | Clearly shows trends and temporal patterns; easy to compare multiple series with color/linetype. | Implies ordering/continuity; misleading for unordered data; sensitive to missing values creating gaps. |
| Bar | Intuitive for comparing magnitudes across categories; easily extended with stacking and faceting. | Can be misleading with truncated y-axes; not suitable for continuous distributions; cluttered with many categories. |
| Histogram | Shows distributional shape (skewness, modality); essential for assessing normality assumptions. | Bin width choice significantly affects interpretation; difficult to compare multiple distributions directly. |
| Boxplot | Compact multi-group comparison; highlights median, spread, and outliers; scales well to many groups. | Hides distributional shape (bimodality invisible); less intuitive for non-statistical audiences. |
Connection to Advanced Visualization Techniques
The five basic plot types covered in this lesson are the building blocks from which more sophisticated visualizations are constructed. Each has advanced descendants that address specific limitations or extend functionality. Understanding this progression will help you know when to reach for a more powerful tool and how it relates to the foundational plot you already understand.
| Basic Plot | Advanced Extension | What It Adds |
|---|---|---|
| Scatter | Hexbin plot / 2D density plot | Handles millions of points by aggregating into hexagonal bins with geom_hex() or geom_density_2d(). |
| Line | Ribbon / area chart | Shows confidence intervals or ranges with geom_ribbon(); stacked areas for proportional time series. |
| Bar | Lollipop chart / Cleveland dot plot | Reduces ink-to-data ratio; cleaner when many categories exist. Uses geom_segment() + geom_point(). |
| Histogram | Density plot / ridgeline plot | Smoothed continuous estimate with geom_density(); ridgeline plots (ggridges) compare many distributions. |
| Boxplot | Violin plot / beeswarm plot | Shows full distributional shape with geom_violin(); beeswarm (ggbeeswarm) shows individual data points. |
Beyond individual plot upgrades, the broader visualization frontier in R includes interactive graphics (via plotly::ggplotly() or shiny), faceted small multiples (via facet_wrap() and facet_grid()), and animation (via gganimate). Each of these builds on the same grammar-of-graphics foundation, so the conceptual investment you make in learning the five basic geoms pays compounding dividends as you explore the R visualization ecosystem.
plotly::ggplotly(p). This adds hover tooltips, zoom, and pan—extremely useful for exploring algorithm benchmarks or large datasets during development.Practice Problems
df with columns cpu_usage (x-axis) and memory_mb (y-axis), colored by a factor column process_type. Add appropriate axis labels and a minimal theme.latency_df has columns service (factor) and latency_ms (numeric). Write the code for a boxplot that uses notches and adds the raw data points on top with jitter to show the underlying distribution.training_log with columns epoch (integer), loss (numeric), and split (factor: 'train' or 'validation'). Create a line plot that shows loss curves for both splits, add a horizontal dashed reference line at loss = 0.1 representing the target loss, and use a log-scale y-axis. Explain each layer.Lesson Summary
This lesson covered the five foundational plot types in R: scatter plots for revealing relationships between two continuous variables, line plots for displaying trends over ordered sequences, bar charts for comparing magnitudes across categorical groups, histograms for examining the distributional shape of continuous data, and boxplots for compact multi-group distributional comparisons including median, spread, and outlier detection. We explored both base R syntax (imperative, pen-on-paper model) and ggplot2 syntax (declarative, grammar-of-graphics model) for constructing each type.
The key to effective visualization is matching the variable type and analytical question to the appropriate geometric representation. Remember that aesthetic mappings connect data columns to visual channels, geometry selection determines what is drawn, and parameters like bin width, alpha transparency, and axis scales profoundly affect interpretation. These five plot types serve as the foundation for the entire R visualization ecosystem, from interactive dashboards to publication-quality figures.