2

It seems like many users are trying to do what I am also trying here.

I am trying to do these things:

  1. x-axis labels to be fixed (range 0 to 1200), the x-axis to be repeated on each plot, and for the x-axis label name to be repeated and customizable for each plot
  2. y-axis labels to be free (but to be able to edit the range in the second plot to be breaks=c(0, 3, 6, 9, 12)), the y-axis to be repeated on each plot, and for the y-axis labels to be repeated and customizable for each plot

Using the below code (adapted from previous OPs and documentation):

ggplot(mapping=aes(x=dist, fill=sex)) +
   geom_histogram(data = cbind(move, panel = "A"), color="black", binwidth=10) +
   geom_vline(data = cbind(move, panel = "A"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
   geom_histogram(data = cbind(settle_data, panel = "B"), color="black", binwidth=10) +
   geom_vline(data = cbind(settle_data, panel = "B"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
   geom_histogram(data = cbind(recruit_data, panel = "C"), color="black", binwidth=10) +
   geom_vline(data = cbind(recruit_data, panel = "C"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
    coord_cartesian(xlim=c(0,1200))+ #limits axes range without deleting points
    scale_fill_manual(values=c("gray97", "gray47"),
            name = "Sex",   
            breaks=c("F","M"),
            labels=c("Female","Male")) +
    theme_bw() +
    theme(legend.position = c(0.8,0.8), #move legend into plot
        axis.line = element_line(colour = "black"),
        axis.text=element_text(size = 20), #changes size of axes #s
        axis.title=element_text(size=20), #changes size of axes labels
        plot.caption = element_text(hjust = 0, vjust = 2.12),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank(),
        text = element_text(size = 15)) +
    facet_wrap(~ panel, ncol = 1, scales='free_y', labeller=as_labeller(c(A = "Number of fixes", B = "Number of individuals", C = "Number of individuals") ), strip.position="left") +
    scale_x_continuous(breaks = seq(0,1250,by = 200)) +     
    theme(
    strip.placement = "outside",
    strip.background = element_blank(),
        axis.title.y = element_blank(),
        strip.text = element_text(size = 19))+
    labs(x = "Distance travelled from natal nest (m)")

Resulting plot (I was able to "hack" the y-axis issues I was having by moving the panel names to the left outside of the plots and act like they were axis labels):

enter image description here

The plot I want (edits circled in red): enter image description here

I cannot post the data I used for the plots, but I've created a sample dataset below:

move<- data.frame(
    dist = c(0,100,200,300,400,400,500,600,700,800,1000,1200,0,100,200,300,400,400,500,600,700,800,1000,1200,0,100,200,300,400,400,500,600,700,800,1000,1200,0,100,200,300,400,400,500,600,700,800,1000,1200),
    sex = c ("F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F","F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F"))
settle_data<-data.frame(
    dist = c(0,10,20,30,400,40,50,60,700,80,1000,1200,0,10,20,30,400,40,50,60,700,80,1000,1200, 0,10,20,30,400,40,50,60,700,80,1000,1200,0,100,200,300,400,400,500,600,700,800,1000,1200,0,10,20,30,400,40,50,60,700,80,1000,1200,0,10,20,30,400,40,50,60,700,80,1000,1200,0,10,20,30,400,40,50,60,700,80,1000,1200,0,100,200,300,400,400,500,600,700,800,1000,1200),
    sex = c ("F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F","F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M","F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M", "F", "F","F", "M", "M", "M", "M", "M", "F", "F", "F", "M", "F", "F", "F", "M", "M", "M","M", "M", "F", "F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M","F", "F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M","F", "F", "F", "M", "M", "M", "M", "M", "F", "F"))
recruit_data<- data.frame(
    dist = c(0,10,20,30,400,40,50,60,700,80,1000,1200,0,10,20,30,4,40,50,60,700,80,10,120,0,10,20,30,40,40,50,60,70,80,100,120,0,100,200,300,400,400,500,600,700,800,1000,1200),
    sex = c ("F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F","F","F", "M", "F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M","F", "F", "F", "M", "M", "M", "M", "M", "F", "F","F", "M", "F", "F","F", "M", "M", "M", "M", "M", "F", "F"))
Blundering Ecologist
  • 1,199
  • 2
  • 14
  • 38
  • 3
    If you're plotting three different data frames with three different axes, consider making three separate plots and using `grid.arrange` or `arrangeGrob` – Punintended Aug 07 '19 at 18:47
  • Since you want separate axis labels and fine-tuned control over the axes, I second the idea that making separate plots might be the way to go. See packages cowplot, patchwork, and egg for some nice tools for combining separate plots. – aosmith Aug 07 '19 at 18:49
  • 1
    For the y axis breaks issue you may be able to use `scales::pretty_breaks()` as discussed [here](https://community.rstudio.com/t/separate-axis-breaks-for-facets/10352/2). For the x axis, you could allow free x scales but then set the limits and breaks with `scale_x_continuous. This would give you an axis for each plot but have the limits/breaks be the same (but only one overall axis label). – aosmith Aug 07 '19 at 19:02
  • Using `lemon::facet_rep_wrap(~panel, ncol=1, strip.position='b', repeat.tick.labels='bottom')` will place the panel names on the bottom of each facet and repeat the x-axis labels. – kstew Aug 07 '19 at 19:02
  • As @teunbrand points out, when I used `grid.arrange` the y-axes do not line up given the top plot has 4 digit numbers which pushes that plot in further than the two lower plots (with only 1 digit numbers on the y-axes). Also, I am looking for a general solution that works with `facet_wrap` given the OP is only an example of the kind of problem I keep encountering with `facet_wrap`. – Blundering Ecologist Aug 07 '19 at 20:01

2 Answers2

5

For those who want or need to use facet_wrap, here is the solution that worked for me (pulling up suggestions from the comments).

The below edits gave the following plot:

enter image description here

The important notes on the edits:

coord_cartesian(xlim=c(0,1200)) sets limit for x axis to be the same for all plots

scale_y_continuous(breaks = scales::pretty_breaks(5)) made the y-axes not have decimals

labs(x = "") adds an x-axis to the very bottom

To add a label to each other plot, geom_text() allowed for this simply by changing which plot (panel=c("B") and panel=c("C")) the annotation appeared in.

Within facet_wrap():

scales='free' makes it so that the axes are repeated for every plot

labeller=as_labeller(c([text])) can be used to give each plot its own unique y-axis name

strip.position='left' moves the panels to the left side (where the y-axis would be)

Within theme():

strip.placement = "outside" moves the panel labels to the outside of the plot, allowing them to act as y-axes label names

The code used:

ggplot(mapping=aes(x=dist, fill=sex)) +
   geom_histogram(data = cbind(move, panel = "A"), color="black", binwidth=10) +
   geom_vline(data = cbind(move, panel = "A"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
   geom_histogram(data = cbind(settle_data, panel = "B"), color="black", binwidth=10) +
   geom_vline(data = cbind(settle_data, panel = "B"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
   geom_histogram(data = cbind(recruit_data, panel = "C"), color="black", binwidth=10) +
   geom_vline(data = cbind(recruit_data, panel = "C"), aes(xintercept=mean(dist)), 
    color="black", linetype="dashed", size=1)+
    coord_cartesian(xlim=c(0,1200))+ 
    scale_fill_manual(values=c("gray97", "gray47"),
            name = "Sex",   
            breaks=c("F","M"),
            labels=c("Female","Male")) +
    theme_bw() +
    theme(legend.position = c(0.95,0.93), #move legend into plot
        axis.line = element_line(colour = "black"),
        axis.text=element_text(size = 20), #changes size of axes #s
        axis.title=element_text(size=20), #changes size of axes labels
        plot.caption = element_text(hjust = 0, vjust = 2.12),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        panel.border = element_blank(),
        panel.background = element_blank(),
        text = element_text(size = 20)) +
    facet_wrap(~ panel, ncol = 1, scales='free', labeller=as_labeller(c(A = "Number of fixes", B = "Number of individuals", C = "Number of individuals") ), strip.position="left") +
    scale_x_continuous(breaks = seq(0,1250,by = 200)) +  
    scale_y_continuous(breaks = scales::pretty_breaks(5))+  
    theme(
    strip.placement = "outside",
    strip.background = element_blank(),
        axis.title.y = element_blank(),
        strip.text = element_text(size = 19))+
    labs(x = "Distance recruited from natal nest (m)")+
 geom_text(data=data.frame(x=600, y=12, 
        label="Distance travelled from natal nest (m)", panel=c("B")), 
               aes(x,y,label=label), inherit.aes=FALSE, size = 7) +
 geom_text(data=data.frame(x=600, y=6, 
        label="Distance settled from natal nest (m)", panel=c("C")), 
               aes(x,y,label=label), inherit.aes=FALSE, size = 7) 
Blundering Ecologist
  • 1,199
  • 2
  • 14
  • 38
4

I agree with most of the comments that it would be easier to make seperate plots. A painpoint of such an approach is often that the alignment of the axes is off. To counteract this, I'll point towards the patchwork package, that takes out most of this pain.

library(ggplot2)
library(patchwork)

# Get some basic plots
dat_list <- list(move, recruit_data, settle_data)

plots <- lapply(dat_list, function(df) {
  ggplot(df, aes(dist, fill = sex)) +
    geom_histogram(binwidth = 10) +
    scale_x_continuous(limits = c(0, 1200)) +
    theme(legend.position = "none")
})

# Make adjustments to each plot as needed
plots[[1]] <- plots[[1]] + labs(x = "Distance settled (m)", y = "Number of fixes") + 
  theme(legend.position = c(1,1), legend.justification = c(1,1))

plots[[2]] <- plots[[2]] + labs(x = "Distance recruited (m)", y = "Number of individuals") + 
  scale_y_continuous(breaks = seq(0, 12, by = 3), limits = c(0, 12))

plots[[3]] <- plots[[3]] + labs(x = "Distance from natal nest (m)", y = "Number of individuals")

# Patchwork all the plots together
plots[[1]] + plots[[2]] + plots[[3]] + plot_layout(nrow = 3)

enter image description here

And finetune to taste.

teunbrand
  • 33,645
  • 4
  • 37
  • 63