1

I am trying to add a legend to each plot output of the ccl4model(ODE model) in the deSolve package using plot.deSolve and the legend method but it only appears on the last plot output of the model. I would like for each legend to appear on each plot output of the model

library(deSolve)
head(ccl4data) #observed data

obs <- subset(ccl4data, animal == "A", c(time, ChamberConc))
names(obs) <- c("time", "CP")

parms <- c(0.182, 4.0, 4.0, 0.08, 0.04, 0.74, 0.05, 0.15, 0.32, 16.17,
            281.48, 13.3, 16.17, 5.487, 153.8, 0.04321671,
            0.40272550, 951.46, 0.02, 1.0, 3.80000000)
#Scenario 1
yini <- c(AI = 21, AAM = 0, AT = 0, AF = 0, AL = 0, CLT = 0, AM = 0)
out <- ccl4model(times = seq(0, 6, by = 0.05), y = yini, parms = parms)

#Scenario 2
par2 <- parms; par2[1] <- 0.1
out2 <- ccl4model(times = seq(0, 6, by = 0.05), y = yini, parms = par2)


#Scenario 3
par3 <- parms;par3[1] <- 0.05
out3 <- ccl4model(times = seq(0, 6, by = 0.05), y = yini, parms = par3)

#Plotting all the scenarios
plot.deSolve(out, out2, out3, which = c("AI","MASS", "CP"),
     col = c("black", "red", "green"), lwd = 2,
     obs = obs, obspar = list(pch = 18, col = "blue", cex = 1.2))

legend("topright", lty = c(1,2,3,NA), pch = c(NA, NA, NA, 18),
         col = c("black", "red", "green", "blue"), lwd = 2,
          legend = c("par1", "par2", "par3", "obs"))
2
  • Can you make your code reproducible? We don't have any of your input values. Commented Mar 26 at 6:06
  • I have modified the question into a reproducible one. Commented Mar 26 at 9:33

2 Answers 2

1

I'm pretty sure you can't do that since, although the function does include a legend argument, it can only be TRUE or FALSE. The only option I can think of is to add the legend on a 4th plot, which works here because you have 3 plots in a 4x4 grid, and the legend applies to all plots.

plot(out, out2, out3, which = c("AI","MASS", "CP"),
     col = c("black", "red", "green"), lwd = 2,
     obs = obs, 
     obspar = list(pch = 18, col = "blue", cex = 1.2))

plot(1, ann=FALSE, pch="", axes=FALSE)
legend("top", lty = c(1,2,3,NA), pch = c(NA, NA, NA, 18),
       col = c("black", "red", "green", "blue"), lwd = 2,
       legend = c("par1", "par2", "par3", "obs"), bty="n")

enter image description here

Perhaps a better option is to layout the plots horizontally (or vertically) and put the legend to the right using layout and mfrow=NULL in the plot call:

layout(matrix(1:4, nc=4), widths=c(4,4,4,1))

plot(out, out2, out3, which = c("AI","MASS", "CP"),
     col = c("black", "red", "green"), lwd = 2,
     obs = obs, las=1, mfrow=NULL, ## <--- HERE
     obspar = list(pch = 18, col = "blue", cex = 1.2))

opar <- par(mar=rep(0,4))
plot.new()
legend("left", lty=c(1,2,3,NA), pch=c(NA, NA, NA, 18),
       col = c("black", "red", "green", "blue"), lwd = 2,
       legend = c("par1", "par2", "par3", "obs"),
       bty="n", title="Legend")
par(opar)

enter image description here

If you want a legend in each plot, then mapply can help:

cols <- c("black", "red", "green", "blue")
layout(matrix(1:3, nc=3))

mapply(\(which, x) {
        plot(out, out2, out3, which = which, col = cols, lwd = 2,
             obs = obs, las=1, mfrow=NULL, 
             obspar = list(pch = 18, col = "blue", cex = 1.2))
        if(which=="CP")
           legend(x, lty=c(1:3, NA), pch=c(NA,NA,NA,18), col=cols, lwd=2,
                  legend=c("par1","par2","par3","obs"), bty="n")
        else
           legend(x, lty=1:3, col=cols, lwd = 2,
                     legend = c("par1","par2","par3"), bty="n")}, 
       which=c("AI","MASS", "CP"), x=c("topright", "topleft", "topright"))

enter image description here

Sign up to request clarification or add additional context in comments.

3 Comments

In the deSolve package, a legend argument is only implemented for the image and matplot.0D methods, but not for plot.deSolve, so one cannot automatically add legends to each subplot. However, manually adding a legend to the last plot is as intended. I also admit that I don't understand why the OP wants a (redundant) legend for each subplot, but custom plotting is of course possible with R's general plotting functions.
Thank you very much, the reason I want to add a legend for each plot is because I want to incorporate RMSE between the model and observations in each grid.
Thank you very much @Edward and tpetzoldt for your help, I was able to implement this in the source file of the package, it worked but still needs some improvement on my end for my use case. Thank you so much.
0

In the deSolve package, a legend argument is only implemented for the image and matplot.0D methods, but not for plot.deSolve, so one cannot automatically add legends to each subplot. However, manually adding a legend to the last plot is as intended.

Here is a semi-automatic way using individual calls for each state variable and a custom legend function mylegend. The first call to plot defines the numbers of rows and columns with mfrow while resetting is suppressed with mfrow = NULL in the subsequent plots.

#Plotting all the scenarios

mylegend <- function(pos="topright", obs=FALSE) {
  if (obs) {
  legend(x=pos, lty = c(1,2,3,NA), pch = c(NA, NA, NA, 18),
         col = c("black", "red", "green", "blue"), lwd = 2,
         legend = c("par1", "par2", "par3", "obs"))
  } else {
    legend(x=pos, lty = c(1,2,3), pch = c(NA, NA, NA),
           col = c("black", "red", "green"), lwd = 2,
           legend = c("par1", "par2", "par3"))
  }
}

obspar <- list(pch = 18, col = "blue", cex = 1.2)

plot(out, out2, out3, which = c("AI"),
     col = c("black", "red", "green"), lwd = 2, mfrow = c(1, 3),
     obs = obs, obspar = obspar)
mylegend()


plot(out, out2, out3, which = c("MASS"),
     col = c("black", "red", "green"), lwd = 2, mfrow = NULL,
     obs = obs, obspar = obspar)
mylegend(pos = "bottomright")


plot(out, out2, out3, which = c("CP"),
     col = c("black", "red", "green"), lwd = 2, mfrow = NULL,
     obs = obs, obspar = obspar)
mylegend(obs = TRUE)

plot of 3 state variables with legend on each subplot

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.