[R] ggplot2 with an arbitrary number of curves from geom_function + legend?

Michael Bonert m|ch@e|@bonert @end|ng |rom @|umn|@utoronto@c@
Mon Feb 28 02:02:17 CET 2022


Dear R-help,

I am trying to create a ggplot with an arbitrary number of curves + legend.

The plot should contain:
1. data points + a line that is either user defined *or* represents the median of the data points
2. an arbitrary number of confidence intervals (CIs) around the line, where the CIs are defined by a function

#1: this is easy enough
#2: I don't know how to combine this with #1

I can write #2 as a loop (see below) but it is then seems to be impossible to get ggplot to do a proper legend.

The challenge I have seems similar to this problem:
https://stackoverflow.com/questions/12169289/ggplot2-fail-to-apply-color-with-scale-fill-manual-inside-a-loop

I read that one should not use a loop with ggplot ( https://stackoverflow.com/questions/62603533/adding-ggplot2-legend-with-many-lines-using-for-loop ).
(Interesting: there are probably a half dozen posts on ggplot + loops floating around on the web.  I do not feel alone in my problem... but I also do not think I am close to solving it.)
(As a user of GNU/Octave ( octave.org ) using a loop seems the intuitive solution. In GNU/Octave one does a "hold on" and then plots however many lines/objects one wants within a figure.)

Is there a way to freeze the 'scale_colour_manual' attribute?
(The way the code is written: the colour scale seems to be overwritten with the 'fp=fp ...' statement)
(I have gotten the message: "Scale for 'colour' is already present. Adding another scale for 'colour',
which will replace the existing scale.")

Loop aside:
Is there a way to elegantly pipe an arbitrary number of 'geom_function' calls to the 'ggplot'?
(I wondered whether the '%>%' operator in 'dplyr' is a possible solution -- as suggested here:
https://stackoverflow.com/questions/30655364/ggplot2-getting-a-color-legend-to-appear-using-stat-function-in-a-for-loop )


I could write code to generate a data frame that has the data points and points for arbitrary curves; however,
this would necessitate writing a function that replicates 'geom_function'.

Is there a way to generate the text string and then execute it?
( https://stackoverflow.com/questions/1743698/evaluate-expression-given-as-a-string )
(In my humble opinion: this would be a hack.)

Is there a way to generate a legend with 'override.aes'?
( https://www.r-bloggers.com/2020/07/controlling-legend-appearance-in-ggplot2-with-override-aes/ )


Below is the attempt at creating a legend:
~~
  # plot the data points
  fp = ggplot(df,aes(x=x_var, y=y_var), colour4labels[num_of_funnels+2]) + geom_point(size=4, na.rm = TRUE, alpha = 0.75) + theme(legend.position="bottom") + scale_color_manual(labels = legend_labels, values = colour4labels) + guides(color=guide_legend("Control Limits")) + geom_hline(aes(yintercept = funnel_centre_line, colour = colour4labels[num_of_funnels+1]), linetype = 2) + xlab(x_label) + ylab(y_label) + ggtitle(plot_title) + theme(axis.title=element_text(face="bold"), plot.title = element_text(size = 14, face = "bold", hjust = 0.5))

  # add funnel plot curves (limits)
  for (ci_ctr in 1:length(limits)) {
    if (ci_ctr==1) {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), size = 1.1, na.rm = TRUE, linetype = 5) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), size = 1.1, na.rm = TRUE, linetype = 5) + theme(legend.position="bottom") + scale_color_manual(labels = legend_labels, values = colour4labels) + guides(color=guide_legend("Control Limits"))

    } else if (ci_ctr==2) {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), size = 1.3, na.rm = TRUE) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), size = 1.3, na.rm = TRUE) + theme(legend.position="bottom") + scale_color_manual(labels = legend_labels, values = colour4labels) + guides(color=guide_legend("Control Limits"))

    } else {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), na.rm = TRUE, linetype = 5) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), aes(colour = colour4labels[ci_ctr]), na.rm = TRUE, linetype = 5) + theme(legend.position="bottom") + scale_color_manual(labels = legend_labels, values = colour4labels) + guides(color=guide_legend("Control Limits"))
    }
  }
~~

Code without elements to generate a legend (that otherwise does what I want it to):
~~
  # plot the data points
  fp = ggplot(df,aes(x=x_var, y=y_var)) + geom_point(size=4, na.rm = TRUE, colour = "blue", alpha = 0.75) + theme_bw()

  # add centre line, axis labels and plot title
  fp = fp + geom_hline(aes(yintercept = funnel_centre_line), linetype = 2, colour = "black") + xlab(x_label) + ylab(y_label) + ggtitle(plot_title) + theme(axis.title=element_text(face="bold"), plot.title = element_text(size = 14, face = "bold", hjust = 0.5))

  # add funnel plot curves (limits)
  for (ci_ctr in 1:length(limits)) {
    if (ci_ctr==1) {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), colour = colour4labels[ci_ctr], size = 1.1, na.rm = TRUE, linetype = 5) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), colour = colour4labels[ci_ctr], size = 1.1, na.rm = TRUE, linetype = 5)

    } else if (ci_ctr==2) {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), colour = colour4labels[ci_ctr], size = 1.3, na.rm = TRUE) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), colour = colour4labels[ci_ctr], size = 1.3, na.rm = TRUE)

    } else {
      fp = fp + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 1, trunc_val = 0), colour = colour4labels[ci_ctr], na.rm = TRUE, linetype = 5) + geom_function(fun = calc_fpc, args = list(ci_or_alpha = limits[ci_ctr], probability = funnel_centre_line, upper_or_lower = 2, trunc_val = 0), colour = colour4labels[ci_ctr], na.rm = TRUE, linetype = 5)
    }
  }
~~

Happy to post the complete code. I was thinking it might be a contribution--if deemed non-trivial.
(I also have a test function that runs the code with a data set already in r-cran.)

Take Care,
Michael Bonert, BASc, MASc, MD, FRCPC

PS -
Environment details:
 R version: 4.0.4 (2021-02-15)
 Platform: Debian GNU/Linux 11 (stable)

I am trying to generate R code similar to that in this paper: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC8219089/
(If you want to know where I am coming from: it is in that paper.)

The code I wrote is somewhat similar to the package 'FunnelPlotR' ( https://cran.rstudio.com/web/packages/FunnelPlotR/index.html ). I did look at the code in that package.

	[[alternative HTML version deleted]]



More information about the R-help mailing list