To create a scatter plot in R, you first need to understand the data you are working with and choose the right tools for visualization. Scatter plots are indispensable for revealing patterns, trends, and potential outliers between two numeric variables, making them a go‑to technique in exploratory data analysis. Whether you are a student just starting with R or an experienced analyst looking to polish your graphics, mastering the creation of scatter plots will enhance your ability to communicate insights effectively. This guide walks you through the entire process, from data preparation to advanced customization, using both base R functions and the powerful ggplot2 package.
Preparing Your Data
Before you can create a scatter plot in R, ensure your dataset is tidy and contains the two variables you wish to compare. A common format is a data frame where each column represents a variable and each row an observation Small thing, real impact..
# Example: loading a built‑in dataset
data(mtcars) # motor trend car road tests
head(mtcars) # inspect first few rows
If you are working with external data, functions like read.csv() or read_excel() (from the readxl package) will import it into a data frame. Check for missing values (NA) and decide whether to remove or impute them, as missing data can break plotting functions Not complicated — just consistent..
# Remove rows with any NA values
df_clean <- na.omit(mtcars)
Basic Scatter Plot with Base R
R’s built‑in graphics system provides a quick way to create a scatter plot using the plot() function. This approach is useful for fast checks and when you prefer minimal dependencies.
# Simple scatter plot: weight vs. miles per gallon
plot(mtcars$wt, mtcars$mpg,
main = "Scatter Plot of Weight vs. MPG",
xlab = "Weight (1000 lbs)",
ylab = "Miles per Gallon",
pch = 19, # solid circle points
col = "steelblue") # point color
Key arguments explained
xandy: the numeric vectors for the horizontal and vertical axes.main: title of the plot.xlab/ylab: axis labels.pch: plotting character;19gives a filled circle.col: color of the points.
While base R graphics are fast, they offer limited flexibility for layered aesthetics. For more sophisticated visualizations, many analysts turn to ggplot2 The details matter here..
Creating a Scatter Plot with ggplot2
The ggplot2 package implements the Grammar of Graphics, allowing you to build plots layer by layer. Install it once (if not already) and load it before use And that's really what it comes down to..
install.packages("ggplot2") # run once
library(ggplot2)
A basic ggplot scatter plot follows this template:
ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_point() +
labs(title = "Scatter Plot of Weight vs. MPG",
x = "Weight (1000 lbs)",
y = "Miles per Gallon")
Components
ggplot(): initializes the plot, specifying the data frame and aesthetic mappings (aes()).geom_point(): adds the point layer.labs(): sets title and axis labels; you can also useggtitle(),xlab(), andylab()separately.
Customizing Appearance
Both base R and ggplot2 allow extensive customization. Below are common enhancements you might apply It's one of those things that adds up. Worth knowing..
Changing Point Shape and Size
In ggplot2, modify shape and size inside geom_point() or map them to a variable.
ggplot(mtcars, aes(x = wt, y = mpg, color = hp, size = qsec)) +
geom_point(alpha = 0.7) +
scale_color_viridis_c() +
labs(title = "Weight vs. MPG Colored by Horsepower",
subtitle = "Point size reflects quarter‑mile time",
x = "Weight (1000 lbs)",
y = "Miles per Gallon",
color = "Horsepower",
size = "Quarter‑mile time (sec)")
alphacontrols transparency, useful for overplotting.scale_color_viridis_c()applies a perceptually uniform color palette.
Adding Themes
ggplot2 includes built‑in themes that quickly alter the overall look.
+ theme_minimal() # clean, minimalistic
+ theme_classic() # classic axes with no background
+ theme_bw() # white background with gray grid
You can further tweak elements via theme():
+ theme(
plot.title = element_text(face = "bold", size = 16),
axis.text = element_text(size = 12),
panel.grid.major = element_line(color = "gray80")
)
Base R Customization
In base R, you can adjust colors, point types, and add legends manually Worth keeping that in mind..
plot(mtcars$wt, mtcars$mpg,
pch = ifelse(mtcars$am == 0, 15, 17), # different shapes for transmission
col = ifelse(mtcars$am == 0, "red", "blue"),
main = "Weight vs. MPG by Transmission",
xlab = "Weight (1000 lbs)",
ylab = "Miles per Gallon")
legend("topright",
legend = c("Automatic", "Manual"),
pch = c(15, 17),
col = c("red", "blue"))
Adding Regression Lines and Confidence Bands
To highlight trends, overlay a linear model fit That's the part that actually makes a difference. Practical, not theoretical..
Base R
fit <- lm(mpg ~ wt, data = mtcars)
abline(fit, col = "darkorange", lwd = 2) # adds the regression line
ggplot2
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point() +
geom_smooth(method = "lm", se = TRUE, color = "darkorange", fill = "lightorange") +
labs(title = "Weight vs. MPG with Linear Fit",
x = "Weight (1000 lbs)",
y = "Miles per
Continuing from the previous example, you can enrich the visual by adding annotations, facets, and alternative geoms that draw attention to specific patterns.
```r
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point(alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "steelblue", size = 1.2) +
annotate("text", x = 3.5, y = 30,
label = "Efficient zone",
color = "forestgreen", size = 4, hjust = 0) +
facet_wrap(~ cyl, ncol = 2) +
labs(title = "Weight vs. MPG – Trend Line",
subtitle = "Separated by cylinder count",
x = "Weight (1000 lbs)",
y = "Miles per Gallon",
color = "Model year")
The annotate() layer places a single text label at the specified coordinates, while geom_text() would let you add multiple labels or legends. By wrapping the data in facet_wrap(~ cyl), the plot is split into two panels — one for four‑cylinder cars and one for six‑cylinder cars — making it easier to compare trends across groups.
Fine‑tuning the visual hierarchy
- Axis scaling – Apply a logarithmic scale to the x‑axis when the range spans orders of magnitude:
scale_x_log10(). - Custom palettes – Replace the default continuous color scale with a discrete palette that matches your branding:
scale_color_manual(values = c("red", "blue", "green")). - Grid lines – Soften the background grid with
panel.grid.minor = element_line(color = "gray90")to keep the focus on the data points. - Font choices – Adjust the appearance of titles and axis labels via
theme(text = element_text(family = "sans", face = "bold")).
Saving and sharing
Once the composition meets your expectations, export the graphic directly from R:
ggsave("weight_mpg_plot.png", width = 9, height = 6, dpi = 300)
For interactive exploration, the plotly package can convert a static ggplot into a web‑ready chart:
library(plotly)
ggplotly(last_plot())
Concluding remarks
The flexibility of the graphics system lies in its layered approach: start with a basic scatter, then stack additional elements — points, smoothers, annotations, facets, and custom themes — to tell a richer story. Whether you remain within the tidy‑verse ecosystem or dip into base‑R graphics, the same principles of mapping data to aesthetics, controlling visual properties, and iterating through incremental refinements apply. By mastering these techniques, you can produce clear, publication‑ready visualizations that effectively communicate the underlying relationships in your data The details matter here..
The official docs gloss over this. That's a mistake.