using ggplots stat_function in a custom function - r

I am trying to create some ggplots automaticly. Here is my working code example for adding stat_functions:
require(ggplot2)
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()+
stat_function(fun= function(x){1*x},lwd=1.25, colour = "navyblue") +
stat_function(fun= function(x){2*x},lwd=1.25, colour = "navyblue") +
stat_function(fun= function(x){3*-x},lwd=1.25, colour = "red")
p1
As you can see the stat_functions all use (nearly) the same function just with a different parameter.
Here is what i have tried to write:
f <- function(plot,list){
for (i in 1:length(list)){
plot <- plot + stat_function(fun= function(x){x*list[i]})
}
return(plot)
}
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()
p2 <- f(p1,c(1,2,3))
p2
This however doesnt return 3 lines, but only one. Why?

Your question is a bit confusing, because the first plot actually contains some other variable bits, but in your function you have a single stat_summary call for only one variable element.
Anyways. Keep the ggplot main object separate and create a list of additional objects, very easy for example with lapply. Add this list to your main plot as usual.
Check also https://ggplot2-book.org/programming.html
library(ggplot2)
p <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x)) + theme_minimal()
ls_sumfun <- lapply(1:3, function(y){
stat_function(fun= function(x){y*x}, lwd=1.25, colour = "navyblue")
}
)
p + ls_sumfun
Created on 2021-04-26 by the reprex package (v2.0.0)

In R, you can pass functions as arguments. You can also return functions from functions. This might make your code simpler and cleaner.
Here's an example:
p1 <- ggplot(data.frame(x = c(-2.5, 7.5)), aes(x = x))
add_stat_fun <- function (ggp, f) {
ggp + stat_function(fun = f)
}
make_multiply_fun <- function (multiplier) {
force(multiplier) # not sure if this is required...
f <- function (x) {multiplier * x}
return(f)
}
my_funs <- lapply(1:3, make_multiply_fun)
# my_funs is now a list of functions
add_stat_fun(p1, my_funs[[1]])
add_stat_fun(p1, my_funs[[2]])
add_stat_fun(p1, my_funs[[3]])

Related

How to combine two function plots in R?

I can't seem to find a way to combine two ggplots having different function ranges.
library(ggplot2)
myfun <- function(x) {
1/(1 + exp(-x))}
ggplot( NULL,aes(x)) +
stat_function(data=data.frame(x=c(0, 20)),fun=myfun, geom="line") +
stat_function(data=data.frame(x=c(10, 20)),fun=1/myfun, geom="line")
EDIT: Had a mistake in the question: 1/myfunc instead of myfunc in the second function data.
I am not sure if this is what you want, but I give your function two different colors based on two ranges. You can use the following code:
library(ggplot2)
myfun <- function(x) {
1/(1 + exp(-x))}
ggplot(NULL) +
stat_function(data= data.frame(x = c(0, 10)), aes(x, color = "blue"), fun=myfun, xlim = c(0,10)) +
stat_function(data= data.frame(x = c(10, 20)), aes(x, color = "red"), fun=myfun, xlim = c(10,20)) +
scale_color_manual(labels = c("blue", "red"), values = c("blue", "red"))
Output:
As you can see in the plot, the function is plotted within two different ranges.
Answer to edited question
I would suggest to just make a second function like this:
library(ggplot2)
myfun1 <- function(x) {
1/(1 + exp(-x))}
myfun2 <- function(x) {
1/(1/(1 + exp(-x)))}
ggplot( NULL) +
stat_function(data=data.frame(x=c(0, 20)),fun=myfun1, geom="line") +
stat_function(data=data.frame(x=c(10, 20)),fun=myfun2, geom="line")
Output:

R: ggplot - Different Title for each object in a plot list

currently I am trying to making a plot list with ggplot from a list of data frames (94 time series). Then I want to export the plots to a PDF. This, so far, was successful using following code:
plot.list = lapply(HR_clean, function(x) {
y = length(x)
z = data.frame("HR" = x, "Time" = rep(1:y, 1))
ggplot(z, aes(x = Time, y = HR)) +
theme_bw() +
geom_line(linetype = "solid") +
ggtitle("Plot Title")
})
ggsave(
filename = "plots2.pdf",
plot = marrangeGrob(plot.list, nrow=1, ncol=1),
width = 15, height = 9
)
However, I also want that the main title of each plot is equal name of the corresponding list object. Perhaps anyone knows a smart solution for this problem.
Best,
Johnson
I couldn't test this (since you are not providing example data) but this should work using purrr's imap():
plot.list <- purrr::imap(HR_clean, function(x, name) {
y <- length(x)
z <- data.frame("HR" = x, "Time" = rep(1:y, 1))
ggplot(z, aes(x = Time, y = HR)) +
theme_bw() +
geom_line(linetype = "solid") +
ggtitle(name)
})

