wut
wut

Reputation: 143

How to plot side-by-side with sub labels and without space between subplots

I wonder how we can plot sub-labels on each plot and then have them side by side without space between subplots.

For example,

iris$petal.range <- findInterval(iris$Petal.Width, c(0, 0.75, 1.5))
iris$BigSpecies <- "type 2"
iris$BigSpecies[iris$Species == "versicolor"] <- "type 1"
mycolors <- ifelse(levels(iris$Species) == "setosa", "grey", 
                   ifelse(levels(iris$Species) == "versicolor", "pink", "skyblue"))

par(mfrow = c(1,3))

boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 1,], 
        main = "petal.range = 1", xlab = "x", ylab = "y", 
        col = mycolors)
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 2,], 
        main = "petal.range = 2", xlab = "x", ylab = "y", 
        col = mycolors)
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 3,], 
        main = "petal.range = 3", xlab = "x", ylab = "y", 
        col = mycolors)

I would like my plot to look similar to this

enter image description here

The labels in the plot above are "Subject 2", "Subject 5", "Subject 4". The sub-labels are "LocLm" and "LocGP". The labels in my example will be petal.range: "petal.range = 1", "petal.range = 2", "petal.range = 3". The sub-labels will be BigSpecies: "type = 1", and "type = 2".

Upvotes: 0

Views: 296

Answers (1)

r2evans
r2evans

Reputation: 160952

(This is a journey.)

The first attempt will use par("mar") to set the margins. Note that I'm starting with the default margins of c(5, 4, 4, 2) + 0.1; if you are using something else, make sure you change the mar vector appropriately.

par(mfrow = c(1,3))
mar <- c(5, 4, 4, 2) + 0.2
par(mar = c(5.1, mar[2], mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 1,],
        main = "petal.range = 1", xlab = "x", ylab = "y",
        col = mycolors)
par(mar = c(mar[1], 0, mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 2,],
        main = "petal.range = 2", xlab = "x", ylab = "y",
        col = mycolors)
par(mar = c(mar[1], 0, mar[3], mar[4]))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 3,],
        main = "petal.range = 3", xlab = "x", ylab = "y",
        col = mycolors)

plot 1, side by side, noisy Y axis

Problems: we have y-axes where we don't want them. Note that we also have the problem of unequal y ranges/limits, but I wanted to skip that for a moment to demonstrate the hazard of blindly removing the axis without safeguards.

Okay, here's remove the y-axes with yaxt="n":

par(mfrow = c(1,3))
mar <- c(5, 4, 4, 2) + 0.2
par(mar = c(5.1, mar[2], mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 1,],
        main = "petal.range = 1", xlab = "x", ylab = "y",
        col = mycolors)
par(mar = c(mar[1], 0, mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 2,],
        main = "petal.range = 2", xlab = "x", ylab = "y",
        col = mycolors, yaxt = "n")
par(mar = c(mar[1], 0, mar[3], mar[4]))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 3,],
        main = "petal.range = 3", xlab = "x", ylab = "y",
        col = mycolors, yaxt = "n")

plot 2, removed extra Y axis labels but Y ranges are silently different

This looks better, but it would be easy (natural) to assume that all of the boxplots are on the same y values, which they certainly are not. Let's add ylim= to each based on the overall range.

ylim <- range(iris$Petal.Length)
par(mfrow = c(1,3))
mar <- c(5, 4, 4, 2) + 0.2
par(mar = c(5.1, mar[2], mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 1,],
        main = "petal.range = 1", xlab = "x", ylab = "y",
        col = mycolors, ylim = ylim)
par(mar = c(mar[1], 0, mar[3], 0))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 2,],
        main = "petal.range = 2", xlab = "x", ylab = "y",
        col = mycolors, ylim = ylim, yaxt = "n")
par(mar = c(mar[1], 0, mar[3], mar[4]))
boxplot(Petal.Length ~ Species, data = iris[iris$petal.range == 3,],
        main = "petal.range = 3", xlab = "x", ylab = "y",
        col = mycolors, ylim = ylim, yaxt = "n")

plot 3, Y axis better and normalized

Unfortunately, the widths of each panel are different. This is natural, frankly, as we really need to go through some (non-trivial) calcs to determine a priori how wide (in pixels) everything is going to be with the y-axis markings and without.

Fortunately, there are packages to help with this, and one good mechanism to use is called faceting. I recommend this option:

library(ggplot2)
ggplot(iris, aes(Species, Petal.Length)) +
  facet_wrap(~ petal.range, nrow = 1) +
  geom_boxplot(aes(fill = Species))

ggplot2, faceted boxplot

Upvotes: 2

Related Questions