0

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
Justin Landis
  • 1,981
  • 7
  • 9

1 Answers1

0

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

Axeman
  • 32,068
  • 8
  • 81
  • 94
  • Is it better to use `limits` or `breaks` in this situation? – the-mad-statter Jun 24 '19 at 17:27
  • You could use either. Not sure if there is any reason to have preference either way. – Axeman Jun 24 '19 at 17:29
  • I was thinking `breaks` because OP did not intend to exclude any levels. But both give the same result in this situation. – the-mad-statter Jun 24 '19 at 17:31
  • That's true, if you only provide one label only that one will be shown (the other will just be grey). You could potentially use this if you want to highlight one curve out of several. Limits also allow you to add extra levels to the scale that are not in your data, e.g. `plot(foo1, foo2, labels = c("obj3","obj2",'this is another level))`. I guess that could be useful in some cases. – Axeman Jun 24 '19 at 17:34