Creating boxplots for visualisation in R using a function

I wrote a code to get boxplots for visualisation in R. The code is running but I am not getting any boxplots. Can you please help identify where I went wrong.
create_boxplots <- function(x, y){
ggplot(data = forest_fires) +
aes_string(x = x, y = y) +
geom_boxplot() +
theme(panel.background = element_rect(fill = "white"))
}
x_var_month <- names(forest_fires)[3]
y_var <- names(forest_fires)[5:12]
month_box <- map2(x_var_month, y_var, create_boxplots)
The code is ok, just that when you call ggplot inside a function, you return the object and it is stored in a list. You need to print it. For example:
library(ggplot2)
library(purrr)
library(gridExtra)
create_boxplots <- function(x, y){
ggplot(data = forest_fires) +
aes_string(x = x, y = y) +
geom_boxplot() +
theme(panel.background = element_rect(fill = "white"))
}
forest_fires = data.frame(matrix(runif(1300),ncol=13))
forest_fires[,3] = factor(sample(1:12,nrow(forest_fires),replace=TRUE))
x_var_month <- names(forest_fires)[3]
y_var <- names(forest_fires)[5:12]
month_box <- map2(x_var_month, y_var, create_boxplots)
This shows you the plot for y_var[1]
month_box[[1]]
#or print(month_box[[1]])
To get everything in 1 plot do:
grid.arrange(grobs=month_box)

R: How to pass parameters to ggplot geom_ within a function?

