A Diary of Charts
Some code sketches for Base R plotting
My love for the base R plotting system has been growing since I started
dealing with complicated charts. Complexity in these charts can arise
for many reasons. For one, it might simply result from the data
structure when working with complex statistical models (e.g., multilevel
models). Conventions in the workplace could be another source of
complexity. For instance, tweaking a chart in R to replicate the one
originally created in Excel could be extremely difficult, and it is
nearly impossible to do so with higher-level plotting packages such as
ggplot2
. To approach the look of a chart created outside of R, life
would be much easier if lower-level frameworks such as the base R
plotting system were utilized.
Below is just a cumulation of charts I’ve created. It is intended to help me search and locate the code for plotting certain features in base R. The remainder of the post is divided into sections by charts, each of which consists of (1) the code, (2) the output chart, and (3) a list of features presented in the chart.
Scatter plots
Features
- Text on plot margins:
mtext()
1#### Data ####
2set.seed(2023)
3subj_idx = 1:30
4gender = rep(1:2, each=15)
5heights = c(160, 175)[gender] + rnorm(30, sd=15)
6
7#### Plot ####
8ylim = c( min(heights)-5, max(heights)+5 )
9plot( 1, type="n", xlim=c(.5,30.5), ylim=ylim,
10 xlab="Subject Index", ylab="Height (cm)" )
11points( 1:15, heights[ 1:15], col=2, pch=19 )
12points( 16:30, heights[16:30], col=4, pch=19 )
13abline( v=15.5, col="grey", lty="dashed" )
14mtext( c("Girls","Boys"), at=c(7,23), col=c(2,4), padj=-.5 )
Bar Charts
Features
- custom axis:
axis()
- axis label rotation (horizontal):
las = 1
- axis label padding adjustment:
hadj
,padj
- number of digits:
spintf("%.2f", yseq)
Code & Plot
1yseq = seq(0, 1, .25)
2plot(1, type="n", xaxt='n', yaxt='n',
3 ylab = "Probability", xlab="",
4 xlim=c(-.7, 1.7), ylim=c(0, 1) )
5lines( c(0, 0), c(0, 0.25), lwd=12, col=2 )
6lines( c(1, 1), c(0, 0.75), lwd=12, col=2 )
7axis( 2, at=yseq, labels=sprintf("%.2f", yseq), las=1, hadj = .85 )
8axis( 1, at=0:1, padj = .5,
9 labels = c("0\n(tail)", "1\n(head)"),
10)
Interaction Plots
Features
- Legend outside of plotting region:
par()
,legend()
,mar
,xpd
,inset
- Shaded region:
polygon()
- Custom axis (categorical axis):
axis()
- text / label:
text()
Code & Plot
1library(stom)
2library(dplyr)
3
4#### Data ####
5d = iris |>
6 group_by(Species) |>
7 summarise(
8 S.L = mean(Sepal.Length),
9 S.W = mean(Sepal.Width),
10 P.L = mean(Petal.Length),
11 P.W = mean(Petal.Width)
12 )
13d
14
15#### Annotations ####
16(LABELS = colnames(d)[-1])
# A tibble: 3 x 5
Species S.L S.W P.L P.W
<fct> <dbl> <dbl> <dbl> <dbl>
1 setosa 5.01 3.43 1.46 0.246
2 versicolor 5.94 2.77 4.26 1.33
3 virginica 6.59 2.97 5.55 2.03
[1] "S.L" "S.W" "P.L" "P.W"
1#### Plot ####
2# c( b, l, t, r )
3par( mar=c(5.1, 4.1, 4.1, 8.1), xpd=F ) # Larger right margin for legend
4plot( 1, type="n", xlim=c(.5, 4.5), ylim=c(0,7),
5 xlab="", ylab="Mean",
6 xaxt="n", yaxt="n") # disable x/y-axis
7# Shaded region (coordinates are specified (counter-)clockwise)
8polygon( c(0,4.9,4.9,0),c(2,2,5,5), col=col.alpha("grey",.15), lty=2, border="grey" )
9# Auxiliary lines
10for ( h in 0:7 )
11 abline( h=h, col=col.alpha("grey") )
12# Lines & Points
13for ( i in 1:nrow(d) ) {
14 lines ( 1:4, d[i,-1], col=i, lwd=4 )
15 points( 1:4, d[i,-1], col=i, lwd=4, pch=2+i )
16}
17# Labels
18for ( i in 1:nrow(d) ) {
19 if ( i != 2 ) next
20 text( 1:4-.03, d[i,-1]-.45, labels = d[i,-1], cex=.9, col=i )
21}
22# Axis
23axis( 1, at=1:4, tck=-.02, labels=LABELS )
24axis( 2, at=0:7, labels=sprintf("%.1f", 0:7), las=1 ) # rotated y-axis labels
25# Legend (outside of plotting region)
26legend("right", legend=d$Species, inset=c(-.17,0), xpd=TRUE,
27 col=1:nrow(d), pch=1:nrow(d) + 2, lwd=4, cex=.9,
28 y.intersp=1.8, box.col="transparent", bg="transparent" )
Density Plots
Features
- Line segments:
segments()
- Density:
density()
- text / label:
text()
Code & Plot
1library(stom)
2library(dplyr)
3
4#### Data ####
5set.seed(2020)
6d = data.frame(
7 x = rnorm(2000)
8)
9
10#### Annotations ####
11X = d$x
12AVG = mean(X)
13SD = sd(X)
14
15#### Plot ####
16plot( density(X), ylim=c(0,.46),
17 main = "A Standard Normal", xlab = "X" )
18# Mean line segment
19segments( x0=AVG, y0=0, y1=.43, col=2, lwd=3 )
20text( AVG, .46, labels = paste("AVG:",round(AVG,3)), col=2, cex=.8 )
21# SD line segment
22segments( x0=AVG+.06, x1=AVG+SD, y0=.02, lwd=2, col=4 )
23segments( x0=AVG+.06, y0=.01, y1=.03, lwd=2, col=4 )
24segments( x0=AVG+SD, y0=.01, y1=.03, lwd=2, col=4 )
25text( .5*(AVG+SD+.06), .035,
26 labels = paste("SD =",round(SD,3)), col=4, cex=.8 )
Combining Plots
Features
- Plot panels:
par()
+mfrow()
Code & Plot
1inner = c(4.1, 4.1, 1.8, .5)
2par( mfrow=c(2,2), oma=c(0,0,0,0), mar=inner )
3
4for ( i in 1:4 ) {
5 plot( 1, type="n", xlim=0:1, ylim=0:1, xlab="", ylab="" )
6 text( .5, .5, labels=paste("Plot",i), cex=2 )
7}