Michael Bonert
2022-Feb-28 01:02 UTC
[R] ggplot2 with an arbitrary number of curves from geom_function + legend?
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]]
Avi Gross
2022-Feb-28 06:00 UTC
[R] ggplot2 with an arbitrary number of curves from geom_function + legend?
I would reply but since ggplot was mentioned, I must abstain. ;-) I will say that you can use ggplot in a loop in some cases if you do this: p <- ggplot(...) + ... Then you can in your loop keep adding to p as in: p <- p + geom_whatever() + ... You do at some point need to make p print itself and generate a graph. Until then, it can be amended, albeit some changes undo or overwrite earlier ones if not done carefully. Having said all that, there is often a way to do a kind of looping within ggplot by say grouping your data and having it draw things grouped, or say making multiple vertical (or horizontal) lines by supplying a vector of positions. But as noted, this is not considered a good place by some to discuss this, so forget I said anything. -----Original Message----- From: Michael Bonert <michael.bonert at alumni.utoronto.ca> To: r-help at r-project.org <r-help at r-project.org> Sent: Sun, Feb 27, 2022 8:02 pm Subject: [R] ggplot2 with an arbitrary number of curves from geom_function + legend? 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]] ______________________________________________ R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see https://stat.ethz.ch/mailman/listinfo/r-help PLEASE do read the posting guide http://www.R-project.org/posting-guide.html and provide commented, minimal, self-contained, reproducible code.