I'm trying to create a utility function that combines several geom_, like in this example (which doesn't work):
my_geom_y <- function(yy, colour){
geom_line(aes(y=yy), col=colour) + geom_point(aes(y=yy), col=colour)
}
so that then I can do this:
myX <- 0:90
ggplot(mapping = aes(x=myX)) + my_geom_y(dlnorm(myX), "red") + my_geom_y(dexp(myX), "blue")
Is that possible?
I tried using get(), eval(), substitute(), as.name() with no avail.
Looking at related posts: passing parameters to ggplot, Use of ggplot() within another function in R didn't help.
I like MSM's approach, but if you want to be able to add my_geom_y to a ggplot you've already made, this is an alternative that might suit what you're after:
library(ggplot2)
x <- 1:100
my_geom_y <- function(yy, colour = "black"){
list(
geom_line(mapping = aes(y= yy),
col = colour),
data = data.frame(x, yy)),
geom_point(mapping = aes(y = yy),
col = colour,
data = data.frame(x, yy))
)
}
ggplot(mapping = aes(x)) +
my_geom_y(x, "red") +
my_geom_y(dlnorm(x), "blue") +
my_geom_y((x^1.1), "black") +
my_geom_y(x/2, "yellow")
I don't have enough reputations to comment so here is a suggestion:
my_geom_y <- function(xx, yy, colour){
ggplot() +
geom_line(aes(x=xx, y=yy), col=colour) +
geom_point(aes(x=xx, y=yy), col=colour)
}
This will create one plot. To create multiple ones, you need to pass your inputs to the function as a list and loop through it inside the function for each geom (since we can't add two or more ggplot objects) - if that makes sense.
Based on #luke-c idea, this makes the function standalone, cut-n-paste ready. We can also add now labels to each curve.
my_geom_y <- function(.xx, .yy, yLabel = 1, .colour=NA ){
if (is.na(.colour))
.colour <- palette()[yLabel%%length(palette())]
list( geom_line(mapping=aes(.xx,.yy), col=.colour, data=data.frame(.xx, .yy)),
geom_point(mapping=aes(.xx,.yy), col=.colour, data=data.frame(.xx, .yy)),
annotate(geom="text" , col = .colour, label=deparse(substitute(.yy)),
x=mean(.xx),y=max(.yy)-(max(.yy)-min(.yy))/20*yLabel)
)
}
myX <- 1:10
ggplot() + my_geom_y(myX, dlnorm(myX), 1) +
my_geom_y(myX, dexp(myX), 2) + my_geom_y(myX, dexp(myX,0.7), 3)
This function becomes handy when you need to visually compare multiple distributions.

Pass variable columns with optional facets in a function via ggplot in R

I am trying to figure out how to create multiple ggplots with one function, to avoid tedious copying and pasting. I do not want to have to change argument names in the function when I want to use different columns in the same data.frame. There may be completely different approach to this problem, but I am including two attempts that almost worked but still fall short of what I want.
Thanks!
Edit
I would also like the function to add a facet depending on on argument, such as groupBy="brand", for example. I think aes_string along side of https://stat.ethz.ch/pipermail/r-help/2009-October/213946.html may get me there. I included my facet request as part of my question, because aes_string alone falls short of my goal of being able to facet as part of the plot function. I added brand to the dataset, just to share what I could not find by searching online today.
library(ggplot2)
### sample data ###
n=25
dataTest = data.frame(
xVar=sample(1:3, n, replace=TRUE),
yVar = rnorm(n, 5, 2),
zVar=rnorm(n, 5, .5),
brand=letters[1:5])
### a first attempt ###
### works, but forces me to create a new function whenever column names need to change
my_plot =function(data) {ggplot(data=data, aes(x=xVar, y=yVar))+geom_bar(stat="identity")}
do.call("my_plot", list(data=dataTest))
### wish for something like this... but this does not work
my_plot = function(data) {ggplot(data=data, aes(x=x, y=y))+geom_bar(stat="identity")}
do.call("my_plot", list(data=dataTest, x=xVar, y=yVar))
do.call("my_plot", list(data=dataTest, x=xVar, y=zVar))
### a second attempt, does not work ###
my.plot = function(x, y, data)
{
arguments <- as.list(match.call())
data = eval(arguments$data, envir=data)
x = eval(arguments$x, envir=data)
y = eval(arguments$y, envir=data)
p=ggplot(data=data, aes(x, y))+geom_bar(stat="identity")
return(p)
}
my.plot(x=xVar, y=yVar, data=dataTest)
Using aes_string will allow you to pass character strings into your ggplot2 function, allowing you to programmatically change it more easily:
my.plot = function(x, y, data)
{
p=ggplot(data, aes_string(x=x, y=y))+geom_bar(stat="identity")
print(p)
}
my.plot(x="xVar", y="yVar", data=dataTest)
my.plot(x="xVar", y="zVar", data=dataTest)
What about using %+% to update the plots instead?
Example:
library(ggplot2)
### sample data ###
n=25
dataTest = data.frame(
xVar=sample(1:3, n, replace=TRUE),
yVar = rnorm(n, 5, 2),
zVar=rnorm(n, 5, .5) )
p1 <- ggplot(data = dataTest, aes(x = xVar, y = yVar)) +
geom_bar(stat = "identity")
aes2 <- aes(x = xVar, y = zVar)
p2 <- p1 %+% aes2
p1:
p2:
EDIT
or as #sebastian-c mentioned, aes_string
plots <- function(x, y, data = dataTest) {
p1 <- ggplot(data = data, aes_string(x = x, y = y)) +
geom_bar(stat = "identity")
p1
}
plots('xVar','yVar')
plots('xVar','zVar')
EDIT 2: beat me to the punch :o
Using #sebastian-c's answer and other sources, I have a function that I think will work and I wanted to share it. I think I see #Henrik's solution, but it seems like more typing, as I have 4 groups, 4 'x' categories, and a third category related to time (year, quarters, months).
library(ggplot2)
### sample data ###
n=25
dataTest = data.frame(
xVar=sample(1:3, n, replace=TRUE),
yVar = rnorm(n, 5, 2),
zVar=rnorm(n, 5, .5),
brand=letters[1:5])
### function
my.plot = function(x, y, data, group=NULL)
{
p=ggplot(data, aes_string(x=x, y=y, fill=group))+
geom_bar(stat="identity")
# make a facet if group is not null
if(length(group)>0) {
facets = facet_wrap(formula(paste("~", group)))
p = p + facets
}
return(p)
}

Resources