Search code examples
rggplot2levels

ggplot2: Releveling factors for sequentially added layers


I am writing a plotting method for class "foo". I would like this plot function to take multiple foo objects and plot them on the same graph.

The Code

#parabola function
parabolas <- function(x, parm) {
  y <- parm[1]*(x^2)+parm[2]*x+parm[3]
  return(y)
}
#make foo object
make_foo <- function(a, b, c) {
   x <- runif(100, 0 , 20)
   y <- parabolas(x = x, parm = c(a,b,c)) + rnorm(100, mean = 100 ,sd = 100)
   foo <- list(data = data.frame(x = x, y = y), parameters = c(a,b,c))
   class(foo) <- "foo"
   return(foo)
}


#plot function
plot.foo <- function(x, 
                 ...,
                 labels) {

 a <- ggplot(NULL, aes(x = x, y = y))

 foo.list <- list(x, ...)
 #browser()
 #build plot
 for(i in 1:length(foo.list)){
   foo.obj <- foo.list[[i]]
   foo.obj$data$lab <- factor(rep(labels[i], nrow(foo.obj$data)), levels = labels)
a <- a + geom_point(data = foo.obj$data, size = 5, alpha = .7, aes(color = lab))
a <- a + stat_function(data = foo.obj$data,
                       fun = parabolas, 
                       args = list(parm = foo.obj$parameters), size = 1.2)
 }
 return(a)
}

The Problem

ggplot will relevel the factor levels of lab according to the alphabetical order of the factor labels. I do not know how to choose the factor level order for lab when adding these layers sequentially. I would like for the first element of labels to correspond to the first foo object plotted, and the second element to correspond to the second foo object, and so forth and so forth.

foo1 <- make_foo(2, 10, 3)
foo2 <- make_foo(-6, -3, 2000)
plot(foo1, foo2, labels = c("obj1","obj2"))
#label for foo1 is "obj1" and label for foo2 is "obj2"
plot(foo1, foo2, labels = c("obj3","obj2"))
#label for foo1 should be "obj3" and label for foo2 should be "obj2"

The motivation

The reason I structure the plot function like this as opposed to binding the data frames together and assigning the correct factor levels to lab is because in that particular case, facet_wrap and stat_function do not work well together. After applying multiple stat_function and using facet_wrap together, all curves will appear in each panel. This thread illustrates a similar problem. Because I have these different layers limited to different data sets, facet_wrap will correctly facet each stat_function plot according to the data/parameters used to draw it.

plot(foo1, foo2, labels = c("z","a")) + facet_wrap(~lab, scales = "free")
#Shows facet_wrap works as intended but the labels for foo1 and foo2 are
#still not in the intended order

Solution

  • You can manually override the order of the color scale by setting the limits. Here is how:

    plot.foo <- function(x, 
                         ...,
                         labels) {
    
      a <- ggplot(NULL, aes(x = x, y = y))
    
      foo.list <- list(x, ...)
      #browser()
      #build plot
      for(i in 1:length(foo.list)){
        foo.obj <- foo.list[[i]]
        foo.obj$data$lab <- factor(rep(labels[i], nrow(foo.obj$data)), levels = labels)
        a <- a + geom_point(data = foo.obj$data, size = 5, alpha = .7, aes(color = lab))
        a <- a + stat_function(data = foo.obj$data,
                               fun = parabolas, 
                               args = list(parm = foo.obj$parameters), size = 1.2)
      }
    
      ### added line:
      a <- a + scale_color_discrete(limits = labels)
      ###
    
      return(a)
    }
    

    enter image description here

    enter image description here