Basic Plotting with Base R

Learn how to create simple diagnostic plots using base R plotting functions — the foundation behind all visualization in R.
Tip

Big idea: Before fancy plotting libraries, there is base R. If you understand plot(), you understand the core idea of visualizing relationships in R.

Learning Objectives

By the end of this lesson, you will be able to:

  • Create scatter plots using plot().
  • Add lines and points to existing plots.
  • Customize titles and axis labels.
  • Control basic graphical parameters (color, point type, line type).
  • Understand how PK concentration–time plots are constructed from simple building blocks.

Why Base R Plotting Still Matters

Even though modern workflows often use ggplot2, base R plotting is still important because:

  • it is always available (no additional packages required),
  • it reveals the fundamental structure of a plot,
  • it is fast for quick diagnostics,
  • and it reinforces how numeric vectors map directly to axes.

When you call:

plot(x, y)

R takes one numeric vector (x) and places it on the horizontal axis, and another numeric vector (y) on the vertical axis. It then draws a visual relationship between them.

In pharmacometrics (PMx), this simple relationship — time vs concentration — is the backbone of nearly every exploratory analysis.


Create a Small PK Dataset

pk <- data.frame(
  ID = c(1, 1, 1, 2, 2, 2),
  TIME = c(0.5, 1, 2, 0.5, 1, 2),
  DV = c(2.1, 3.8, 3.0, 1.6, 2.9, 2.4)
)

pk
  ID TIME  DV
1  1  0.5 2.1
2  1  1.0 3.8
3  1  2.0 3.0
4  2  0.5 1.6
5  2  1.0 2.9
6  2  2.0 2.4

Think of:

  • ID → subject identifier
  • TIME → time after dose (hours)
  • DV → observed concentration

Each row is one observation. Each column is a variable.


The Simplest Plot: plot()

plot(pk$TIME, pk$DV)

This produces a scatter plot.

Behind the scenes:

  • TIME is mapped to the x-axis.
  • DV is mapped to the y-axis.
  • R draws points at each (TIME, DV) pair.

This alone already lets you visually inspect trends, variability, and potential outliers.


Add Labels (Always Do This in PMx)

plot(
  pk$TIME,
  pk$DV,
  xlab = "Time (hours)",
  ylab = "Concentration (mg/L)",
  main = "Concentration vs Time"
)

Now the plot communicates clearly what is being shown.

Tip

PMx habit: Always label axes with units. Plots without units are scientifically incomplete.


Points vs Lines

By default, plot() draws points (type = "p").

Common options:

  • "p" → points
  • "l" → lines
  • "b" → both
plot(
  pk$TIME,
  pk$DV,
  type = "b",
  xlab = "Time (hours)",
  ylab = "Concentration (mg/L)"
)

In PK, type = "b" is often useful because it shows both measurements and trajectory.


Plot by Subject

We should not connect observations across subjects. Instead:

plot(NULL,
     xlim = range(pk$TIME),
     ylim = range(pk$DV),
     xlab = "Time (hours)",
     ylab = "Concentration (mg/L)",
     main = "PK Profiles by Subject")

for (id in unique(pk$ID)) {
  sub <- pk[pk$ID == id, ]
  sub <- sub[order(sub$TIME), ]
  lines(sub$TIME, sub$DV, type = "b")
}

Steps:

  1. Initialize an empty plotting region.
  2. Loop over subjects.
  3. Subset and sort by time.
  4. Add lines for each subject.

This structure is conceptually how many PK visualizations are built.


Log-Scale Plots (Very Common in PK)

plot(
  pk$TIME,
  pk$DV,
  log = "y",
  type = "b",
  xlab = "Time (hours)",
  ylab = "Concentration (mg/L, log scale)",
  main = "Semi-Log PK Plot"
)

log = "y" keeps time linear but log-transforms concentration.

Semi-log plots help identify exponential decline and elimination phases.


Strategies

  • Always think: x = TIME, y = DV.
  • Label axes immediately.
  • Check str() if a plot behaves strangely.
  • Sort by time before drawing lines.
  • Use semi-log plots for concentration–time data when appropriate.

Practice Problems

  1. Create a scatter plot of TIME vs DV.
  2. Add axis labels and a title.
  3. Change the plot to show both points and lines.
  4. Create a semi-log (y-axis) plot.
  5. Modify color and point type.

plot(pk$TIME, pk$DV,
     xlab = "Time (hours)",
     ylab = "Concentration (mg/L)",
     main = "PK Plot")

plot(pk$TIME, pk$DV,
     type = "b",
     log = "y",
     col = "red",
     pch = 16,
     lwd = 2,
     xlab = "Time (hours)",
     ylab = "Concentration (mg/L, log scale)")


Summary

You now understand the foundations of plotting in R:

  • plot(x, y) creates a scatter plot.
  • type controls points vs lines.
  • Labels and units are essential in PMx.
  • lines() allows layered plots.
  • Log scales are common for concentration–time data.

Everything else in R visualization builds on these ideas.


  • plot(x, y) → fastest way to visualize two numeric vectors.
  • Always label axes with units.
  • Use type = "b" for PK profiles.
  • Use log = "y" for semi-log concentration plots.
  • If lines look wrong, check ordering by time.