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.